Update deployment docs, complete Deployment Strategy epic
Rewrite DEPLOYMENT.md to reflect current state (CI/CD, backups, merge strategy). Expand CI paths-ignore to skip docs, license, gitignore, and deploy workflow changes. Add merge strategy to CLAUDE.md. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
228
DEPLOYMENT.md
228
DEPLOYMENT.md
@@ -1,31 +1,95 @@
|
||||
# Deployment
|
||||
|
||||
This document describes the deployment architecture and workflows for the nuzlocke-tracker. It is a living document — sections marked with **TODO** are planned but not yet implemented.
|
||||
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` (built image) |
|
||||
| Database | PostgreSQL 16 (Docker volume) | PostgreSQL 16 (Docker volume) |
|
||||
| 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) |
|
||||
| Container Management | — | Portainer CE |
|
||||
| Reverse Proxy | — | Nginx Proxy Manager |
|
||||
| CI/CD | — | Gitea Actions |
|
||||
|
||||
### Services
|
||||
|
||||
- **Gitea** — self-hosted Git server, container registry, and (future) CI/CD. Accessible at `gitea.nerdboden.de` via SSL.
|
||||
- **Portainer** — Docker management UI. Accessible at `portainer.nerdboden.de` via SSL. Manages the production stack and provides webhook-triggered redeployments.
|
||||
- **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
|
||||
|
||||
Docker images are hosted on Gitea's built-in container registry as **user-level packages**.
|
||||
|
||||
### Image naming
|
||||
|
||||
Images use the format `gitea.nerdboden.de/<user>/<image>:<tag>`:
|
||||
Images are hosted on Gitea's built-in container registry as user-level packages:
|
||||
|
||||
```
|
||||
gitea.nerdboden.de/thefurya/nuzlocke-tracker-api:latest
|
||||
@@ -35,118 +99,58 @@ 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 from the dev machine:
|
||||
```bash
|
||||
docker login gitea.nerdboden.de
|
||||
```
|
||||
Use your Gitea username and the access token as password.
|
||||
2. Log in: `docker login gitea.nerdboden.de` (username + token as password).
|
||||
|
||||
### Pushing images
|
||||
## Production Environment
|
||||
|
||||
```bash
|
||||
# Build and tag
|
||||
docker build -t gitea.nerdboden.de/thefurya/nuzlocke-tracker-api:latest ./backend
|
||||
docker build -t gitea.nerdboden.de/thefurya/nuzlocke-tracker-frontend:latest ./frontend
|
||||
### File layout on Unraid
|
||||
|
||||
# Push
|
||||
docker push gitea.nerdboden.de/thefurya/nuzlocke-tracker-api:latest
|
||||
docker push gitea.nerdboden.de/thefurya/nuzlocke-tracker-frontend:latest
|
||||
```
|
||||
/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)
|
||||
```
|
||||
|
||||
Pushed images are visible under the **Packages** tab on your Gitea user profile.
|
||||
### Environment variables
|
||||
|
||||
## Branching Strategy
|
||||
|
||||
The project uses a `main` / `develop` / `feature/*` branching model.
|
||||
|
||||
| Branch | Purpose |
|
||||
|---|---|
|
||||
| `main` | Always production-ready. Deploy script builds from here. |
|
||||
| `develop` | Integration branch for day-to-day work. |
|
||||
| `feature/*` | Short-lived branches off `develop` for individual features/fixes. |
|
||||
|
||||
### Workflow
|
||||
|
||||
1. Create `feature/xyz` from `develop`
|
||||
2. Work on the feature, commit, merge into `develop`
|
||||
3. When ready to deploy: merge `develop` into `main`
|
||||
4. Run the deploy script (see below)
|
||||
|
||||
## Deploying
|
||||
|
||||
> **TODO** — deploy script (`./deploy.sh`) not yet created.
|
||||
|
||||
The deploy script will automate:
|
||||
|
||||
1. Build Docker images from `main`
|
||||
2. Tag and push to the Gitea container registry
|
||||
3. Trigger the Portainer webhook to pull new images and restart the stack
|
||||
|
||||
### Manual deployment
|
||||
|
||||
Until the deploy script is in place, deploy manually:
|
||||
|
||||
```bash
|
||||
# 1. Ensure you're on main with latest changes
|
||||
git checkout main
|
||||
|
||||
# 2. Build and push images
|
||||
docker build -t gitea.nerdboden.de/thefurya/nuzlocke-tracker-api:latest ./backend
|
||||
docker build -t gitea.nerdboden.de/thefurya/nuzlocke-tracker-frontend:latest ./frontend
|
||||
docker push gitea.nerdboden.de/thefurya/nuzlocke-tracker-api:latest
|
||||
docker push gitea.nerdboden.de/thefurya/nuzlocke-tracker-frontend:latest
|
||||
|
||||
# 3. On Unraid (or via Portainer): pull and restart
|
||||
docker compose -f docker-compose.prod.yml pull
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
## Production Compose
|
||||
|
||||
> **TODO** — `docker-compose.prod.yml` not yet created.
|
||||
|
||||
The production compose file will differ from the dev compose in:
|
||||
|
||||
- Uses pre-built images from the Gitea registry (no source volume mounts)
|
||||
- No hot reload / debug mode
|
||||
- Production environment variables
|
||||
- Proper restart policies
|
||||
- Frontend served as a static build (not Vite dev server)
|
||||
|
||||
## Portainer
|
||||
|
||||
Portainer CE is running on Unraid at `portainer.nerdboden.de`.
|
||||
|
||||
- Manages the production Docker stack
|
||||
- **TODO**: Configure a webhook for automated redeployment (pull latest images + restart on trigger)
|
||||
|
||||
## Nginx Proxy Manager
|
||||
|
||||
NPM runs on Unraid and handles SSL termination and routing for:
|
||||
|
||||
- `gitea.nerdboden.de` → Gitea
|
||||
- `portainer.nerdboden.de` → Portainer
|
||||
- **TODO**: `nuzlocke.nerdboden.de` (or similar) → nuzlocke-tracker frontend/API
|
||||
|
||||
## Environment & Secrets
|
||||
|
||||
> **TODO** — `.env.prod` template not yet created.
|
||||
|
||||
Production environment variables to configure:
|
||||
|
||||
- `DATABASE_URL` — PostgreSQL connection string
|
||||
- `DEBUG` — must be `false` in production
|
||||
- Additional secrets TBD
|
||||
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 stored in a named Docker volume.
|
||||
PostgreSQL 16 with data persisted via bind mount at `./data/postgres/`.
|
||||
|
||||
- Migrations run automatically on API container startup (Alembic)
|
||||
- **TODO**: Set up scheduled backups (`pg_dump` cron job on Unraid)
|
||||
- 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
|
||||
|
||||
> **TODO** — rollback procedure to be documented once image tagging strategy is finalized.
|
||||
Currently images are tagged as `latest` only. To roll back:
|
||||
|
||||
General approach: tag images with version/commit hash in addition to `latest`, so rolling back means redeploying a previous tag.
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user