Caddy Web Server Installation and Configuration

Caddy is a modern, easy-to-use web server written in Go that automatically provisions and renews HTTPS certificates from Let's Encrypt. Unlike traditional web servers that require complex SSL configuration, Caddy handles certificate management transparently, making it ideal for sysadmins managing multiple domains. This guide covers installation, configuration through Caddyfiles, and advanced reverse proxy scenarios.

Table of Contents

  1. Introduction
  2. System Requirements
  3. Installation Methods
  4. Binary Installation
  5. Package Manager Installation
  6. Caddyfile Basics
  7. Reverse Proxy Configuration
  8. Static File Server
  9. PHP Support
  10. API Gateway Configuration
  11. Automatic HTTPS
  12. Troubleshooting and Monitoring

Introduction

Caddy is a powerful web server designed with security and ease of use as primary goals. Its most distinctive feature is automatic HTTPS—Caddy automatically obtains and renews SSL/TLS certificates from Let's Encrypt without any additional configuration. This guide helps you harness Caddy's capabilities for production environments.

System Requirements

Ensure your VPS or bare metal server meets these requirements:

  • Linux kernel 3.10 or newer
  • 256 MB RAM minimum (1 GB recommended)
  • 50 MB disk space
  • Port 80 and 443 accessible for HTTPS
  • Internet connectivity for certificate validation
  • Sudo or root access

Caddy supports multiple architectures: x86_64, ARM, ARM64, and MIPS.

Installation Methods

Caddy can be installed through several methods depending on your preference for updates and maintainability.

Binary Installation

Download the latest Caddy release:

cd /tmp
wget https://github.com/caddyserver/caddy/releases/download/v2.7.4/caddy_2.7.4_linux_amd64.tar.gz
tar xzf caddy_2.7.4_linux_amd64.tar.gz

Create the necessary system directories and user account:

sudo useradd -r -s /bin/false -d /var/lib/caddy -m caddy
sudo mkdir -p /etc/caddy
sudo mkdir -p /var/www
sudo mkdir -p /var/log/caddy
sudo chown -R caddy:caddy /var/lib/caddy /var/log/caddy /etc/caddy

Install the binary and set permissions:

sudo mv /tmp/caddy /usr/local/bin/caddy
sudo chmod +x /usr/local/bin/caddy
sudo setcap cap_net_bind_service=+ep /usr/local/bin/caddy

The setcap command allows Caddy to bind to ports 80 and 443 without requiring root privileges.

Create a systemd service file:

sudo tee /etc/systemd/system/caddy.service > /dev/null <<EOF
[Unit]
Description=Caddy Web Server
Documentation=https://caddyserver.com/docs/
After=network-online.target
Wants=network-online.target

[Service]
User=caddy
Group=caddy
ExecStart=/usr/local/bin/caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
ExecReload=/usr/local/bin/caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile
Type=notify
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal
SyslogIdentifier=caddy

[Install]
WantedBy=multi-user.target
EOF

Enable and start Caddy:

sudo systemctl daemon-reload
sudo systemctl enable caddy
sudo systemctl start caddy

Package Manager Installation

For Debian/Ubuntu systems, use the official repository:

sudo apt update
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl https://dl.filippo.io/caddy/key.gpg | sudo tee /etc/apt/trusted.gpg.d/caddy-filippo.asc > /dev/null
echo "deb [signed-by=/etc/apt/trusted.gpg.d/caddy-filippo.asc] https://dl.filippo.io/caddy/deb stable main" | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

For RHEL/CentOS systems:

sudo dnf copr enable @caddy/caddy
sudo dnf install caddy
sudo systemctl start caddy
sudo systemctl enable caddy

Caddyfile Basics

Create a Caddyfile at /etc/caddy/Caddyfile to define how Caddy serves your content:

example.com www.example.com {
    root * /var/www/html
    file_server
    encode gzip
}

This basic configuration serves static files from /var/www/html with automatic HTTPS.

Handle multiple sites in a single Caddyfile:

example.com {
    root * /var/www/example
    file_server
    encode gzip
}

api.example.com {
    reverse_proxy localhost:8080
}

blog.example.com {
    reverse_proxy localhost:3000
}

Reload the configuration without restarting:

sudo caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile

Reverse Proxy Configuration

Caddy excels at reverse proxying to backend applications:

