Repository navigation
Deployment
Chris edited this page Oct 4, 2026
·
2 revisions
Production runs on a Contabo server with Docker Compose, behind Cloudflare. Only the owner deploys, and only the owner works on the server. Contributors never need server access: merged work is visible on Staging.
- The owner merges pull requests into
main. Each merge updates staging, not production. - When staging looks right, the owner publishes a GitHub release (for example
v2.2.0). That starts the Deploy App workflow (.github/workflows/deploy.yml). It can also be started by hand from Actions. - The workflow connects over SSH to
/opt/barkandambrosiagalleryand does these steps in order:- It pulls
mainand builds new images. The live site keeps running on the old version meanwhile. - It checks settings and the database connection in a throw-away container (
check --deploy,migrate --plan). - It builds CSS and static files, runs migrations, and loads the published interactions dataset. The last step only adds missing rows.
- It switches the web container and the workers to the new version.
- It runs a health check: the public home page must answer 200 within two minutes. If it doesn't, the previous version is put back and the job fails.
- It pulls
Any failing step stops the deploy before the live site changes.
After a release with new settings or one-off steps, the PR description says what to run. scripts/post_deploy_setup.sh
covers email and the interactions dataset
(docs/production_setup.md).
| Path | |
|---|---|
Code and settings (.env.prod, never committed) |
/opt/barkandambrosiagallery |
| Database files | /opt/barkandambrosia_data/postgres |
Images and other uploads (served at /media/) |
/opt/barkandambrosia_data/media |
| Database dumps for backups | /opt/barkandambrosia_data/db_backup |
| Staging | /opt/barkandambrosia_staging |
Always name both compose files on the server. Otherwise Docker would also load the local-only override (Mailpit):
cd /opt/barkandambrosiagallery
P="docker compose -f docker-compose.yml -f docker-compose.prod.yml"
$P ps # what is running
$P logs -f --tail=200 web # web log (Ctrl+C to leave)
$P logs -f --tail=200 worker-heavy # CSV uploads, updates and downloads
$P restart web worker worker-heavy # after editing .env.prod
$P exec web pixi run python manage.py createsuperuserServices:
-
web: gunicorn, on port 80 behind Cloudflare; -
db: Postgres 15; -
redis; -
worker: quick jobs such as game re-scoring; -
worker-heavy: uploads and downloads, one at a time, at low priority.
They all run against the server:
-
Dropbox Backup (
backup.yaml): dumps the database todb_backup/, then copies the data folder to Dropbox. On the 1st of the month it copies everything; on other days it skips the images. - Access reminders and game scores: run their management commands in the web container.
-
Never run
docker compose down -v, delete the data folders, or runflush,reset_dbormigrate_taxonomy_to_dbagainst production unless that is exactly what you intend.migrate_taxonomy_to_dbreloads the taxonomy, and admins run it by hand. -
Never run
git clean -fdxin/opt/barkandambrosiagallery: it would delete.env.prod. -
Never print or paste secrets. To check that two copies match, compare
md5sumfingerprints.