6.0 KiB
42 — Implemented deployment baseline
Scope and status
Milestone 0 provides one multi-stage Dockerfile, a hardened production Compose reference, a Compose Watch development reference, and GitHub Actions gates. The production path was built and exercised on Unraid 7.2 with Docker 27.5.1 and Compose 2.40.3. Image build, container health, database degradation/recovery, migration replay, persisted restart, and backup/restore evidence are recorded in docs/43-milestone-zero-host-validation.md.
Pinned runtime images
- Node.js
24.18.0(Krypton, LTS):node:24.18.0-bookworm-slim@sha256:6f7b03f7c2c8e2e784dcf9295400527b9b1270fd37b7e9a7285cf83b6951452d - PostgreSQL
17.9:postgres:17.9-bookworm@sha256:47f917f7409eacd22fc5dfb1dee634e1b55cf0c01d1a7eb701be2227a03e0641 - pnpm
10.33.0, activated by Corepack to matchpackage.json
The Docker Hub API reported both digests as multi-platform manifest-list digests. That metadata was checked over HTTPS on 2026-07-27. Both images were pulled and exercised on the linux/amd64 validation host. Final release evidence must still record the release application's image digests and scan results.
Production setup
Use URL-safe random characters for POSTGRES_PASSWORD, because Compose embeds it in DATABASE_URL. Generate the other values with a cryptographically secure tool; do not reuse these commands' output across instances.
export POSTGRES_PASSWORD="replace-with-a-url-safe-random-password"
export SESSION_SECRET="replace-with-at-least-32-random-bytes"
export INTEGRATION_ENCRYPTION_KEY="replace-with-base64-of-exactly-32-random-bytes"
export BOOTSTRAP_TOKEN="replace-with-a-random-first-run-token"
export PUBLIC_BASE_URL="https://runbooks.example.com"
docker compose build
docker compose up -d
docker compose ps
Compose starts PostgreSQL, runs migrations as a one-shot prerequisite, and then starts independent web and worker targets. Only the web port is published. PostgreSQL remains internal. The 28 built-in playbooks remain immutable in the image at /content/playbooks, so an empty host volume cannot obscure them. operator-content, artifacts, and postgres-data persist independently of containers. /operator-content is reserved for the controlled operator-package import path; the current vertical slice does not consume it automatically.
The web and worker run as the image's unprivileged node user with all Linux capabilities dropped, no-new-privileges, a read-only root filesystem, and a bounded writable /tmp. Built-in and operator content are read-only to both runtime services. The artifact mount is writable. No Docker socket, privileged mode, host PID namespace, or application secret is included.
Development setup
The development reference contains explicit local-only credentials and must not be exposed or promoted to production.
docker compose -f docker-compose.dev.yml up --build
# For synchronized source changes with Docker Compose 2.22+:
docker compose -f docker-compose.dev.yml watch
PostgreSQL binds only to loopback by default. Change POSTGRES_DEV_PORT or DEVRUNBOOK_DEV_PORT when those ports are occupied. Compose Watch synchronizes source while keeping installed container dependencies intact and rebuilds after a lockfile change.
Unraid mapping
Create one stack with the three persistent volumes mapped to Unraid application data paths:
| Container path | Suggested host path | Purpose |
|---|---|---|
/var/lib/postgresql/data |
/mnt/user/appdata/devrunbook/postgres |
PostgreSQL data |
/operator-content |
/mnt/user/appdata/devrunbook/content |
Operator-managed playbook packages |
/artifacts |
/mnt/user/appdata/devrunbook/artifacts |
Generated artifacts |
Expose container port 3000 through the normal bridge network and configure the reverse proxy to the selected host port. Do not publish PostgreSQL. Do not mount over /content; doing so would hide the baked 28-package catalog. Map operator-managed packages to /operator-content instead. Set the WebUI URL to the configured PUBLIC_BASE_URL. The container health state comes from /health/live; readiness remains available at /health/ready.
Upgrades, backup, and restore
Before an upgrade, stop write traffic, record the current image digest, create a PostgreSQL logical dump, and back up operator-content plus artifacts. Back up encryption keys separately in a secret store; losing an encryption key makes encrypted integration credentials unrecoverable. Pull/build the new pinned image, run migration status/preflight when implemented, run the one-shot migration service, and then start worker and web. Retain the prior application image until smoke checks pass. Database rollback is migration-specific and is not yet evidenced.
The Milestone 0 rehearsal restored a logical dump and artifact archive into empty isolated volumes, confirmed the catalog and historical run, and compared the artifact digest. A final release rehearsal must additionally include any integration-secret key versions and operator content that exist by that milestone.
CI contract
.github/workflows/ci.yml uses Node 24.18.0 and pnpm 10.33.0. It runs the root pnpm verify aggregate, PostgreSQL-backed migration/integration/security gates, and the canonical Playwright browser command. CI credentials are fixed, isolated test-only values. Browser diagnostics upload only after failure and are retained for seven days. Third-party actions are pinned to commit SHAs resolved from their official v4 release branches on 2026-07-27.
No CI run is claimed by this document. Release evidence must still record the action SHAs actually executed and review any automated dependency-update proposal before merging it.
Remaining release evidence
- Build and exercise final release images for every supported architecture; Milestone 0 proved
linux/amd64only. - Rehearse the final upgrade with migration-specific rollback analysis.
- Scan final images for vulnerabilities, secrets, licenses, and unexpected files.
- Record final application image digests and measured startup/resource behavior.