Repository navigation
Setup and Deployment
This page documents how the project is run in each environment and how to self-host the production stack.
Unless stated otherwise, commands should be run from the repository root. Commands that need to be run from backend/ or frontend/ explicitly say so.
| Environment | Compose file | What runs | Main use |
|---|---|---|---|
| Dev | infrastructure/docker-compose.dev.yml |
PostgreSQL only | Local development with Django and Vite running directly on your machine |
| Staging | infrastructure/docker-compose.staging.yml |
PostgreSQL, backend, frontend build container, nginx | Full local stack on http://localhost without HTTPS/certbot |
| Production | infrastructure/docker-compose.prod.yml |
PostgreSQL, backend, frontend build container, nginx, certbot | Self-hosted deployment on a public server |
Use the dev setup when you want fast iteration and hot reload.
Create infrastructure/.env by copying infrastructure/.env.example.
For local development with python manage.py runserver, Django loads this file via python-dotenv.
Start PostgreSQL:
docker compose -f infrastructure/docker-compose.dev.yml up -dThis exposes PostgreSQL on localhost:5432.
From backend/:
pip install -r requirements/development.txt
python manage.py makemigrations
python manage.py migrate
python manage.py runserverFrom frontend/:
npm ci
npm run devBy default, Vite runs on http://localhost:5173.
When you are done, stop the database again so it does not keep running and cause conflicts later.
docker compose -f infrastructure/docker-compose.dev.yml downUse the staging stack when you want to run the full application locally behind nginx, close to production, but without HTTPS and certbot.
Create infrastructure/.env by copying infrastructure/.env.example.
For local staging, set DOMAIN=localhost.
docker compose -f infrastructure/docker-compose.staging.yml up --buildWhat this starts:
-
database: PostgreSQL -
backend: Django withconfig.settings.staging -
frontend: builds the React app into a shared volume -
nginx: serves the frontend and reverse proxies/api/and/admin/
- Frontend:
http://localhost/ - API:
http://localhost/api/ - Admin:
http://localhost/admin/ - Health:
http://localhost/health
When you are done, stop the staging stack again so it does not keep running and cause conflicts later.
docker compose -f infrastructure/docker-compose.staging.yml downIf you also want to remove volumes:
Warning: down -v permanently deletes the PostgreSQL volume and all database data in it.
docker compose -f infrastructure/docker-compose.staging.yml down -vThe production deployment is intended for a Linux server that runs a self-hosted GitHub Actions runner and Docker.
infrastructure/docker-compose.prod.yml starts:
-
database: PostgreSQL with persistent storage -
backend: Django withconfig.settings.prod -
frontend: builds the static frontend bundle -
nginx: serves the frontend,/api/,/admin/,/static/, and/media/ -
certbot: renews Let's Encrypt certificates automatically
Before the first deployment, make sure you have:
- A Linux server
- A public domain name pointing to the server
- Ports
80and443open to the internet - A GitHub repository admin who can add Actions runners and repository secrets
The safest order is:
- Install Docker on the server.
- Install and register the GitHub Actions self-hosted runner.
- Add the production variables as GitHub Secrets.
- Trigger the deployment workflow once so
infrastructure/.envis generated on the server. - Run
infrastructure/certbot/setup-certbot.shonce to request the real certificate.
Install Docker and the Docker Compose plugin on the server.
Follow the official Docker instructions for your Linux distribution:
Verify the installation:
docker --version
docker compose versionInstall it from the repository settings in GitHub:
- Open
Settings -> Actions -> Runners. - Click
New self-hosted runner. - Select the correct OS and architecture for your server.
- Run the commands GitHub shows on the server.
- Install the runner as a service so it survives reboots.
Official reference:
The deploy workflow creates infrastructure/.env directly from GitHub Secrets. Every variable listed in infrastructure/.env.example should exist as a GitHub repository secret with the same name.
To add them:
- Open the repository on GitHub.
- Go to
Settings -> Secrets and variables -> Actions. - Click
New repository secret. - Open
infrastructure/.env.example. - For each variable name in that file, create a secret in GitHub with the same name and the correct production value.
The production workflow is defined in .github/workflows/deploy.yml.
It:
- Checks out the repository on the self-hosted runner.
- Creates
infrastructure/.envfrom GitHub Secrets. - Builds the production containers.
- Starts the stack with Docker Compose.
- Runs a simple post-deploy container status check.
To trigger it:
- push to
main, or - run the workflow manually from the GitHub Actions UI
After the first deployment, SSH into the server and run:
cd ~/actions-runner/_work/<repo-name>/<repo-name>/
bash infrastructure/certbot/setup-certbot.sh <domain> <email>Example:
bash infrastructure/certbot/setup-certbot.sh your-domain.example admin@example.comUseful checks on the server:
docker compose -f infrastructure/docker-compose.prod.yml psThen verify:
-
http://<domain>/healthreturnsok -
https://<domain>/serves the frontend -
https://<domain>/api/docs/serves the OpenAPI docs