Assessor_2.0/docs/DEPLOYMENT.md

4.9 KiB

Parallel deployment

Environments

The Gitea workflow deploys two permanent, isolated environments on the target server. The old HTTPS installation is outside this workflow and remains unchanged until a separately approved cutover.

Branch Compose project VPN address Secret
main assessor_prod http://10.0.0.154:4173 PROD_ENV_FILE
experimental assessor_experimental http://10.0.0.154:4174 STAGING_ENV_FILE

assessor_prod already exists on the server. Keep its current PROD_ENV_FILE database name, user and password unchanged so the existing PostgreSQL volume remains accessible. Do not rotate only the secret; password rotation requires a coordinated PostgreSQL role change.

STAGING_ENV_FILE contains a complete environment file with credentials that are distinct from stable:

ASSESSOR_POSTGRES_DB=assessor_experimental
ASSESSOR_POSTGRES_USER=assessor_experimental
ASSESSOR_POSTGRES_PASSWORD=replace-with-a-long-random-experimental-password

Do not reuse the stable database name, user, or password for experimental. Secret values must never be printed into an Actions log.

Mandatory server inventory

Before the first push-triggered deployment, connect to the target server over SSH and perform a read-only inventory. Record only technical identifiers; do not inspect database rows, uploaded files, application pages, or report text.

docker version
docker compose version
docker ps -a --format '{{.Names}}\t{{.Image}}\t{{.Ports}}\t{{.Label "com.docker.compose.project"}}'
docker volume ls
docker network ls
docker ps --filter publish=4173 --format '{{.Names}}\t{{.Ports}}'
docker ps --filter publish=4174 --format '{{.Names}}\t{{.Ports}}'

Also identify the existing reverse proxy and firewall policy without changing them. Confirm that 10.0.0.154 is a host address, port 4174 is unused, the runner uses this Docker daemon, and assessor_experimental does not belong to an unrelated stack. Port 4173 and project assessor_prod are intentionally adopted as stable; only its expected app and postgres services may exist. Any other conflict blocks deployment and is never resolved by stopping or deleting the existing resource.

Deployment behavior

Pushes to a branch are serialized per environment with an atomic lock container on the shared Docker host. This is necessary because Gitea currently ignores the GitHub-compatible workflow concurrency key. A normal job removes its lock; a crashed job's --rm lock expires after two hours. The workflow installs dependencies, runs the complete test suite and production build, validates the resolved Compose configuration, checks port/project isolation, backs up an existing target environment, then builds and starts only that environment.

Compose project scoping produces independent PostgreSQL and artifact volumes. PostgreSQL has no host-published port. restart: unless-stopped preserves both environments across Docker daemon and server restarts.

Feature promotion is a merge from experimental to main. Stable is rebuilt from the merged commit; databases and artifacts are never copied between the environments.

Backups and restore checks

Before every update of an existing environment, the workflow writes a custom PostgreSQL dump, an artifact archive and SHA-256 checksums to the external Docker volume assessor_deployment_backups:

<compose-project>/<UTC timestamp>/postgres.dump
<compose-project>/<UTC timestamp>/artifacts.tar.gz
<compose-project>/<UTC timestamp>/SHA256SUMS

The workflow never deletes backups. Retention and off-host copying require a separate approved operations policy. The backup volume must be included in the server's protected backup system; a volume on the same Docker host is not a complete disaster-recovery copy.

Restore tests must use an isolated temporary Compose project and synthetic data. Verify SHA256SUMS, restore postgres.dump with pg_restore --exit-on-error into a fresh database, extract the artifact archive into a fresh artifact volume, and confirm only content-free counts and health state. Never restore an experimental backup into stable or a stable backup into experimental. Never run docker compose down --volumes for either permanent environment.

Acceptance and later cutover

After deployment, verify both in-container health checks and VPN access. Import different synthetic fixtures into stable and experimental, redeploy each branch, and confirm persistence and isolation. Confirm the old HTTPS site remains available without reading its domain content.

Changing assessor.soc-ops.de is not part of the deployment workflow. After explicit production acceptance, validate the existing proxy configuration, preserve its old upstream, point it to stable, reload it, and verify TLS and health. Rollback restores the saved upstream; the archived old stack and its data must remain recoverable until a later retention decision.