Olympus Docs
CookbookDeployment

DNS and TLS setup for Olympus

Domain configuration from zero to running

A clean DNS + TLS setup for new Olympus deployments. From buying domain to working HTTPS.

Domain setup

Buy a domain (Cloudflare Registrar, Namecheap, etc.). Wait 24h for it to be active.

DNS records

# Primary
A     ciam.your-domain.com    → host-IP
A     iam.your-domain.com     → host-IP

# Optional: apex
A     your-domain.com         → host-IP (or your marketing site)
A     www.your-domain.com     → host-IP

# Verification (optional)
TXT   your-domain.com         → "olympus-verification=..."

For wildcard:

A     *.your-domain.com       → host-IP

Wildcard is risky, covers all subdomains. Be specific.

TLS via Caddy

Caddy auto-fetches Let's Encrypt certs. In Caddyfile:

ciam.your-domain.com {
  tls you@your-domain.com  # for Let's Encrypt notifications
  reverse_proxy hera:3000
}

iam.your-domain.com {
  tls you@your-domain.com
  reverse_proxy athena:3001
}

First request: Caddy fetches cert (~30s). Auto-renews 30 days before expiry.

Wildcard cert

For many subdomains, wildcard via DNS challenge:

*.your-domain.com {
  tls {
    dns cloudflare api-token-here
  }
  reverse_proxy hera:3000
}

Caddy talks to Cloudflare's API → adds TXT for challenge → Let's Encrypt issues wildcard cert.

Required: Caddy with cloudflare DNS plugin (custom build).

Custom domains (white-label)

For customers who bring their domain:

auth.acme.com {
  reverse_proxy hera:3000
}

auth.bigcorp.com {
  reverse_proxy hera:3000
}

Each custom domain → vhost.

For on-demand (many customers):

{
  on_demand_tls {
    ask http://your-backend/check-domain
    rate_limit_burst 5
    rate_limit_window 5m
  }
}

:443 {
  tls { on_demand }
  reverse_proxy hera:3000
}

Backend confirms domain is registered:

app.get("/check-domain", async (req, res) => {
  const domain = req.query.domain;
  const exists = await db`SELECT 1 FROM customer_domains WHERE domain = ${domain}`.first();
  if (exists) return res.status(200).send("OK");
  return res.status(403).send("Domain not registered");
});

DNS propagation

After adding records:

  • TTL determines propagation time.
  • Set short TTL during setup (5 min).
  • Increase after stable (1 day).
A ciam.your-domain.com → host-IP, TTL: 300

Check propagation:

dig +short ciam.your-domain.com
nslookup ciam.your-domain.com 8.8.8.8

Cloudflare in front

If you proxy through Cloudflare:

  1. Add A records with proxy enabled (orange cloud).
  2. Cloudflare's edge IP returned, not yours.
  3. SSL/TLS mode: Full (strict), Cloudflare ↔ origin TLS.
  4. Origin cert: Caddy auto-provisions Let's Encrypt OR Cloudflare's "Origin CA cert" (free, 15-year cert).

Caddy needs to know real client IP:

ciam.your-domain.com {
  bind 0.0.0.0
  servers {
    trusted_proxies cloudflare
  }
  reverse_proxy hera:3000
}

Otherwise rate-limiting sees Cloudflare IPs (all the same), not real clients.

CAA records

CAA your-domain.com → 0 issue "letsencrypt.org"

Tells CAs: only Let's Encrypt is authorized to issue for this domain. Defense against accidental cert misissuance.

Cert pinning (rare)

For ultra-high security, HPKP (HTTP Public Key Pinning) was deprecated. Don't pin certificates, risky if you lose access to keys.

Instead: Expect-CT header (also deprecated). Modern approach: Certificate Transparency logs (automatic, Let's Encrypt is in CT logs).

Renewal

Caddy auto-renews 30 days before expiry. Logs:

podman logs ciam-caddy | grep certmagic

If renewals fail:

  • DNS misconfigured.
  • Rate limited by Let's Encrypt.
  • Disk full.

Renew manually:

podman exec ciam-caddy caddy reload

Triggers cert check.

Backup certs

Caddy stores certs in /data/caddy. Backup:

tar czf caddy-data-backup.tgz /var/lib/containers/storage/volumes/caddy_data

If you lose them, Caddy re-fetches (rate-limited).

HSTS

header {
  Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
}

Tells browsers: ALWAYS HTTPS, even on subdomains, for 1 year.

For HSTS preload list:

  1. Have HSTS header for 1 year.
  2. Submit at hstspreload.org.
  3. Browsers ship the preload, even first visit is HTTPS-only.

SNI

If multiple TLS certs on one host (multiple domains), Caddy uses SNI (Server Name Indication) to pick. All modern browsers support.

Older clients (< Android 4) don't support SNI, but probably don't matter to you.

Email DNS

For email auth flows:

  • MX records → mail server.
  • SPF for sending.
  • DKIM for signing.
  • DMARC for policy.

Different topic, see email deliverability.

Common mistakes

Forgot apex

If you only set ciam.X but not X itself, https://your-domain.com is broken. Either set it OR redirect at registrar:

your-domain.com → 301 → https://ciam.your-domain.com

Wildcard with proxied Cloudflare

Cloudflare proxies require listed records, not pure wildcard. Add each subdomain.

Cert not auto-renewing

Check Caddy logs. Common: disk full, permissions, DNS broken.

df -h
ls -la /var/lib/containers/storage/volumes/caddy_data

On this page