- DNS: Explicit A records per subdomain pointing to each VM's public IP
- TLS: Caddy handles certificate issuance and renewal automatically via ACME (Let's Encrypt / ZeroSSL)
- No wildcard DNS — reduces attack surface and keeps certificate scoping explicit
Log into your DNS provider and create the following A records:
| Type | Name | Value | TTL |
|---|---|---|---|
| A | notes |
<VM1_PUBLIC_IP> |
300 |
| A | links |
<VM1_PUBLIC_IP> |
300 |
| Type | Name | Value | TTL |
|---|---|---|---|
| A | docs |
<VM2_PUBLIC_IP> |
300 |
| A | vault |
<VM2_PUBLIC_IP> |
300 |
Replace
notes,links,docs,vaultwith whatever subdomain names you chose. Replace<VM1_PUBLIC_IP>and<VM2_PUBLIC_IP>with the actual public IPs from the OCI provisioning step.
Set TTL to 300 (5 minutes) initially for faster iteration. Raise it to 3600 once everything is stable.
Before starting Caddy, confirm your DNS records have propagated:
dig notes.example.com +short
# Expected output: your VM1 public IP
dig docs.example.com +short
# Expected output: your VM2 public IPAlso check from a public DNS resolver:
dig @8.8.8.8 notes.example.com +short
dig @1.1.1.1 vault.example.com +shortDo not start Caddy until DNS is propagated. If Caddy attempts the ACME challenge before DNS resolves to the correct IP, Let's Encrypt will fail the challenge and rate-limit your domain.
Caddy uses the ACME HTTP-01 challenge by default:
- Let's Encrypt (or ZeroSSL) sends a request to
http://<your-domain>/.well-known/acme-challenge/<token> - Caddy receives this on port 80 and responds with the challenge token
- ACME server verifies the token and issues the certificate
- Caddy stores the certificate in its data volume and begins serving HTTPS
This is fully automatic. You do not need to:
- Run
certbotmanually - Generate CSRs
- Renew certificates manually (Caddy renews ~30 days before expiry)
Caddy stores certificates in its data directory. This is mapped to a named Docker volume (caddy_data) in both VM stacks.
Critical: Do not delete the caddy_data volume unnecessarily. Doing so forces Caddy to re-issue certificates, which counts against Let's Encrypt's rate limits (5 certificates per registered domain per week).
To backup certificates alongside other data, see 10 — Backups.
Certificate not issued / HTTPS not working:
# Check Caddy logs
docker compose logs caddy
# Look for ACME challenge errors, DNS resolution failures, or port 80 blocksCommon causes:
- DNS not propagated yet → wait and verify with
dig - Port 80 blocked by OCI Security List or NSG → re-check 03 — OCI Infrastructure
- Port 80 blocked by VM-level iptables → see 11 — Troubleshooting
- Caddyfile has a syntax error →
docker compose exec caddy caddy validate --config /etc/caddy/Caddyfile