Skip to content

Self Hosting

Syed Ahmer Shah edited this page Oct 25, 2026 · 1 revision

One production image runs everything. Full guide, including Azure Container Apps and the deployment pipeline: docs/deployment.md.

The image

Every push to main builds ghcr.io/ahmershahdev/holidayops:<commit sha> and :latest. The same image runs four roles with different commands:

Role Command Replicas
Web (API, public site, web app, static files) default (gunicorn on port 8000) 1 or more
Worker celery -A config worker --loglevel=info 1 or more
Beat (scheduler) celery -A config beat --loglevel=info --schedule=/tmp/celerybeat-schedule exactly 1
Migrations python manage.py migrate --noinput once per release, before the rollout

You also need MySQL 8 (utf8mb4) and Redis 7.

Try it locally first

docker compose -f docker-compose.prod.yml up --build     # http://localhost:8080

Environment

On top of the Configuration variables:

Variable Value
DJANGO_ALLOWED_HOSTS your domain
DJANGO_CSRF_TRUSTED_ORIGINS https:// + your domain
APP_URL https:// + your domain
MYSQL_SSL_MODE REQUIRED for managed databases
NUM_PROXIES how many proxies sit in front of Django (1 behind one load balancer)
LOG_LEVEL optional, default INFO; security events log on holidayops.security

Never set DJANGO_HTTPS=false in production; it is only for the local production image.

Health check

GET /healthz returns 200 {"status": "ok", ...} when MySQL and Redis answer, otherwise 503. It works over plain HTTP by IP, so load-balancer probes can use it.

After the first deploy

  • Sign up through the site for the first account (the demo seed refuses to run in production).
  • python manage.py set_plan <org> pro moves an organization to Pro.
  • Submit /sitemap.xml to Google Search Console.
  • Back up MySQL and keep FIELD_ENCRYPTION_KEYS somewhere outside the server.

Before a release

Run the attack simulation against the production image:

docker compose -f docker-compose.prod.yml -f docker/simulation.yml up --build -d
python scripts/attack_simulation.py

Use 127.0.0.1, not localhost, on Windows.

Clone this wiki locally