109 lines
4.0 KiB
Markdown
109 lines
4.0 KiB
Markdown
# Docker Deployment (On Host)
|
|
|
|
Alternative way to run the project for local stack and portable deployment.
|
|
On the production Flatlogic VM **Docker is not used** — there it's PM2 + local PostgreSQL
|
|
+ Cloudflare tunnel (see [`deployment-vm.md`](./deployment-vm.md)).
|
|
|
|
The project consists of two applications:
|
|
|
|
- `frontend/` — Vite + React + TypeScript (SPA). Build output → `frontend/dist/`.
|
|
- `backend/` — Express + Sequelize on TypeScript/ESM. Build output → `backend/dist/`.
|
|
|
|
## Files
|
|
|
|
In the repository root:
|
|
|
|
| File | Purpose | Port |
|
|
|---|---|---|
|
|
| `Dockerfile` | production single-image: compiled backend serves API **and** SPA (from `public`) | 8080 |
|
|
| `Dockerfile.dev` | staging VM replica: nginx + frontend `vite preview` (3001) + backend (3000), `dev_stage` | 8080 |
|
|
| `docker/docker-compose.yml` | local stack: PostgreSQL + app (from `Dockerfile`), `NODE_ENV=development` | 8080 |
|
|
|
|
All stages use `node:24-alpine`, `npm ci`. Native `bcrypt` is compiled with the toolchain
|
|
(`python3 make g++`) in the builder stage; the compiled `node_modules` is copied to runtime
|
|
(without recompilation). `rolldown` (Vite bundler) has musl bindings, so alpine works.
|
|
|
|
## 1. Quick Start — docker compose (Recommended)
|
|
|
|
Starts PostgreSQL + application with a single command. Backend in `development` serves API
|
|
and SPA on the same port (login works over http).
|
|
|
|
```bash
|
|
cd docker
|
|
docker compose up --build
|
|
# open http://localhost:8080
|
|
```
|
|
|
|
Parameters (env vars set in `docker-compose.yml`, modify as needed):
|
|
|
|
- `SECRET_KEY=local_dev_secret_change_me`
|
|
- `DB_*` point to the `db` service (Postgres 16, DB/user `app_local`)
|
|
- Seed passwords are hardcoded in the seeder (see `CLAUDE.md` for credentials)
|
|
|
|
Stop and remove (including DB data):
|
|
|
|
```bash
|
|
docker compose down -v
|
|
```
|
|
|
|
## 2. Production Single-Image (`Dockerfile`)
|
|
|
|
One container: compiled backend on `NODE_ENV=production` listens on 8080 and
|
|
serves both `/api` and the built SPA (frontend is placed in `public`).
|
|
|
|
```bash
|
|
docker build -t schoolchain:prod .
|
|
|
|
docker run --rm -p 8080:8080 \
|
|
-e NODE_ENV=production \
|
|
-e PORT=8080 \
|
|
-e SECRET_KEY=<secret> \
|
|
-e ALLOWED_ORIGINS=https://<your-domain> \
|
|
-e DB_HOST=<host> -e DB_PORT=5432 -e DB_NAME=<db> -e DB_USER=<user> -e DB_PASS=<pass> \
|
|
schoolchain:prod
|
|
```
|
|
|
|
> In `NODE_ENV=production`, `SECRET_KEY` and `ALLOWED_ORIGINS` are required, and cookies
|
|
> have the `Secure` flag (HTTPS frontend needed in front of the container). The startup
|
|
> command (`npm run start:production`) automatically runs migrations and seeders —
|
|
> the DB must be accessible.
|
|
|
|
## 3. Staging VM Replica (`Dockerfile.dev`)
|
|
|
|
Replicates the VM setup in one image: nginx (8080) → frontend `vite preview` (3001) + backend
|
|
(3000), `NODE_ENV=dev_stage`, source maps. Run behind HTTPS (in `dev_stage`
|
|
cookies are `Secure`).
|
|
|
|
```bash
|
|
docker build -t schoolchain:staging -f Dockerfile.dev .
|
|
docker run --rm -p 8080:8080 \
|
|
-e SECRET_KEY=<secret> \
|
|
-e DB_HOST=<host> -e DB_PORT=5432 -e DB_NAME=<db> -e DB_USER=<user> -e DB_PASS=<pass> \
|
|
schoolchain:staging
|
|
```
|
|
|
|
## 4. `.dockerignore`
|
|
|
|
Excludes `node_modules`, `dist`, `public`, `**/.env` (to avoid baking dev secrets
|
|
into the image — environment is set at runtime), `.git`, logs.
|
|
|
|
## 5. NODE_ENV — What to Choose
|
|
|
|
| `NODE_ENV` | Where | Characteristics |
|
|
|---|---|---|
|
|
| `development` | local `docker compose` | http login works; no `ALLOWED_ORIGINS` requirement; cookie without `Secure` |
|
|
| `dev_stage` | `Dockerfile.dev` (staging) | production-like but lenient; requires HTTPS (`Secure` cookie); reflects origin |
|
|
| `production` | `Dockerfile` (prod) | requires `SECRET_KEY` + `ALLOWED_ORIGINS`; `Secure` cookie; strict CORS |
|
|
|
|
## 6. Scheduled maintenance: refresh-token cleanup
|
|
|
|
Run the retention cleanup periodically (host cron or a scheduled one-off
|
|
container), e.g. daily:
|
|
|
|
```bash
|
|
docker exec <backend-container> npm run db:cleanup-tokens:prod
|
|
```
|
|
|
|
Deletes refresh-token rows past `AUTH_REFRESH_TOKEN_RETENTION_MS` (default 7
|
|
days); idempotent and session-safe. See `backend/docs/cookie-auth.md`.
|