# Deployment This document describes the deployment architecture and workflows for the nuzlocke-tracker. ## Architecture Overview | Component | Dev (Laptop/PC) | Production (Unraid) | |---|---|---| | API | `docker-compose.yml` (hot reload) | `docker-compose.prod.yml` (built image) | | Frontend | `docker-compose.yml` (Vite dev server) | `docker-compose.prod.yml` (nginx, static build) | | Database | PostgreSQL 16 (Docker volume) | PostgreSQL 16 (bind mount) | | Container Registry | — | Gitea (user-level packages) | | Reverse Proxy | — | Nginx Proxy Manager | | CI/CD | — | Gitea Actions | ### Services - **Gitea** — self-hosted Git server, container registry, and CI/CD runner. Accessible at `gitea.nerdboden.de` via SSL. - **Nginx Proxy Manager** — reverse proxy with SSL termination for all services on the Unraid server. ## Branching Strategy | Branch | Purpose | Merge strategy | |---|---|---| | `main` | Always production-ready. Deploy workflow builds from here. | Merge commit from `develop` | | `develop` | Integration branch for day-to-day work. CI runs on push. | Squash merge from `feature/*` | | `feature/*` | Short-lived branches off `develop` for individual features/fixes. | — | ### Workflow 1. Create `feature/xyz` from `develop` 2. Work on the feature, commit, squash merge into `develop` 3. CI runs automatically on `develop` (lint checks) 4. When ready to deploy: merge `develop` into `main` (merge commit) 5. Trigger the deploy workflow via `workflow_dispatch` in Gitea Actions ## CI/CD (Gitea Actions) The project uses two Gitea Actions workflows (GitHub Actions-compatible syntax): ### CI (`.github/workflows/ci.yml`) **Triggers:** Push to `develop`, PRs targeting `develop` (skips bean-only changes) **Jobs:** - `backend-lint` — `ruff check` + `ruff format --check` - `frontend-lint` — `eslint` + `tsc -b` ### Deploy (`.github/workflows/deploy.yml`) **Triggers:** Manual via `workflow_dispatch` (must be on `main`) **Steps:** 1. Login to Gitea container registry 2. Build Docker images (linux/amd64) for API and frontend 3. Push images to the Gitea registry 4. SCP compose file and backup script to Unraid 5. SSH into Unraid and run `docker compose pull && docker compose up -d` ### Secrets (configured in Gitea repo settings) | Secret | Purpose | |---|---| | `REGISTRY_USERNAME` | Gitea username for pushing images | | `REGISTRY_PASSWORD` | Gitea access token (`read:package` + `write:package` scopes) | | `DEPLOY_SSH_KEY` | SSH private key for `root@192.168.1.10` | ### Runner An `act_runner` container runs on Unraid with the Docker socket mounted, registered with the Gitea instance. ## Manual Deployment The `./deploy.sh` script can also be used to deploy from the dev machine: ```bash # Ensure you're on main with latest changes git checkout main ./deploy.sh ``` The script: 1. Checks you're on `main` with no uncommitted changes 2. Builds Docker images (podman or docker, linux/amd64) 3. Pushes to the Gitea container registry 4. SCPs `docker-compose.prod.yml` and `backup.sh` to Unraid 5. Generates `.env` with a random `POSTGRES_PASSWORD` if missing 6. Pulls images and restarts containers on Unraid via SSH ## Container Registry Images are hosted on Gitea's built-in container registry as user-level packages: ``` gitea.nerdboden.de/thefurya/nuzlocke-tracker-api:latest gitea.nerdboden.de/thefurya/nuzlocke-tracker-frontend:latest ``` ### Authentication 1. Create a Gitea access token at **Settings > Applications** with `read:package` and `write:package` scopes. 2. Log in: `docker login gitea.nerdboden.de` (username + token as password). ## Production Environment ### File layout on Unraid ``` /mnt/user/appdata/nuzlocke-tracker/ ├── docker-compose.yml # production compose (synced from repo) ├── .env # POSTGRES_PASSWORD (auto-generated) ├── backup.sh # database backup script (synced from repo) ├── backups/ # pg_dump backups (daily, 7-day retention) │ └── cron.log └── data/ └── postgres/ # PostgreSQL data (bind mount) ``` ### Environment variables The `.env` file is auto-generated on first deploy with a random `POSTGRES_PASSWORD`. The compose file references it for both the database and API connection string. ## Database PostgreSQL 16 with data persisted via bind mount at `./data/postgres/`. - Migrations run automatically on API container startup (`alembic upgrade head`) - Daily backups via `pg_dump` scheduled through the Unraid User Scripts plugin (03:00, 7-day retention) ### Backup Backups are created by `backup.sh` and stored in `/mnt/user/appdata/nuzlocke-tracker/backups/`. The script is scheduled via the Unraid User Scripts plugin. ### Restore ```bash cd /mnt/user/appdata/nuzlocke-tracker gunzip -c backups/nuzlocke-YYYYMMDD-HHMMSS.sql.gz | \ docker compose exec -T db psql -U postgres nuzlocke ``` ## Rollback Currently images are tagged as `latest` only. To roll back: 1. Revert the merge commit on `main` 2. Trigger the deploy workflow (or run `./deploy.sh`) to rebuild and push For more granular rollback, consider adding commit-hash tags to images in the future. ## Nginx Proxy Manager NPM runs on Unraid and handles SSL termination and routing: - `nuzlocke.nerdboden.de` → nuzlocke-tracker frontend (port 9080) - `gitea.nerdboden.de` → Gitea