Skip to content

Deploying

CLI Deploy

bash
norn deploy <app> [ref]
  • ref defaults to HEAD (latest commit on the configured branch)
  • The CLI connects to the WebSocket and shows real-time progress for each pipeline step
  • On completion, prints the saga ID for later inspection

Run a preflight first when you want the build/test confidence without runtime mutation:

bash
norn preflight <app> [ref]
# alias
norn check <app> [ref]

Preflight runs validate, clone, inspect, build, and test. It intentionally skips snapshot, migrate, submit, healthy, and forge.

Example:

bash
$ norn deploy signal-sideband abc1234
 clone
 clone (3.2s)
 build
 build (37.1s)
 test
 test (5.8s)
 snapshot
 snapshot (1.2s)
 migrate
 migrate (0.4s)
 submit
 submit (0.8s)
 healthy
 healthy (32.0s)
 forge
 forge (1.1s)
 cleanup
 cleanup (0.2s)

deployed signal-sideband ghcr.io/antiartificial/signal-sideband:abc1234
saga: f47ac10b-58cc-4372-a567-0e02b2c3d479

UI Deploy

Click the Deploy button on an app card in the dashboard. The deploy panel shows a live step-by-step progress view with the same information as the CLI.

API Deploy

bash
curl -X POST http://localhost:8800/api/apps/myapp/deploy \
  -H "Content-Type: application/json" \
  -d '{"ref": "abc1234"}'

Pipeline Steps

See Deploy Pipeline for detailed documentation of each step.

StepDescription
validateCheck infraspec shape and reachability assumptions (preflight only)
cloneCheckout repo at ref
inspectCheck Dockerfile, declared encrypted secrets, and known source footguns (preflight only)
buildBuild and push Docker image
testRun test command
snapshotpg_dump database
migrateRun database migrations
submitTranslate and submit Nomad jobs
healthyWait for allocations to be healthy
forgeUpdate cloudflared ingress
cleanupRemove temp files

Port Conflict Detection

During the submit step, norn checks for port conflicts with other running Nomad jobs. If the app's requested static port is already in use by a different job, norn logs a warning with a suggested alternative port:

port.conflict: port 8090 is used by mail-indexer — suggest 8092

Apps with endpoints: in their infraspec use static ports (Nomad ReservedPorts), meaning the container binds to the exact port specified. If another service already occupies that port on IPv4, Nomad may fall back to IPv6-only binding, which causes cloudflared routing to hit the wrong service.

Checking used ports

The Nomad client exposes UsedPorts() and SuggestPort(base):

  • UsedPorts() — returns all static ports currently claimed by running jobs
  • SuggestPort(base) — returns the lowest unused port starting from base

To avoid conflicts, pick a port not in use. Currently allocated ports can be queried through the Nomad API or by running norn status and inspecting allocations.

Auto-Deploy

Enable auto-deploy by setting autoDeploy: true in the infraspec's repo config and configuring a GitHub webhook.

GitHub Webhook Setup

  1. In your GitHub repo, go to Settings → Webhooks → Add webhook
  2. Set the payload URL to https://norn.example.com/api/webhooks/github
  3. Set the content type to application/json
  4. Set the secret to match NORN_WEBHOOK_SECRET
  5. Select "Just the push event"

When a push event is received for an app with autoDeploy: true, Norn automatically triggers a deploy with the pushed commit SHA.

Auto-Rollback

App deploys auto-rollback by default when the healthy step fails and Norn can find a previous successful deployment.

yaml
deployPolicy:
  autoRollback: true

Set deployPolicy.autoRollback: false only when a failed health gate should stop for manual review. When auto-rollback runs, Norn queues an app rollback through the durable operation worker, emits a deploy.auto_rollback Beacon event, and keeps the rollback steps visible in deployment_steps.

Canary Deploys

Declare canary behavior on a service process:

yaml
processes:
  web:
    port: 8080
    health:
      path: /health
    canary:
      count: 1
      evaluateAfter: 2m

During deploy, Norn submits the Nomad job with canary allocations, waits for the normal health gate, then evaluates canary health after the configured window. Healthy canaries are promoted automatically by the pipeline; operators can also inspect or promote manually:

bash
norn canary myapp
norn promote myapp

The dashboard shows canary status on the app card when a deployment is active.

Deploy Groups

Deploy groups roll out multiple apps in a declared order. Place group files under deploy-groups/*.yaml:

yaml
name: field-harbor-stack
apps:
  - app: contextdb
    waitReady: true
  - app: field-harbor
    waitReady: true
  - app: field-harbor-digest

Run a group from the CLI:

bash
norn deploy-groups
norn deploy-group field-harbor-stack HEAD

Each app deploy still creates its own saga and operation row. waitReady gates keep later apps from starting until the earlier app is healthy.

Rollback

bash
norn rollback <app>

Finds the most recent successful deployment and re-deploys its image tag. This skips the clone/build/test steps and goes straight to submit with the previous image.

Upgrading Norn Itself

When upgrading the Norn control plane, restart only the Norn API LaunchAgent and leave Nomad, Consul, Postgres, and hosted apps running. See Upgrading Norn for the safe local upgrade and rollback runbook.