Back to articles
Technology Insight

Automating Free Wildcard SSL for Thousands of Subdomains: A Production Guide Using Caddy Server on a VPS

May 28, 2026

Introduction to Scalable SSL Management

In modern web architecture, managing secure connections at scale is a critical requirement. Businesses hosting Multi-Tenant Software-as-a-Service (SaaS) platforms, dynamic user profile subdomains, or massive landing page networks frequently face the challenge of provisioning SSL/TLS certificates for thousands of subdomains. Traditional web servers like Nginx or Apache, while powerful, often require complex external scripts (such as Certbot), manual configuration adjustments, and frequent service reloads to handle new domains. This operational overhead introduces risk and scaling bottlenecks.

Caddy Server emerges as a game-changing solution to this problem. Written in Go, Caddy is a modern, memory-safe web server that features Automatic HTTPS by default. By utilizing a dynamic configuration API and native Let's Encrypt or ZeroSSL integration, Caddy can automatically request, install, and renew SSL certificates with zero manual intervention. This guide provides an enterprise-grade blueprint for deploying Caddy Server on a Virtual Private Server (VPS) to manage dynamic, free Wildcard SSL certificates for thousands of subdomains seamlessly.

The Architecture: Why Wildcard Certificates via DNS-01?

When dealing with thousands of subdomains (e.g., *.tenant.yourdomain.com), requesting an individual HTTP-01 challenge certificate for every single subdomain is inefficient and will quickly trigger Let's Encrypt's strict rate limits. The optimal approach is leveraging a Wildcard SSL Certificate via the DNS-01 challenge.

Key Advantage: A single wildcard certificate (e.g., covering *.yourdomain.com) protects an infinite number of subdomains at the first level. Because the verification happens at the DNS level rather than routing traffic to a specific server instance, it eliminates the need to expose HTTP ports during validation and facilitates centralized certificate management.

To automate this, Caddy interacts directly with your DNS provider's API (such as Cloudflare, DigitalOcean, or AWS Route53) to temporarily create a TXT record (_acme-challenge), prove ownership, and fetch the wildcard certificate.

Prerequisites and System Preparation

