Skip to content

Deploy with Docker

The forum ships as a Docker image built on python:3.13-slim, running as a non-root user. This page covers running it in production.

Provide the contract settings and point the web server at a TCP port so your proxy can reach it:

Terminal window
docker run -d --name bluset \
-e DATABASE_URL='postgresql://user:pass@db:5432/bluset' \
-e SECRET_KEY='<a long random string>' \
-e GUNICORN_BIND='0.0.0.0:8000' \
-e BLUSET_REDIS_URL='redis://redis:6379' \
-e BLUSET_REDIS_ENABLED='True' \
-e BLUSET_MAIL_SERVER='smtp.example.com' \
-e BLUSET_MAIL_PORT='587' -e BLUSET_MAIL_USE_TLS='True' \
-e BLUSET_MAIL_USERNAME='...' -e BLUSET_MAIL_PASSWORD='...' \
-e BLUSET_MAIL_DEFAULT_SENDER='noreply@example.com' \
-p 127.0.0.1:8000:8000 \
<your-image-reference>

Run Postgres and Redis either as their own containers on the same network (a Compose file is the usual way) or as managed services you point the URLs at.

The image runs its install/migrate/seed steps idempotently on every start, but it will not create an administrator with default credentials. Create the first admin explicitly:

Terminal window
docker exec -it bluset bluset create-admin \
-u admin -e admin@example.com -p '<a strong password>'

See the CLI reference for install, seed-features, and more.

The container exposes a health blueprint:

  • GET /up/ — liveness.
  • GET /up/databases — deep check (Redis + database).
  • GET /up/ready — readiness alias.

Point your orchestrator’s health check at /up/ (the image’s default) and your load balancer’s deep check at /up/databases.

By default (BLUSET_AUTO_MIGRATE=True) the container applies migrations on start, so a new image version migrates itself when it boots. If you prefer to control exactly when schema changes land, set BLUSET_AUTO_MIGRATE=False and run them yourself before starting the new version:

Terminal window
docker exec -it bluset bluset db upgrade

Running migrations twice is safe (they’re idempotent) — pick one approach and be consistent.

The container serves plain HTTP; you terminate TLS. Any reverse proxy works (nginx, Caddy, Traefik, a cloud load balancer). The essentials:

  • Terminate HTTPS at the proxy and forward to the container’s 0.0.0.0:8000.
  • Forward the Host header and the standard X-Forwarded-* headers so the app builds correct absolute URLs and treats the connection as secure.
  • Serve on your own domain; production mode already marks cookies Secure, so the site must be reachable over HTTPS.

You own backups. At minimum, back up the PostgreSQL database and any uploaded files on a schedule, store them off the host, and rehearse a restore before you need one. Test that a restored copy boots and serves /up/databases green.

  1. Pull the new image tag.
  2. Run bluset db upgrade (or rely on BLUSET_AUTO_MIGRATE).
  3. Recreate the container on the new tag.
  4. Verify /up/databases and log in.

Keep the previous image tag handy so you can roll back by recreating the container on it if an upgrade misbehaves.