Why choose Caddy as the front end on your VPS?
The historical complexity of a reverse proxy comes down to TLS: generating a certificate, configuring renewal, managing the HTTP→HTTPS redirection. Caddy removes all of that. As soon as you specify a domain name in the Caddyfile, it contacts Let's Encrypt (or ZeroSSL as a fallback), provisions the certificate, staples it (OCSP stapling) and renews it in the background. The configuration is readable, short, and hot-reloadable without service interruption.
Caddy's strengths in production
- End-to-end automatic HTTPS: issuance, renewal and redirection without intervention
- Minimalist Caddyfile: a block of a few lines per site, far shorter than an Nginx config
- Built-in reverse proxy with health checks and load balancing to your containers or local ports
- HTTP/2 and HTTP/3 (QUIC) enabled by default
- Hot reload of the configuration (
caddy reload) without interrupting connections - JSON administration API to drive the configuration dynamically
Prerequisites
Caddy is frugal: 1 vCPU and 512 MB to 1 GB of RAM are enough to serve several sites as a reverse proxy. You can choose between installing it as a system binary (systemd service) on Ubuntu/Debian, or running it in a Docker container. In both cases, you need a domain pointing to the VPS IP and ports 80, 443 (and 443/UDP for HTTP/3) open in your firewall.
Deploy Caddy as a reverse proxy
Install Caddy
On Ubuntu/Debian, add the official repository then apt install caddy. Caddy registers itself as a systemd service active at startup. Check: systemctl status caddy. In container mode, start from the caddy:latest image and mount a volume on /data to persist the certificates.
Point the DNS and open the ports
Create an A record app.yourdomain.com to the VPS IP. Open the necessary ports: ufw allow 80, ufw allow 443 and ufw allow 443/udp for HTTP/3. Port 80 is essential for Let's Encrypt's HTTP challenge.
Write the Caddyfile
Edit /etc/caddy/Caddyfile. To expose an application running locally on port 3000: app.yourdomain.com { reverse_proxy localhost:3000 }. That's all: Caddy deduces that HTTPS is needed because you specified a domain and triggers the certificate issuance.
Reload and check
Apply the configuration without downtime: caddy reload --config /etc/caddy/Caddyfile. Follow the certificate acquisition in the logs: journalctl -u caddy -f. On the first HTTPS call to your domain, the certificate is in place.
Add security headers and compression
Enrich the block with a header directive for HSTS and the anti-clickjacking headers, and enable encode gzip zstd for compression. Since the config is declarative, these additions stay readable and a simple caddy reload applies them.
To host several applications on the same VPS, simply list several site blocks in the same Caddyfile, each with its reverse_proxy. And for local development without a public domain, replace the domain name with :80 or use the tls internal directive: Caddy then generates a local CA and signs the certificate itself, handy for an internal pre-production environment.
The official documentation
For advanced configuration and options specific to the tool, refer to the official Caddy documentation. This guide covers going live on a VPS; the vendor's docs remain the reference for fine-tuning, major updates and specific use cases.