Olympus Docs
DeployDeploy wizards

Without Daedalus, manual deploy

Deploying Olympus to production by hand

If Daedalus's assumptions don't fit your environment, you have infrastructure-as-code, your VPS provider isn't supported, you want to deploy to Kubernetes or a multi-region setup, this page walks the manual path.

What Daedalus does that you'll do yourself

  • Fork the platform repo
  • Provision the VPS (or K8s cluster, or wherever Olympus runs)
  • Configure DNS
  • Generate and store secrets
  • Edit the prod configs (Kratos, Hydra, Caddyfile)
  • Push secrets to GitHub Actions
  • Trigger the deploy workflow
  • Seed the first admin
  • Register OAuth2 clients
  • Run health checks

The full step-by-step is in First production deploy. This page focuses on the deviations.

Deviation: Kubernetes

Olympus is not Kubernetes-native (see ADR 0001, multi-host is out of scope). But you can run it on K8s as a single pod or set of pods.

A starting Helm-ish layout:

# olympus-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: olympus-athena-ciam
spec:
  replicas: 1
  selector: { matchLabels: { app: athena-ciam } }
  template:
    metadata: { labels: { app: athena-ciam } }
    spec:
      containers:
        - name: athena
          image: ghcr.io/olympusoss/athena@sha256:...
          env:
            - { name: DATABASE_URL, valueFrom: { secretKeyRef: {...} } }
            - { name: ENCRYPTION_KEY, valueFrom: { secretKeyRef: {...} } }
            - { name: SESSION_SIGNING_KEY, valueFrom: { secretKeyRef: {...} } }
            - { name: KRATOS_URL, value: http://ciam-kratos:5001 }
            - { name: HYDRA_URL, value: http://ciam-hydra:5003 }

You'd need similar deployments for each of the ~12 services. The intranet network in Compose becomes a Kubernetes service mesh.

We don't ship Helm charts and aren't planning to, running Olympus on K8s is a one-way door away from the supported configurations.

Deviation: multi-host

Olympus single-host is by design. Multi-host means:

  • Multiple Caddys with externalized cert storage (Redis or S3).
  • Postgres on a separate host (which you'd be doing anyway with managed Postgres).
  • App services replicated.

The Ory binaries are stateless and replicate naturally, so this works in principle. We don't test it.

Deviation: alternative DNS provider

If you're using a DNS provider that Daedalus doesn't directly support (Route 53, Namecheap, etc.), Daedalus's Domain step doesn't manipulate DNS, it just records your choices. Set the A records yourself in your DNS provider's UI.

For DNS-01 ACME (needed if you're behind a proxy), Caddy supports many providers via plugins. See caddyserver.com/docs/caddyfile/options#tls-providers.

Deviation: pre-existing GitHub Actions Secrets store

Daedalus expects to manage GitHub Secrets. If you have an existing setup (e.g. shared org-level secrets, an external vault), skip Daedalus's Secrets step and populate the values yourself.

The platform deploy.yml workflow expects these env vars from GitHub Secrets:

ENCRYPTION_KEY
SESSION_SIGNING_KEY
CIAM_RELOAD_API_KEY, IAM_RELOAD_API_KEY
CIAM_KRATOS_COOKIE_SECRET, CIAM_KRATOS_CIPHER_SECRET
IAM_KRATOS_COOKIE_SECRET, IAM_KRATOS_CIPHER_SECRET
CIAM_HYDRA_SYSTEM_SECRET, IAM_HYDRA_SYSTEM_SECRET
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, SMTP_FROM_ADDRESS
TURNSTILE_SECRET_KEY (if captcha enabled)
DATABASE_URL_CIAM_KRATOS, DATABASE_URL_CIAM_HYDRA, ...
DEPLOY_SSH_KEY (private key for SSH into the VPS)
DEPLOY_HOST (VPS hostname)

See Security, Secrets Management for the full catalog.

Deviation: build images yourself

By default, the deploy workflow pulls images from ghcr.io/olympusoss/*. To build your own:

cd athena
podman build -t ghcr.io/<your-org>/athena:$(git rev-parse HEAD) .
podman push ghcr.io/<your-org>/athena:$(git rev-parse HEAD)

Then update compose.prod.yml to reference your registry path with digest pinning.

The verify-image-pins.yml workflow enforces digest pinning in prod compose, using :latest will fail CI.

Deviation: alternative CI/CD

If you don't use GitHub Actions (you use Drone, Jenkins, Gitea Actions, etc.), translate the steps in deploy.yml to your platform's syntax. The high-level steps:

  1. SSH into the VPS.
  2. SCP the compose.prod.yml + config files.
  3. Write .env.prod with the secrets.
  4. podman compose -f compose.prod.yml --env-file .env.prod up -d.
  5. Wait for podman compose -f compose.prod.yml ps to show all services healthy.

Total deploy time: ~3 minutes.

Verification (same regardless of deploy method)

The post-deploy checklist from First production deploy, health endpoints, registration flow, recovery flow, OAuth2 client, applies regardless of how you got there.

Where next

On this page