Docker Compose
The docker-compose.yml at the pod-portals root orchestrates all services for local development.
Services
| Service | Image / Build | Port | Description |
|---|---|---|---|
django | ./django-api | 8000 | Django REST API |
db | mysql:8.0.35 | 8100 | MySQL database |
redis | redis:7.2.4 | — | Cache + Celery broker |
celeryworker | ./django-api | — | Celery task worker |
celerybeat | ./django-api | — | Periodic task scheduler |
portals | node:20-alpine | 3000 | React SPA (Vite dev) |
docs | node:20-alpine | 4173 | VitePress docs |
Common Commands
# First-time setup — copies .env files and builds images
make build
# Start everything
make up
# Stop everything
make down
# Rebuild after dependency changes
docker compose up --build
# Run Django management commands
docker compose run --rm django python manage.py migrate
docker compose run --rm django python manage.py shell
# Run backend tests
docker compose run --rm django pytest
# Tail logs for a specific service
docker compose logs -f celeryworker
docker compose logs -f portalsVolumes
| Volume | Mounted at | Purpose |
|---|---|---|
db_data | /var/lib/mysql | Persist MySQL data across restarts |
django_venv | /app/.venv | Persist Poetry virtualenv across rebuilds |
portals_node_modules | /app/node_modules | Isolate portals deps from host bind mount |
docs_node_modules | /app/node_modules | Isolate docs deps from host bind mount |
Environment Files
Each service reads its own .env file. make build copies the .example files automatically on first run. See Environment Variables for details.
Known Gotchas
createsuperuser error on restart
django/compose/local/start.sh runs python manage.py createsuperuser --noinput on every container start. On the first run this creates the admin user fine. On any subsequent restart, Django prints:
CommandError: That email address is already taken.This is a non-fatal error — the script continues and the server starts normally. You can safely ignore it in the logs. It is pre-existing behaviour in the original start script.
drf-spectacular schema errors on startup
During collectstatic, drf-spectacular introspects every view to build the OpenAPI schema. Some views require an authenticated, org-scoped request to resolve their serializer — running without one causes errors like:
Error [FormViewSet]: exception raised while getting serializer.
(Exception: 'AnonymousUser' object has no attribute 'is_admin')
Error [ContactFormView]: exception raised while getting serializer.
(Exception: 'Request' object has no attribute 'organization')
Error [PingView]: unable to guess serializer.These are non-fatal — drf-spectacular skips the affected views and the server starts normally. The OpenAPI schema will be incomplete for those endpoints until the underlying views are made schema-safe. See To Review for the open investigation item.
Node service cold start is slow
portals and docs run pnpm install every time the container starts from scratch (i.e. after make clean or a first run). Subsequent restarts skip the install because node_modules is persisted in a named volume. If you see the SPA or docs take a minute to come up on first boot, this is why.