# Docker ```bash docker compose up -d --build ``` Root `.env` is a **pointer only** (`COMPOSE_ENV_FILES=./backend/.env`) so Compose interpolates `${FRONTEND_PORT}`, `${BACKEND_PORT}`, … from **`backend/.env`**. All secrets and app config live in `backend/.env` (also injected into containers via `env_file`). ## How the browser reaches the API | Surface | URL | |---|---| | SPA | http://127.0.0.1:5173 | | API (host) | http://127.0.0.1:8000 | nginx on `:5173` also proxies API paths to `backend-api` (same-origin when `VITE_API_BASE` is empty). In `backend/.env`: ```env FRONTEND_PORT=5173 BACKEND_PORT=8000 FRONTEND_URL=http://127.0.0.1:5173 ``` ## Local (host Postgres) ```env PROD_ENV=false DB_USERNAME=... DB_PASSWORD=... DB_HOST=localhost DB_PORT=5432 DB_NAME=hrms DB_SSLMODE= FRONTEND_PORT=5173 BACKEND_PORT=8000 FRONTEND_URL=http://127.0.0.1:5173 ``` Containers set `IN_DOCKER=1`. With `PROD_ENV=false`, `db_setup` rewrites `localhost` / `127.0.0.1` → `host.docker.internal` for the connection URL only (SSL off unless `DB_SSLMODE` is set). ```bash cp backend/.env.example backend/.env # set JWT, OpenAI, DB_*, PROD_ENV=false docker compose up -d --build ``` | Service | Host access | |---|---| | `frontend` | `${FRONTEND_PORT:-5173}` | | `backend-api` | `${BACKEND_PORT:-8000}` | | `ats-engine` / `redis` | Compose network (optional host-ports overlay) | | `postgres` | not started (optional `--profile postgres`) | Optional loopback publishes for ATS / Redis: ```bash docker compose -f docker-compose.yml -f docker-compose.host-ports.yml up -d ``` Optional live-reload / bind mounts: ```bash docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build ``` Optional Compose Postgres (empty volume — not host data): ```bash docker compose --profile postgres up -d postgres # set DB_HOST=postgres in backend/.env, then recreate backend services ``` ## Production (RDS) In `backend/.env`, set `PROD_ENV=true` and point plain `DB_*` at RDS. Blank `DB_SSLMODE` → SSL `require` (or set `DB_SSLMODE=require` explicitly). ```bash docker compose up -d --build docker compose ps ``` First boot against RDS can take a few minutes while Alembic applies drift; the API healthcheck `start_period` is 180s so Compose does not mark it unhealthy too early. ### Schema / migrations (automatic) On every `backend-api` start: 1. Fresh empty Postgres → create all tables from models and stamp a marker. 2. Otherwise → `alembic upgrade head` if any revision files exist in the image. 3. If `DB_AUTOGENERATE=true` → detect ORM drift and apply DDL **in-memory**. 4. Apply any pending `backend/migrations/manual/*.sql`. Toggle in `backend/.env`: `DB_AUTO_MIGRATE` / `DB_AUTOGENERATE`. ### Verify ```bash docker compose config curl -sf http://127.0.0.1:5173/health curl -sf http://127.0.0.1:8000/health docker compose logs -f backend-api ``` ### Secrets - Never bake `backend/.env` into images. - Root `.env` must stay a pointer (`COMPOSE_ENV_FILES`) — no passwords there. - Do not put `DB_HOST` under Compose `environment:` (empty override blanks RDS). ## Useful commands ```bash docker compose logs -f backend-api docker compose restart backend-api docker compose down docker compose down -v ```