Skip to content

Self Hosting

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

Everything runs from compose.prod.yaml. Full guide: docs/deploy.md.

Steps

  1. Point a DNS A record at the server.

  2. Clone the repository and create .env from .env.example with strong MYSQL_* values. Set SITE_URL=https://<your-domain>. If ports 3000 or 5000 are taken, set WEB_PORT and API_PORT.

  3. Start the stack:

    docker compose -f compose.prod.yaml up -d --build

    The API container migrates, loads the sourced seed rows and starts gunicorn on every start (both steps are safe to repeat). Both containers listen on 127.0.0.1 only.

  4. Put a reverse proxy in front. With Caddy (which also gets the TLS certificate):

    your-domain {
        encode zstd gzip
        reverse_proxy 127.0.0.1:3000
    }

    The web app proxies /api/v1/* to the API, so one upstream is enough. The proxy must set X-Forwarded-For (Caddy does by default; with nginx add proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;), because the rate limit keys on it.

  5. Check https://<your-domain>/api/v1/health.

Updates

git pull
docker compose -f compose.prod.yaml up -d --build

Backups

Only the settlement cycles and official closures live in MySQL, and both can be rebuilt with flask --app wsgi seed. Back up the mysql-data volume anyway.

Clone this wiki locally