VPS preflight

Do not expose the app until production posture passes locally on the target server.

This runbook turns a disposable/private VPS-like rehearsal host into a repeatable, non-destructive gate: load a production-shaped environment, run the bootstrap bundle, validate compose shape, run local diagnostics, then connect the HTTPS reverse proxy only after blocking checks are clean. Passing this rehearsal proves a transplantable container/edge shape; it is not the later official long-lived VPS cutover.

Bundle

Files in this slice

FilePurposeExposure rule
.env.production.exampleCopy-safe production variable template with placeholders.Commit template only; never commit .env.production.
docker-compose.vps.ymlVPS-oriented app/Postgres/Redis skeleton with no app host port published and named volumes for Postgres, private media, backups, backup manifests, and custody ledgers.Use first on a disposable test-VPS rehearsal host, then transplant the proven shape to the official VPS with fresh private secrets/evidence; do not use the dev compose file for production.
deploy/reverse-proxy/Copy-safe Caddy/nginx edge templates for the public TLS handoff after preflight.Use one template as a starting point; keep live configs and TLS secrets off-repo.
vps-bootstrap-planNon-destructive CLI bundle that checks the private env file, compose skeleton, proxy templates, gitignored runtime ledger paths, and safe operator order.Run before starting VPS services; it does not start compose, create directories, connect to databases, or touch backup contents.
deployment-rehearsal-planSecret-free operator checklist that connects bootstrap, preflight, external smokes, evidence records, backup metadata, restore-drill planning, and private readiness.Run before copying commands into a VPS session; it prints commands only and performs no deployment actions.
production-preflightCLI gate that blocks local profile, placeholder domains, insecure cookies, missing owner gate, local-only backups, and long owner sessions.Run on the VPS before public proxy exposure.
Docker HEALTHCHECKContainer-level shallow /healthz liveness check.Public health remains shallow; deep diagnostics stay local.
Step 1

Create the private production environment

cp .env.production.example .env.production
chmod 600 .env.production
# Edit every CHANGE_ME value before starting services.
Step 2

Run the bootstrap bundle before services start

cargo run -- vps-bootstrap-plan --env-file .env.production

The bootstrap bundle reads only local files and secret-free metadata. It verifies that the live env file is not the committed example, no placeholder variables remain, the env file is private, backup manifest/custody ledgers are git-ignored, the VPS compose file persists backup manifest/custody ledgers in named volumes, the compose file does not publish host ports, and the Caddy/nginx templates preserve body caps, access-log minimization, and client-IP header stripping. It also verifies that the stock-Caddy rate-limit boundary remains explicit and that nginx retains its memory-only sensitive-POST throttle markers; it does not prove live throttle behavior or WAF/DDoS resilience.

Boundary: the command does not start Docker, create directories, connect to Postgres, copy/encrypt/upload backups, restore backups, or inspect backup contents. It prints the safe run order and exits non-zero on blocking setup failures.

For the full private VPS operator sequence, generate deployment-rehearsal-plan with the selected HTTPS rehearsal origin and backup ID before starting the live rehearsal.

Step 3

Validate compose shape without starting public exposure

NOCTURNE_ENV_FILE=.env.production docker compose \
  -f docker-compose.vps.yml \
  --env-file .env.production \
  config

The VPS compose skeleton intentionally uses expose, not ports, for the application. That keeps the Rust app private to the Docker network until you explicitly attach a reverse proxy. It also keeps backup artifacts, manifest ledgers, and custody ledgers in separate named volumes so container restarts do not erase the metadata required by private-VPS readiness. The development docker-compose.yml publishes app, Postgres, and Redis host ports for local testing and should not be the production exposure file.

Step 4

Run production preflight on the target server

# If running from the host binary:
set -a; . ./.env.production; set +a
cargo run -- production-preflight

# If running through the VPS compose file:
NOCTURNE_ENV_FILE=.env.production docker compose \
  -f docker-compose.vps.yml \
  --env-file .env.production \
  run --rm app nocturne-platform production-preflight

The preflight command runs strict production config checks plus the deeper local doctor. It returns non-zero for blocking failures. Warnings are still operator review items; do not ignore a bind/network warning unless the app is definitely reachable only through a private Docker network or host-level HTTPS proxy.

Step 5

Only then connect the HTTPS reverse proxy

Stop conditions

Do not expose when any of these are true