How to Set Up Wildcard Subdomains with Caddy and DigitalOcean

5 min read.

If you want one server to handle unlimited subdomains, setting up a wildcard subdomain with Caddy and DigitalOcean is one of the cleanest ways to do it.

The key detail: wildcard certificates (*.example.com) require a DNS challenge, so Caddy must be built with a DNS provider module for DigitalOcean.

Why wildcard HTTPS needs DNS challenge

HTTP challenge works great for normal domains, but wildcard certs are different. Certificate authorities only issue wildcard certs through DNS validation (ACME DNS-01 challenge).

That means Caddy needs permission to create temporary TXT records in your DNS zone. For DigitalOcean DNS, that happens through an API token.

Prerequisites

  • A domain managed in DigitalOcean DNS
  • A Droplet (or any server) with a public IP
  • A record for * pointing to your server IP
  • Caddy 2 running on the server
  • A DigitalOcean personal access token with DNS write access

If you also want the apex domain (example.com) to work, add an A record for @ too.

1. Create DNS records in DigitalOcean

In your domain DNS settings, add:

Type: A
Host: *
Value: <your-server-ip>
TTL: 3600

Optional for apex domain:

Type: A
Host: @
Value: <your-server-ip>
TTL: 3600

The wildcard record is what lets app.example.com, api.example.com, and anything else resolve to the same server.

2. Build Caddy with the DigitalOcean DNS module

Stock Caddy binary does not include every DNS provider module by default. You need the DigitalOcean DNS module.

Install xcaddy and build Caddy:

go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest

xcaddy build \
  --with github.com/caddy-dns/digitalocean

That gives you a local caddy binary with DNS support for DigitalOcean.

Move it into place (example on Linux):

sudo mv ./caddy /usr/local/bin/caddy
sudo setcap cap_net_bind_service=+ep /usr/local/bin/caddy

3. Configure Caddyfile for wildcard subdomains

Set your token as an environment variable:

export DO_AUTH_TOKEN="dop_v1_xxxxxxxxxxxxxxxxx"

Now create your Caddyfile:

*.example.com {
  tls {
    dns digitalocean {env.DO_AUTH_TOKEN}
  }

  @api host api.example.com
  handle @api {
    reverse_proxy 127.0.0.1:4000
  }

  handle {
    reverse_proxy 127.0.0.1:3000
  }
}

What this does:

  • Requests a wildcard cert using DNS challenge against DigitalOcean
  • Terminates HTTPS automatically
  • Routes api.example.com to one upstream and all other subdomains to another

If you also need apex domain, add another site block:

example.com {
  tls {
    dns digitalocean {env.DO_AUTH_TOKEN}
  }

  reverse_proxy 127.0.0.1:3000
}

Wildcard does not cover apex automatically.

4. Validate and run Caddy

Validate config:

caddy validate --config /etc/caddy/Caddyfile

Run or reload:

sudo systemctl reload caddy
# or if not using systemd:
caddy run --config /etc/caddy/Caddyfile

Test DNS and HTTPS:

dig app.example.com +short
curl -I https://app.example.com

You should see your server IP from dig and an HTTPS response from curl.

Common mistakes

Using Caddy without DNS module

If Caddy logs show unknown DNS provider/module errors, your binary was built without github.com/caddy-dns/digitalocean.

Token not available in runtime

If DO_AUTH_TOKEN is only exported in your current shell, systemd services will not see it. Put it in your service environment file.

Wildcard DNS record missing

No * record means subdomains do not resolve, so certificate issuance and requests fail before your app is even reached.

Expecting wildcard to include apex

*.example.com covers foo.example.com, not example.com.

Conclusion

To set up a wildcard subdomain with Caddy and DigitalOcean, point * DNS to your server, use a Caddy build that includes the DigitalOcean DNS module, and configure TLS with dns digitalocean. Once that is in place, Caddy can issue and renew wildcard HTTPS certificates automatically.

Latest Posts