app.example.com {
    reverse_proxy localhost:8080 {
        header_uri -Authorization
        health_uri /health
        health_interval 10s
        health_timeout 5s
        policy least_conn
    }
}

Configure multiple backend servers with load balancing:

api.example.com {
    reverse_proxy localhost:8001 localhost:8002 localhost:8003 {
        health_uri /api/health
        health_interval 5s
        header_up Host {http.request.host}
        header_up X-Forwarded-For {http.request.remote.host}
        header_up X-Forwarded-Proto {http.request.scheme}
    }
}

Handle path-based routing:

example.com {
    handle /api/* {
        reverse_proxy localhost:8080
    }
    
    handle /static/* {
        root * /var/www/static
        file_server
    }
    
    handle {
        reverse_proxy localhost:3000
    }
}

Static File Server

Configure Caddy as a high-performance static file server:

cdn.example.com {
    root * /var/www/cdn
    file_server {
        precompressed br gzip
        index index.html
    }
    
    encode gzip
    
    header Cache-Control "public, max-age=31536000"
    
    @immutable {
        path /assets/*
    }
    header @immutable Cache-Control "public, immutable, max-age=31536000"
}

Caddy automatically precompresses files and serves optimized versions to clients supporting brotli or gzip.

PHP Support

Enable PHP support by integrating with PHP-FPM:

example.com {
    root * /var/www/html
    
    php_fastcgi localhost:9000 {
        dial timeout 3s
        read timeout 30s
        write timeout 30s
    }
    
    file_server
    encode gzip
}

Ensure PHP-FPM is running:

sudo systemctl status php8.2-fpm
sudo systemctl enable php8.2-fpm

Configure PHP-FPM listener (edit /etc/php/8.2/fpm/pool.d/www.conf):

[www]
listen = 127.0.0.1:9000
listen.allowed_clients = 127.0.0.1

Reload PHP-FPM:

sudo systemctl reload php8.2-fpm

API Gateway Configuration

Configure Caddy as an API gateway with multiple routes:

api.example.com {
    encode gzip
    
    @v1 {
        path_regexp ^/v1/.*
    }
    handle @v1 {
        reverse_proxy localhost:8001 {
            uri /
        }
    }
    
    @v2 {
        path_regexp ^/v2/.*
    }
    handle @v2 {
        reverse_proxy localhost:8002 {
            uri /
        }
    }
    
    handle {
        respond "API not found" 404
    }
}

Add authentication middleware:

api.example.com {
    @protected {
        not header Authorization Bearer*
    }
    respond @protected 401
    
    reverse_proxy localhost:8080
}

Automatic HTTPS

Caddy automatically provisions HTTPS certificates:

example.com www.example.com {
    root * /var/www/html
    file_server
    encode gzip
}

Specify custom certificate providers or storage:

{
    storage file_system {
        root /var/lib/caddy/certificates
    }
    
    acme_ca https://acme.letsencrypt.org/directory
    acme_email [email protected]
}

example.com {
    root * /var/www/html
    file_server
}

Monitor certificate expiration:

sudo caddy list-certs

Renew certificates manually:

sudo caddy renew

Troubleshooting and Monitoring

Check Caddy service status:

sudo systemctl status caddy
sudo journalctl -u caddy -n 50 -f

Validate Caddyfile syntax:

sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile

Check certificate status:

sudo caddy list-certs

Monitor Caddy metrics with the admin API:

curl http://localhost:2019/config/
curl http://localhost:2019/metrics?namespace=caddy

Enable metrics in Caddyfile:

{
    admin localhost:2019
    metrics
}

example.com {
    root * /var/www/html
    file_server
}

Test HTTPS configuration:

curl -i https://example.com
openssl s_client -connect example.com:443 -servername example.com

Debug configuration issues:

sudo caddy run --config /etc/caddy/Caddyfile --adapter caddyfile --debug

Check port binding:

sudo netstat -tlnp | grep caddy

Verify DNS resolution:

nslookup example.com
dig example.com

Conclusion

Caddy provides a modern, user-friendly web server that eliminates SSL/TLS configuration complexity. Its automatic HTTPS, straightforward Caddyfile syntax, and built-in reverse proxy capabilities make it ideal for cloud VPS and bare metal deployments. Whether serving static files, proxying to backend services, or implementing API gateways, Caddy delivers reliable performance with minimal configuration overhead.