Skip to content

Add container support - #1386

Draft
norkans7 wants to merge 1 commit into
mainfrom
docker-support
Draft

Add container support#1386
norkans7 wants to merge 1 commit into
mainfrom
docker-support

Conversation

@norkans7

Copy link
Copy Markdown
Contributor

Adds everything needed to run the application as a container — the deployment path the resumable-tasks work (#1374#1384) was building toward.

  • Multi-stage Dockerfile: python dependencies installed with uv from the lockfile; the less compiler runs under bun (a drop-in node runtime) only in the build stage, where static assets are collected and offline-compressed — the runtime stage is a slim python image with just libpq added, running as a non-root user. One image serves every process type: the default command is the gunicorn web process; workers (celery -A ureport worker -Q <queue>) and the scheduler (celery -A ureport beat) run from the same image with a different command.
  • ureport/settings.py.docker: a settings flavor (symlinked into place at image build) that takes all deployment configuration from environment variables — secret key (required, no fallback), DATABASE_URL, broker location, hostnames, email, optional S3-compatible media storage, and a DJANGO_SECURE toggle for TLS-terminating deployments — so a single image serves any install. Static assets bake into the image with offline compression, matching the production settings' approach.
  • docker-compose.yml: a local stack (postgres, valkey, web, worker, beat) for development and demos, with the published port configurable.
  • .dockerignore: keeps developer-local files — notably a personal ureport/settings.py — out of the image.

Verified end to end: image builds, migrations apply in-container, the web process serves with baked compressed assets, the worker consumes all three queues, and the beat service holds the RedBeat lock with a live schedule in redis.

One finding from verification worth separate attention: the worker's embedded beat (-B) fails to spawn its child process entirely on celery 5.5.3 + Python 3.14 (no scheduler starts, no schedule file, no redbeat keys, silently). Container deployments here therefore run beat as its own process — which RedBeat's lock makes safe to replicate — but any non-container deployment relying on -B embedding on this python version should verify its beat is actually running.

A multi-stage Dockerfile builds one image for every process type: python
dependencies via uv, the less compiler run under bun for the static build,
and assets collected and offline-compressed at build time so the runtime
stage needs no node. The image runs as a non-root user and defaults to the
gunicorn web process, with workers and the beat scheduler run from the same
image by overriding the command.

A new settings flavor takes all deployment configuration from environment
variables - secret key, database and broker URLs, hostnames, email, optional
object storage, and security toggles - so one image serves any install.
Beat runs as its own process since the worker's embedded beat fails to
spawn on this celery/python combination; RedBeat's lock makes beat replicas
safe. A compose file runs the full stack locally.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant