Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Deploy with Docker

Orion ships as a container image with everything compiled in: all three database backends, Kafka, OTLP export, TLS. There is nothing to install alongside it. This guide is what to change before the container holds anything you care about.

Before you start

You need Docker Engine or Docker Desktop, and for the HA topology, Docker Compose. docker run -p 8080:8080 ghcr.io/goplasmatic/orion:latest gives you a working instance; the rest of this page hardens it.

Give SQLite a volume

Warning

The default SQLite database lives inside the container. Without a volume, every workflow, channel and connector you create is lost when the container is replaced, which includes every image upgrade.

Mount a volume and point the storage URL at it:

docker run -p 8080:8080 \
  -v orion-data:/app/data \
  -e ORION_STORAGE__URL=sqlite:/app/data/orion.db \
  ghcr.io/goplasmatic/orion:latest

Both halves are needed. The volume gives the file somewhere durable to live, and ORION_STORAGE__URL puts it there. As a compose file:

services:
  orion:
    image: ghcr.io/goplasmatic/orion:1.8.1
    ports: ["8080:8080"]
    environment:
      ORION_STORAGE__URL: sqlite:/app/data/orion.db
      ORION_LOGGING__FORMAT: json
    volumes:
      - orion-data:/app/data
    stop_grace_period: 45s

volumes:
  orion-data:

Pin the tag. latest makes an upgrade something that happens to you rather than something you decide.

Configure it

Every setting is an ORION_SECTION__KEY environment variable, which is what makes the image configurable without a config file:

environment:
  ORION_STORAGE__URL: "postgres://user:pass@db:5432/orion"
  ORION_ADMIN_AUTH__ENABLED: "true"
  ORION_ADMIN_AUTH__API_KEYS: "${ORION_ADMIN_API_KEYS:?set this}"
  ORION_METRICS__ENABLED: "true"
  ORION_ENVIRONMENT: "production"

A name that is not a real setting is refused at startup with the nearest match, rather than silently ignored. A typo costs you a boot rather than a week. Mount a TOML file and pass -c if you prefer files; the environment still overrides it.

Give the container time to drain

Orion’s shutdown sequence takes shutdown_drain_secs + shutdown_force_timeout_secs to complete. If Docker’s grace period is shorter, the process is killed mid-drain and in-flight requests die with it:

stop_grace_period: 45s      # > ORION_SERVER__SHUTDOWN_DRAIN_SECS + FORCE_TIMEOUT_SECS

The sequence itself is in Shut down without dropping requests.

Verify

The image needs no extra configuration for health checks:

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:8080/healthz"]
  interval: 5s
  timeout: 3s
  start_period: 10s
  retries: 3

Use /healthz for “is the process alive” and /readyz for “should it get traffic”. A load balancer wants the second one. Then confirm the estate built:

curl -s http://localhost:8080/health | jq '{status, workflows_loaded}'

The reference HA topology

docker-compose.ha.yml in the repository root is the production shape as a compose file. It runs nginx, two Orion nodes in cluster mode, shared PostgreSQL and Redis, and a one-shot migrate service that completes before either node boots:

export ORION_ADMIN_API_KEYS="$(openssl rand -hex 32)"
docker compose -f docker-compose.ha.yml up -d --wait
curl -s http://localhost:8080/health

It boots as ORION_ENVIRONMENT=production, so admin auth is enforced and ORION_ADMIN_API_KEYS is required. The stack refuses to start without it, which is the point. Set ORION_CORS_ALLOWED_ORIGINS if a browser dashboard needs it.

What it demonstrates, and what to copy into your own topology:

  • auto_migrate = false on the replicas, with migrations applied once by a separate service before either node starts.
  • Cluster mode on, so config changes made through either node reach both, and dedup, response caches and rate limits are shared.
  • stop_grace_period above the drain budget, so SIGTERM runs the graceful sequence instead of being cut short.
  • A pinned image tag, overridable with ORION_VERSION.

deploy/ha/rolling-drill.sh drives traffic through the load balancer while one node is SIGTERMed, and asserts every response was a 2xx. Run it to prove the drain settings on your hardware, not only on the reference one.

Upgrade a compose deployment

  1. Back up the database: a SQLite volume snapshot, or your database’s own tooling. See Back up and restore.
  2. Read the version’s upgrade guide and run orion-server preflight with the new image against the old database. See Upgrade an instance.
  3. Bump the pinned tag and, in a cluster, let the migrate service apply migrations before the replicas restart.
  4. Restart one node at a time so the load balancer always has a node to send to. With a single node there is a gap, which is the argument for the second one.

Run without a container

Orion is also a plain binary, and nothing about it requires Docker. Install it from a release or Homebrew, put the config somewhere readable, and run orion-server -c /etc/orion/config.toml under whatever supervises processes on that host.

Orion ships container images and a Helm chart, not a systemd unit. If you run it under systemd, you write the unit file. Give it the same two properties this page is about: a durable path for the database, and a stop timeout longer than the drain budget.

Next steps

Last verified 14 September 2026