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:
- SSH into the VPS.
- SCP the
compose.prod.yml+ config files. - Write
.env.prodwith the secrets. podman compose -f compose.prod.yml --env-file .env.prod up -d.- Wait for
podman compose -f compose.prod.yml psto 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
- Deploy, First production deploy, the canonical step-by-step.
- Operate, Incident Response, when something breaks.