Skip to content

Deploy in production

Run Stegflow as a durable, authenticated service behind a reverse proxy that terminates TLS.

Prerequisites

  • A Docker host with Compose. Podman works too: the same file runs with podman compose.
  • A domain name pointing at that host, and a reverse proxy able to obtain a certificate for it (Caddy, Traefik, nginx, or an existing ingress).
  • An OpenID Connect provider, so sign-in is backed by real accounts (see Configure OpenID Connect).
  • The email address of at least one global administrator.

Beta releases

Database migrations are not guaranteed between beta images, so an upgrade may require wiping the database. Treat beta deployments as disposable, and see Getting started for details.

Steps

1. Put a reverse proxy in front

Stegflow serves plain HTTP on port 8080 and does not terminate TLS itself. In production, a reverse proxy sits in front of it and handles certificates, HTTPS redirection and, if you want, rate limiting.

Configure the proxy to pass the standard forwarding headers (X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host). Stegflow honours them to build absolute URLs, which the OIDC redirect_uri depends on: without them, sign-in redirects back to http:// and the internal host name.

The compose file below publishes Stegflow on 127.0.0.1:8080 only, so nothing but the proxy on the same host can reach it. If instead you expose the port more widely, pin the trusted proxy with STEGFLOW_ReverseProxy__KnownProxies__0 or STEGFLOW_ReverseProxy__KnownNetworks__0 so forwarded headers cannot be spoofed (see Configuration).

2. Write the compose file

This stack runs the engine with PostgreSQL for data and Valkey for the queues and the Data Protection key ring. Both are required for a durable deployment (see Configure persistent storage).

compose.yml
services:
  stegflow:
    image: stegflow/stegflow:beta
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:8080"
    environment:
      STEGFLOW_Engine__AdminEmails__0: "${ADMIN_EMAIL}"
      STEGFLOW_Engine__Database__Provider: PostgreSql
      STEGFLOW_Engine__Database__ConnectionString: "Host=db;Database=stegflow;Username=stegflow;Password=${DB_PASSWORD}"
      STEGFLOW_Engine__Valkey__ConnectionString: "valkey:6379"
      STEGFLOW_OIDC__Authority: "${OIDC_AUTHORITY}"
      STEGFLOW_OIDC__ClientId: "${OIDC_CLIENT_ID}"
      STEGFLOW_OIDC__ClientSecret: "${OIDC_CLIENT_SECRET}"
    depends_on:
      db:
        condition: service_healthy
      valkey:
        condition: service_healthy

  db:
    image: postgres:18
    restart: unless-stopped
    environment:
      POSTGRES_DB: stegflow
      POSTGRES_USER: stegflow
      POSTGRES_PASSWORD: "${DB_PASSWORD}"
    volumes:
      - db-data:/var/lib/postgresql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U stegflow"]
      interval: 10s
      timeout: 5s
      retries: 5

  valkey:
    image: valkey/valkey:8
    restart: unless-stopped
    command: ["valkey-server", "--appendonly", "yes"]
    volumes:
      - valkey-data:/data
    healthcheck:
      test: ["CMD", "valkey-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  db-data:
  valkey-data:

Keep the secrets out of the file itself. Put them in a .env file next to it, readable only by the deploying user:

.env
ADMIN_EMAIL=admin@example.com
DB_PASSWORD=<A_LONG_RANDOM_PASSWORD>
OIDC_AUTHORITY=https://id.example.com/realms/stegflow
OIDC_CLIENT_ID=<CLIENT_ID>
OIDC_CLIENT_SECRET=<CLIENT_SECRET>

3. Start the stack

docker compose up -d
docker compose ps

The engine exposes /health, which reports the database and Valkey probes. The image already declares a healthcheck against it, so docker compose ps shows the service as healthy only once those dependencies answer.

4. Point the proxy at Stegflow

Forward your domain to 127.0.0.1:8080. With Caddy, for example, the whole configuration is:

Caddyfile
stegflow.example.com {
    reverse_proxy 127.0.0.1:8080
}

Caddy obtains the certificate, redirects HTTP to HTTPS and sets the forwarding headers on its own. Any other proxy works as long as it does the same.

5. Sign in and check the setup

Open https://stegflow.example.com. You are redirected to your identity provider, and the account matching STEGFLOW_Engine__AdminEmails__0 signs in as global administrator. From there, create a tenant and start building.

Verify authentication is really on

If Stegflow starts without an OIDC authority, it falls back to single-identity mode, where anyone reaching it is signed in as an administrator. Confirm you are prompted by your provider, and check the logs for the startup warning about the default administrator.

Result

Stegflow runs behind HTTPS with real accounts, its data in PostgreSQL and its queues in Valkey, restarting with the host. Next, decide who may create tenants and tune retention in Configuration, and set up monitoring with Configure telemetry.