Prompt file imported from mobasak/fabrik (
.windsurf/workflows/deploy.md). Copyright stays with the author.
Deploy Workflow
Deploy a Fabrik service to production VPS via the fabrik CLI (SSH + Docker Compose; Coolify was decommissioned 2026-05-30). Traefik + all shape-gated registrars (Authelia, Gatus, Backrest, GlitchTip, Grafana, MeiliSearch, Postgres) are provisioned automatically per shape.* flags in the spec.
Canonical reference: docs/DEPLOYMENT_ARCHITECTURE.md — read first if anything in this file is unclear.
Prerequisites
- Project scaffolded under
/opt/<name>/viafabrik scaffold <name> --type <template> - Spec file
specs/services/<name>.yamlexists (check vials /opt/fabrik/specs/services/) -
shape.*flags in spec match what the service actually needs (admin dashboard? DB? search? persistent data?) - Domain in spec resolves under
*.vps1.ocoron.comOR pre-provisioned withfabrik domain provision - All secrets present in
/opt/fabrik/.env(Cloudflare, Grafana SA, GlitchTip, Backrest, etc.) - Lean gate passes:
python scripts/final_gate.py --lean
Steps
- Pre-flight — Confirm scaffold readiness:
fabrik validate-deploy /opt/<name>
python scripts/final_gate.py --lean
- Dry-run deploy — Shows every mutation without executing:
fabrik apply /opt/fabrik/specs/services/<name>.yaml --dry-run
// turbo 3. Deploy — Full pipeline: validator → secrets → DNS → template → SSH deploy → provisioners → verifier:
fabrik deploy --project /opt/<name>
# Or equivalently: fabrik apply /opt/fabrik/specs/services/<name>.yaml
Runs internally (see docs/DEPLOYMENT_ARCHITECTURE.md §9.2):
SpecValidator→deploy_validator→SecretsManager→DNSClientTemplateRenderer→ComposeLinter→SSHDeployer(deployer_ssh.py: render compose → scp →docker compose up -dover SSH on thefabriknetwork)InfrastructureProvisioner.provision(ctx)— shape-gated: postgres · gatus · backrest · glitchtip+DSN · grafana · authelia+bypass · meilisearchDeploymentVerifier.verify()— HTTP 200, DNS, SSL, SENTRY_DSN viadocker inspect
Expected wall time: ~60s for a scratch-image service, +30s–3min for a Dockerfile build.
// turbo 4. Verify health — Confirm deployment succeeded:
curl -f https://<name>.vps1.ocoron.com/health # (or /) for scratch images
ssh vps 'sudo docker ps --format "{{.Names}}\t{{.Status}}" | grep <name>'
- Re-deploy idempotency check — Running the same deploy again must be a no-op with all registrars reporting
status: exists:
fabrik deploy --project /opt/<name>
Rollback
Any failed step triggers automatic reverse-order cleanup via RollbackManager (see docs/DEPLOYMENT_ARCHITECTURE.md §9.8). DB drops are logged for operator action, never auto-executed.
Verification
-
fabrik deployexits 0 - Container shows
Upindocker ps - Traefik router for the domain is
enabled - HTTPS endpoint reachable (302 to Authelia OR 200 from app)
- All shape-gated registrars green (see
docs/DEPLOYMENT_ARCHITECTURE.md§9.6 for per-registrar verification commands) - Idempotent re-deploy succeeds
Related workflows
/bug-fix— regression fix loop/review— code review before deploydocs/DEPLOYMENT_ARCHITECTURE.md§9.6 — maximal-shape E2E validation (run after any change to registrar driver, orchestrator, or compose template)