4.0 KiB
Docker
Production stack is self-contained: Postgres, Redis, API, ATS engine, Taskiq workers, and the React SPA. Only the frontend is published on the host.
Production
# 1. Root compose env (DB password, port, workers) — see .env.example
cp .env.example .env
# Edit DB_PASSWORD (and JWT_SECRET_KEY inside backend/.env)
# 2. App secrets (JWT, OpenAI, email, Buffer, …)
cp backend/.env.example backend/.env
# Fill JWT_SECRET_KEY, OPENAI_API_KEY, DB_* matching the Compose postgres, …
# 3. Build and start
docker compose up -d --build
docker compose ps
| Service | Image | Host port |
|---|---|---|
frontend |
hrms-frontend:local |
${FRONTEND_PORT:-80} |
backend-api |
hrms-backend:local |
(internal) |
ats-engine |
hrms-ats-engine:local |
(internal) |
redis |
redis:7-alpine |
(internal) |
postgres |
hrms-postgres:local |
(internal) |
| Taskiq workers / schedulers | hrms-backend:local |
(internal) |
Browser → http://<host>/ → nginx (same-origin) → backend-api:8000.
CV files live in the attachments-data named volume (shared by API + workers).
Schema / migrations (automatic)
On every backend-api start (docker compose up -d --build):
- Fresh empty Postgres → create all tables from models and stamp a marker.
- Otherwise →
alembic upgrade headif any revision files exist in the image (they normally do not — versions stay gitignored and are excluded from builds). - If
DB_AUTOGENERATE=true(Compose default) → detect ORM drift (new/changed/removed columns or tables) and apply DDL in-memory — noversions/*.pyis written on the server. - Apply any pending
backend/migrations/manual/*.sql(seed/RBAC batches only).
Revision scripts under backend/migrations/versions/ remain gitignored and are
listed in .dockerignore so they never ship in the production image.
Toggle with root .env: DB_AUTO_MIGRATE / DB_AUTOGENERATE (default true).
Verify
docker compose config
curl -sf http://127.0.0.1/health # nginx → backend /health
curl -sf -o /dev/null -w "%{http_code}\n" http://127.0.0.1/
docker compose logs -f backend-api
Open the SPA, sign in, confirm /jobs lists requisitions (Network: /jobs/fetch
same-origin JSON, not HTML).
Secrets
- Never bake
.envinto images (.dockerignorealready excludes them). - Require a strong
DB_PASSWORDandJWT_SECRET_KEYbefore any real deploy. - Root
.envis for Compose substitution;backend/.envis for the app process.
TLS
This stack serves HTTP on the frontend port. Terminate TLS at a reverse proxy or cloud load balancer in front of port 80.
Local development (host Postgres)
Restores published ports, bind-mounted attachments, and --reload:
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build
| Override | Value |
|---|---|
DB_HOST |
host.docker.internal |
| Frontend | ${FRONTEND_PORT:-5173} |
| API | ${BACKEND_PORT:-8000} |
| ATS | ${ATS_PORT:-8100} |
| Redis | ${REDIS_PORT:-6379} |
| Attachments | ./backend/inbox/decoded_attachments |
Container Postgres still starts (production default) but is unused while
DB_HOST=host.docker.internal. Host Postgres must accept Docker-bridge clients
(listen_addresses = '*', pg_hba for the bridge subnet).
For the Vite HMR loop, run npm run dev on the host against
frontend/.env.development (VITE_API_BASE=http://127.0.0.1:8000).
Data migration
The Compose Postgres volume starts empty. To move an existing host database:
pg_dump -Fc hrms > hrms.dump
# with postgres published via the dev overlay, or `docker compose exec -T postgres …`
pg_restore -h 127.0.0.1 -p 5433 -U postgres -d hrms --clean --if-exists hrms.dump
Useful commands
docker compose logs -f backend-api
docker compose logs -f taskiq-worker
docker compose restart backend-api
docker compose down # keep volumes
docker compose down -v # wipe postgres + attachments + redis data