Aller au contenu

Development & Deployment Workflow

Architecture Overview

All environments (dev, staging, prod) run on a single server behind one shared nginx reverse proxy (nginx-proxy). They share a PostgreSQL instance (shared_postgres) and a Redis instance (shared_redis), isolated by database name and Redis db number.

                        ┌─────────────────────────────────────────────────┐
                        │                  nginx-proxy                    │
                        │         :80 (redirect) / :443 (SSL)            │
                        └────┬──────────────┬──────────────┬─────────────┘
                             │              │              │
           aletheia-dev.     │  aletheia-   │  aletheia.   │
           groupe-suffren.com│  staging.    │  groupe-     │
                             │  groupe-     │  suffren.com │
                             │  suffren.com │              │
                             ▼              ▼              ▼
                        ┌─────────┐  ┌──────────┐  ┌──────────┐
                        │ dev-web │  │stag-web  │  │prod-web  │
                        │  :8000  │  │  :8000   │  │  :8000   │
                        └─────────┘  └──────────┘  └──────────┘
                             │              │              │
                        ┌────┴──────────────┴──────────────┴─────┐
                        │         shared_postgres / shared_redis  │
                        └────────────────────────────────────────┘

Local development stack (separate)

docker compose up  →  localhost:8000
                      ├── aletheia_web    (Django runserver, source-mounted)
                      ├── aletheia_db     (Postgres, port 5432)
                      ├── aletheia_redis  (Redis, port 6379)
                      ├── aletheia_celery
                      └── aletheia_celery_beat

Environments

Local Dev (server) Staging (server) Prod (server)
URL localhost:8000 aletheia-dev.groupe-suffren.com aletheia-staging.groupe-suffren.com aletheia.groupe-suffren.com
Compose files docker-compose.yml + override.yml (auto) docker-compose.yml + dev.yml docker-compose.yml + staging.yml docker-compose.yml + prod.yml
Env file Inline in override.yml /opt/docker/aletheia/envs/.env.dev /opt/docker/aletheia/envs/.env.staging /opt/docker/aletheia/envs/.env.prod
Django settings development development production production
DEBUG True True False False
Web server runserver (auto-reload) gunicorn gunicorn gunicorn
Source Mounted (live) Baked into image Baked into image Baked into image
Database Local aletheia_db container shared_postgres / aletheia_dev shared_postgres / aletheia_staging shared_postgres / aletheia_prod
Redis Local aletheia_redis / db 0 shared_redis / db 4-5 shared_redis / db 2-3 shared_redis / db 0-1
Email Console (disabled) Console (disabled) Brevo SMTP Real SMTP
Sentry Disabled Disabled Enabled Enabled
Auth None nginx basic auth nginx basic auth None (public)
Memory (web) Unlimited 512M 512M 768M

Day-to-Day Workflow

1. Develop locally

make up             # start local stack (Postgres, Redis, Django runserver)
# ... edit code — changes reload automatically ...
make test           # run tests
make format         # auto-format (black + isort)
make lint           # check linting (black, isort, flake8, mypy)
make migrate        # apply new migrations
make shell          # open a bash shell in the web container
make logs           # tail all container logs

2. Commit and push

git add ...
git commit -m "description"
git push origin develop

3. Deploy to staging for validation

make deploy ENV=staging          # deploys current develop branch

This builds the Docker image, runs migrations, collects static files, and restarts the containers. The code is baked into the image (no source mount on server).

4. Release to production

git tag v1.2
git push origin v1.2
make deploy ENV=prod REF=v1.2   # production requires an explicit tag

Makefile Usage

Every make command defaults to the local environment. Use ENV= to target a server environment:

make ps                    # local containers
make ps ENV=staging        # staging containers
make logs ENV=prod         # tail production logs
make db-backup ENV=prod    # backup production database
make migrate ENV=staging   # run migrations on staging

Key commands

Command Purpose
make up / make down Start/stop containers
make build / make rebuild Build images (rebuild = no cache)
make test / make test-coverage Run pytest
make lint / make format Code quality
make migrations / make migrate Database schema
make shell Bash into web container
make db-backup / make db-restore Database backup/restore
make deploy ENV=<env> Deploy via deploy.sh
make i18n-update Extract, translate, compile i18n
make sync-all Full Doctolib sync

Branching Model

Simple linear history with two branches:

  • develop — ongoing work, deployed to dev/staging
  • main — production releases only

Tags (v1.0, v1.1, ...) mark release points. Production deploys require a tag.

develop:  d9e6624 ← a3f806a ← ... ← 9480eaa (v1.1) ← a8634f7 ← 01d19f3
main:                                                              01d19f3

deploy.sh

Located at the repo root. Handles all server deployments:

./deploy.sh dev              # deploy develop branch to dev
./deploy.sh staging          # deploy develop branch to staging
./deploy.sh staging feature  # deploy a specific branch to staging
./deploy.sh prod v1.2        # deploy tag v1.2 to production (tag required)

What it does: 1. git fetch --all --tags 2. git checkout <ref> (branch or tag) 3. docker compose build (code baked into image) 4. Restores previous git checkout 5. python manage.py migrate 6. python manage.py collectstatic 7. docker compose up -d 8. nginx -s reload

Nginx Configuration

Nginx configs live in /opt/docker/nginx/conf.d/:

File Purpose
aletheia-dev.conf Active dev config
aletheia-dev.conf.full Full proxy config (template)
aletheia-dev.conf.temp Placeholder 503 page
aletheia-staging.conf Active staging config
aletheia-prod.conf Active prod config

Dev and staging are protected by basic auth (credentials in /opt/docker/nginx/.htpasswd). Production has rate limiting (limit_req zone=general burst=20 nodelay).

Known Caveats

Dev server environment is redundant

Dev and staging both deploy from develop with the same architecture. Unless there is a specific need for a second server environment (separate dataset, second tester), staging alone covers the "test on server before prod" use case. Consider either: - Dropping the dev environment entirely (local + staging + prod) - Giving dev a distinct role (e.g., auto-deploy on push via CI/CD)

Dev nginx config may show a 503 placeholder

After initial SSL setup, the dev nginx config (aletheia-dev.conf) may still be the placeholder that returns 503 "deployment in progress". To activate:

cp /opt/docker/nginx/conf.d/aletheia-dev.conf.full /opt/docker/nginx/conf.d/aletheia-dev.conf
docker exec nginx-proxy nginx -s reload

No CI/CD pipeline

Deployments are manual via deploy.sh / make deploy. There is no automated testing or deployment on push. All quality checks (tests, linting) must be run locally before deploying.

Single-server architecture

All environments share one server, one Postgres instance, and one Redis instance. Isolation is by Docker project name, database name, and Redis db number — not by infrastructure. A resource-intensive operation in one environment can affect others.

Code is baked into server images

On the server, source code is built into the Docker image (no volume mount). Every code change requires a full redeploy (make deploy ENV=...). This is intentional — it ensures reproducible deployments — but means there is no "hot reload" on server environments.

Static files require collectstatic

Server environments serve static files via nginx from a shared volume. After deploying, deploy.sh runs collectstatic automatically. If static files look stale, run:

make collectstatic ENV=staging