Skip to main content

Deployment

How to take a Titan app from pnpm dev to production. Four paths covered in detail, plus the cross-cutting concerns.

Choose your path

WhereUse
One box, you own itDocker Compose — daemon + apps + infra in one stack
Container orchestratorKubernetes — daemon as a stateful pod, apps as deployments
Platform-as-a-ServiceFly.io / Railway / Render — opinionated, fast to ship
Bare-metal / VMsystemd — daemon as a system service

For dev / staging / prod parity, run the same omnitron.config.ts across environments. Only secrets + scale parameters change per deploy target.

Cross-cutting concerns

What you ship

A production deployment carries:

  1. Compiled app codedist/ for each app.
  2. omnitron.config.ts — the ecosystem config.
  3. Per-app config/default.json — declarative infra.
  4. Static webapp bundle (optional) — apps/omnitron/webapp/dist/.
  5. Migrationsmigrations/*.sql.

That's it. No generated client code, no schema sync artefacts, no shipped node_modules.

What you provide at the target

  • Node.js 22+.
  • PostgreSQL (managed or self-hosted) — connection string in env.
  • Redis (managed or self-hosted) — connection string in env.
  • Object storage (S3 / MinIO) — credentials in env.
  • Secrets manager (optional) — Omnitron's encrypted store works standalone; for prod prefer a real secret manager.

Environment variables — the canonical set

VariablePurpose
NODE_ENV'production'
HOMEDaemon state lives under $HOME/.omnitron/
DATABASE_URLPostgres connection string
REDIS_URLRedis connection string
JWT_SECRETSigning key — long random; from secret manager
OMNITRON_SOCKETOverride the daemon Unix socket path (optional)
OMNITRON_NO_INFRA1 to skip Docker infra provisioning on boot
<APP>_*Per-app overrides (matches the app's config.envPrefix)

The daemon resolves its home directory from $HOME ($HOME/.omnitron/) — there is no OMNITRON_HOME override. Set HOME for the daemon's user to relocate state.

Per-environment values flow in through each app's config.sources / config.envPrefix (Titan's config module) — favour env over config files for per-environment values.

Logs and metrics

  • Logs stream to stdout (pino JSON) — ingest with your log aggregator's stdin-tail or sidecar.
  • Metrics scrape the daemon's Prometheus endpoint at /metrics on its dedicated metrics port (httpPort + 3, default 9803, bound to loopback unless you opt in via host + bearer token), or push to a backend via titan-telemetry-relay.
  • Health probes — the daemon's metrics port also serves /healthz (always public, returns 200 ok). Per-app health is exposed over Netron RPC by titan-health (the live() / ready() methods) — omnitron health surfaces it. There are no /healthz / /readyz HTTP routes on the app transport ports.

Secrets

Three options, in order of preference:

  1. Cloud secret manager (AWS Secrets Manager, GCP Secret Manager, Vault) — inject as env at pod / container boot.
  2. Encrypted file (~/.omnitron/secrets.enc) — works without external dependencies; rotate the passphrase via env.
  3. Plain env vars — fine for non-sensitive config, not for credentials.

Never commit secrets to omnitron.config.ts or config/default.json.

Migrations

# In the deploy script, before starting the daemon:
omnitron infra migrate
# or for one app:
omnitron infra migrate api

Migrations run inside a Postgres advisory lock — concurrent deploys are safe; only one will run, others wait.

Backups

# Manual (defaults to the 'omnitron' database; pass a name for others):
omnitron backup create
omnitron backup create main

# List / restore:
omnitron backup list
omnitron backup restore <id>

For scheduled backups use omnitron backup schedule <target> <cron|hourly|daily|weekly|ms>; schedules persist across daemon restarts, and omnitron backup schedules lists them. Host cron driving omnitron backup create is the alternative when the backup must run whether or not the daemon is up — see the bare-metal guide.

Zero-downtime deploys

The reload command cycles workers one-by-one:

omnitron reload api

In module-worker mode, the worker pool maintains capacity throughout the reload. In classic mode, a new bootstrap is forked side-by-side; the old one drains.

For blue/green or canary, omnitron deploy app <app> takes --strategy <rolling|all-at-once|blue-green|canary> (default all-at-once).

Multi-region

StrategyPattern
One cluster per region (recommended)Each region elects its own leader; cross-region calls via ingress
Single global clusterAll daemons peer; sensitive to partition
Fleet without clusterEach daemon independent; addressed by alias

See Cluster + Fleet / Multi-region patterns.

Observability checklist

  • Logs → SIEM / log aggregator.
  • Metrics → Prometheus / managed metrics backend.
  • Traces → OTel collector (if titan-tracing configured).
  • Health probes → load balancer.
  • Uptime monitoring → external pinger of /healthz.
  • Error reporting → Sentry via netron-browser middleware.
  • Alerts → OmnitronAlerts with delivery webhooks.

Security checklist (deploy time)

  • TLS terminated at the gateway / load balancer (not the app).
  • Strong JWT_SECRET, rotated quarterly.
  • Omnitron TCP port (9700) firewalled to internal only.
  • Database password from secret manager, never env literal.
  • CORS allowlist limited to known origins.
  • CSP header on webapp.
  • Rate limits configured per public endpoint.
  • Audit logs going to append-only sink.

See Auth & RBAC and the per-module Security checklist.

Rollback

# Roll back an app on a target server (alias or tag):
omnitron rollback api --target web-1

omnitron rollback <app> --target <server> requires a target — it operates against a registered remote daemon. Build artefacts are versioned tarballs under <projectRoot>/.omnitron/artifacts/ (<app>-<version>.tar.gz); prune old ones with the ArtifactBuilder retention helper (keeps the latest N per app).

⚠️ NEEDS REWRITE — there is no deployment: { retain, versionTag } block in omnitron.config.ts, and rollback has no --version flag. Artefact retention is handled by the builder, not declarative config.

Read the per-target guides

See also