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
Arecord 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.comto 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.