Before initiating the installation, ensure your environment meets the following baseline requirements for a production-ready VPS deployment:

  • Operating System: Ubuntu 22.04 LTS or newer (or any modern Linux distribution).
  • Hardware Specification: Minimum 2 vCPUs and 4GB RAM to handle concurrent SSL handshakes comfortably at scale.
  • Network Config: Ports 80 (HTTP) and 443 (HTTPS) open on your firewall (UFW/iptables).
  • DNS Provider API Token: Access credentials to your DNS zone with permissions to read/write TXT records (Cloudflare is utilized in this guide's examples).

Step 1: Installing Caddy with Custom DNS Modules

The standard distribution of Caddy does not include specific DNS provider plugins to keep the binary lightweight. To utilize the DNS-01 challenge, we must compile or download a custom Caddy binary containing the appropriate DNS module. This is safely managed via xcaddy (Caddy's official builder) or by downloading it directly from the Caddy download page.

Execute the following commands to install xcaddy and build Caddy with the Cloudflare DNS module:

# Install Go and build essentials
sudo apt update && sudo apt install -y golang-go builder-utils

# Install xcaddy
go install [github.com/caddyserver/xcaddy/cmd/xcaddy@latest](https://github.com/caddyserver/xcaddy/cmd/xcaddy@latest)

# Build Caddy with Cloudflare DNS integration
~/go/bin/xcaddy build --with [github.com/caddy-dns/cloudflare](https://github.com/caddy-dns/cloudflare)

# Move the binary to global path
sudo mv caddy /usr/local/bin/

Verify the installation and check if the module is successfully embedded by running: caddy list-modules | grep cloudflare.

Step 2: Configuring the Caddyfile for Mass Subdomains

Caddy uses a human-readable configuration file named the Caddyfile. To manage thousands of subdomains dynamically, we will configure a wildcard block and use Caddy's reverse proxy capabilities to route backend traffic efficiently.

Create a directory for configuration and initialize the file:

sudo mkdir -p /etc/caddy
sudo nano /etc/caddy/Caddyfile

Insert the following production-optimized configuration structure:

{
    # Global options
    email [email protected]
    acme_ca [https://acme-v02.api.letsencrypt.org/directory](https://acme-v02.api.letsencrypt.org/directory)
    
    # Storage configuration for certificates
    storage file_system /var/lib/caddy
}

# Handling the Wildcard Domain Block
*.yourdomain.com, yourdomain.com {

    # Enable TLS via DNS-01 challenge with Cloudflare
    tls {
        dns cloudflare {env.CLOUDFLARE_API_TOKEN}
    }

    # Dynamic Reverse Proxy Routing based on headers
    reverse_proxy localhost:8080 {
        header_up Host {http.request.host}
        header_up X-Real-IP {http.request.remote}
        header_up X-Forwarded-For {http.request.remote}
        header_up X-Forwarded-Proto {http.request.scheme}
    }

    # Enable gzip and zstd compression for high performance
    encode zstd gzip

    # Security Headers
    header {
        X-XSS-Protection "1; mode=block"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
        Referrer-Policy "no-referrer-when-downgrade"
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
    }

    # Logging configuration for traffic analysis
    log {
        output file /var/log/caddy/access.log {
            roll_size 50mb
            roll_keep 10
        }
    }
}

Step 3: Setting Up Systemd Service and Environment Variables

To ensure security, Caddy should run under a dedicated, non-privileged system user account, and API tokens must be safely loaded via environment variables rather than hardcoded into configurations.

Create the system user and log directory:

sudo useradd --system --user-group --home-dir /var/lib/caddy --shell /usr/sbin/nologin caddy
sudo mkdir -p /var/lib/caddy /var/log/caddy
sudo chown -R caddy:caddy /var/lib/caddy /var/log/caddy

Next, construct the systemd service file at /etc/systemd/system/caddy.service:

[Unit]
Description=Caddy Web Server
Documentation=[https://caddyserver.com/docs/](https://caddyserver.com/docs/)
After=network.target network-online.target
Wants=network-online.target

[Service]
Type=notify
User=caddy
Group=caddy
Environment=CLOUDFLARE_API_TOKEN=your_actual_super_secret_api_token_here
ExecStart=/usr/local/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/local/bin/caddy reload --config /etc/caddy/Caddyfile --force
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

Reload systemd, enable, and start the service:

sudo systemctl daemon-reload
sudo systemctl enable --now caddy
sudo systemctl status caddy

Performance Tuning for Thousands of Subdomains

When scale scales into thousands of concurrent connections over diverse subdomains, OS bottlenecks can emerge. Implement the following Linux kernel optimizations to ensure high-performance request handling:

1. File Descriptor Limits

Every active SSL connection consumes a file descriptor. As configured in our systemd unit file (LimitNOFILE=1048576), Caddy is allowed substantial overhead. Ensure system-wide limits are reflective of this by updating /etc/security/limits.conf:

caddy soft nofile 1048576
caddy hard nofile 1048576

2. Network Stack Adjustments

Optimize the network subsystem for fast connection recycling and memory usage by adding these configurations to /etc/sysctl.conf:

net.core.somaxconn = 65535
net.ipv4.tcp_max_tw_buckets = 1440000
et.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_fin_timeout = 15

Apply changes immediately using sudo sysctl -p.

Monitoring and Automated Renewals

Caddy continuously runs background workers to monitor certificate expiration. Let's Encrypt certificates are valid for 90 days, and Caddy will automatically attempt renewal 30 days prior to expiration. Because we are using a wildcard setup, Caddy refreshes a single foundational block which protects all corresponding subdomains simultaneously, minimizing API consumption on your DNS provider.

To inspect your current certificate health and ensure renewals are progressing cleanly without errors, examine the system logs directly:

sudo journalctl -u caddy -f --no-tail

Look for log tags containing [INFO] [tls] to verify certificate validation success states.

Conclusion

By coupling Caddy Server with the DNS-01 ACME challenge, infrastructure engineers can completely eliminate the operational friction of SSL provisioning at scale. This architecture effortlessly accommodates growth from tens to thousands of subdomains on a modest VPS setup without performance degradation or maintenance interruptions. Implement this setup today to establish a secure, automated, and infinitely scalable domain infrastructure for your business applications.

Automating Free Wildcard SSL for Thousands of Subdomains: A Production Guide Using Caddy Server on a VPS | DPTCloud