Skip to content

Command Palette

Search for a command to run...

Automatic HTTPS with Caddy

Updated Jul 2026

Put a reverse proxy in front of your self-hosted apps and get automatic, auto-renewing Let's Encrypt certificates with a two-line Caddyfile.

Before you start
  • A VPS with a public IP — any $5 box works (1 vCPU / 1 GB RAM is plenty for Caddy).
  • A domain name with an A record pointed at that IP, already propagated.
  • Docker and Docker Compose installed on the server.
  • Ports `80` and `443` open to the internet — Caddy needs both for the ACME challenge.

Most self-hosted apps serve plain HTTP on some port. Caddy sits in front, terminates TLS, and gets a real Let's Encrypt certificate automatically — no certbot cron jobs, no renewal scripts. It is the simplest way to put your apps on a proper https:// domain.

Point your domain at the box first

Caddy proves domain ownership over HTTP/TLS, so DNS has to resolve before it can issue a certificate. Create an A record for your subdomain (e.g. app.example.com) pointing at the server's public IP and let it propagate.

The whole config is the Caddyfile

Caddy's headline feature: name a site and a backend, and it handles certificates for you. Assuming an app listening on localhost:3001:

# /etc/caddy/Caddyfile
app.example.com {
  reverse_proxy localhost:3001
}

That is it. On first request Caddy obtains the certificate, serves HTTPS, and quietly renews it before expiry.

Run Caddy alongside your app in Compose

The clean pattern is to put Caddy in the same Docker network as the app it proxies:

services:
  app:
    image: louislam/uptime-kuma:1
    restart: unless-stopped

  caddy:
    image: caddy:2
    ports: ["80:80", "443:443"]
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
    restart: unless-stopped

volumes:
  caddy_data:

With app and caddy on the same Compose network, the Caddyfile target becomes the service name:

app.example.com {
  reverse_proxy app:3001
}

The caddy_data volume is important — it stores the issued certificates and account keys, so a container restart never re-triggers issuance (and never risks hitting Let's Encrypt rate limits).

Add more apps in seconds

Each new app is one more block:

git.example.com   { reverse_proxy gitea:3000 }
notes.example.com { reverse_proxy outline:3000 }

Reload with docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile and the new hostnames get certificates on first hit.

Where to go next

Need the Docker base layer first? See Docker & Compose on Ubuntu 24.04. Then pick an app from the self-hosting guides and drop it behind Caddy.

Next steps