Files
pokemon-inventory/docs/DEPLOYMENT.md
T
2026-09-10 02:32:43 -04:00

213 lines
6.5 KiB
Markdown

# Deployment
## Environments
- development
- test/CI
- production
## Docker Compose
Baseline services:
- frontend
- API
- worker
- PostgreSQL
- Redis
- reverse proxy
Persistent data:
- PostgreSQL data
- Redis data where needed
- image storage
## Production Compose
The repository includes `compose.production.yaml` with separate API, worker,
web, PostgreSQL, and Redis services. It exposes only the Nginx web service and
keeps database and Redis ports internal to the Compose network.
Before starting, create a production `.env` containing real values for
`POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, `DATABASE_URL`,
`REDIS_URL`, `SMTP_URL`, `SMTP_FROM`, and a stable base64-encoded 32-byte
`MFA_ENCRYPTION_KEY`. Set `PRICING_HTTP_PROVIDER_ENDPOINT` to the approved
provider's HTTPS endpoint and provide its token through
`PRICING_HTTP_PROVIDER_TOKEN` when required. Production startup rejects the
fixture provider, missing endpoints, and non-HTTPS endpoints.
The deploy and rollback scripts load `PRODUCTION_ENV_FILE` (default `.env`)
and run the side-effect-free preflight before touching Compose. Run them from
the repository root:
```sh
set -a
. ./.env
set +a
./scripts/validate-production-env.sh
```
To use a separate production file, set `PRODUCTION_ENV_FILE=/secure/path/.env`
when invoking the deploy or rollback script.
Start the stack and apply migrations with:
```sh
docker compose -f compose.production.yaml up -d --build postgres redis
docker compose -f compose.production.yaml run --rm api npm run db:migrate
docker compose -f compose.production.yaml up -d --build api worker web
```
The default web port is `8080` and can be changed with `WEB_PORT`. The API and
worker images share the same Node workspace image; the web image builds the Vue
application and serves it through Nginx with SPA fallback and internal `/api`
proxying.
API and worker share the persistent `image-storage` volume at
`/var/lib/pokemon-inventory/images`. Set `IMAGE_STORAGE_VOLUME` to a NAS bind
mount when deploying outside Docker-managed volumes, for example:
```env
IMAGE_STORAGE_VOLUME=/volume1/docker/pokemon-inventory/images
```
Include this directory in image-storage backups alongside PostgreSQL dumps.
## Tagged Releases And Rollback
Use an immutable release tag for every deployment. The production Compose file
uses `IMAGE_TAG` to select matching API, worker, and web images. Deploy with:
```sh
RELEASE_TAG=2026-09-09-01 ./scripts/deploy-production.sh
```
The deploy script builds the three tagged images, waits for healthy data
services, applies migrations with the tagged API image, and then switches the
application services after they are healthy. Keep the previous release tag and
migration state with the deployment record.
Rollback application images with:
```sh
RELEASE_TAG=2026-09-08-02 ./scripts/rollback-production.sh
```
The rollback script waits for the restored application services to become
healthy before running the smoke check. Image rollback is safe only when the previous image understands the current
database schema. If the release included an incompatible schema change, restore
the matching PostgreSQL backup before starting the previous images. Always
verify the API and web health checks after a rollback.
Run the post-deploy smoke check against the public endpoints:
```sh
WEB_BASE_URL=http://localhost:8080 \
./scripts/smoke-production.sh
```
The check performs read-only requests against API health, the web health path,
and public search. Without `API_HEALTH_URL`, the API check runs inside the
Compose API container, which is appropriate because the production API port is
not exposed publicly. It exits nonzero on the first failed request. Both deploy
and rollback scripts run this check after changing application services.
## Health Checks
Production Compose waits for healthy PostgreSQL, Redis, and API services before
starting the web service. The API health endpoint is `GET /health`; the web
container checks its `/health` response through Nginx.
## Backup And Restore
Create a PostgreSQL custom-format dump from the running production stack:
```sh
BACKUP_DIR=/secure/backups \
POSTGRES_DB=inventory \
POSTGRES_USER=inventory \
POSTGRES_PASSWORD='use-secret-storage' \
./scripts/backup-postgres.sh
```
Restore only during a planned maintenance window because `pg_restore` replaces
matching database objects:
```sh
BACKUP_FILE=/secure/backups/inventory-20260909T000000Z.dump \
POSTGRES_DB=inventory \
POSTGRES_USER=inventory \
POSTGRES_PASSWORD='use-secret-storage' \
./scripts/restore-postgres.sh
```
Verify a dump before scheduling a restore:
```sh
BACKUP_FILE=/secure/backups/inventory-20260909T000000Z.dump \
./scripts/verify-postgres-backup.sh
```
This checks that the file is a readable PostgreSQL custom-format archive without
changing any database.
Restore a database dump during a scheduled window:
```sh
BACKUP_FILE=/secure/backups/inventory-20260909T000000Z.dump \
POSTGRES_DB=inventory \
POSTGRES_USER=inventory \
POSTGRES_PASSWORD='use-secret-storage' \
./scripts/restore-postgres.sh
```
Restore image storage snapshots using the preserved NAS backup:
```sh
IMAGE_STORAGE_SOURCE=/secure/backups/pokemon-inventory-images \
IMAGE_STORAGE_DESTINATION=/var/lib/pokemon-inventory/images \
./scripts/restore-image-storage.sh
```
Back up image storage and the deployment `.env` through the NAS secret/backup
system as well. Never commit either one. Before upgrading, record the current
image tags and database migration state. To roll back, stop the application
services, restore the previous image tags, restore the database dump if the
schema changed, then restore the image snapshot and verify `/health` and a
read-only catalog request.
## Reverse proxy
Responsible for:
- HTTPS/TLS
- routing
- compression
- security headers
- rate limiting where appropriate
## Configuration
Environment variables/secrets:
- database connection
- Redis connection
- authentication/session secrets
- provider credentials
- email credentials
- storage credentials
Application settings such as pricing strategy and site settings belong in the database.
## Backups
Back up:
- PostgreSQL
- image storage
- required environment/configuration information
Retention policy and restore procedures must be documented for production.
Disaster recovery should allow reconstruction elsewhere from database backup, image backup, and deployment/environment configuration.
## Future hosting
Initial deployment targets a home server/NAS. The architecture should allow later movement to an online server or managed infrastructure without redesigning the domain model.