How to build a web server farm with Caddy and Docker
Deploy a scalable, reverse-proxied web server farm using Caddy with Docker Compose on Ubuntu 24.04. This guide covers network setup, volume management, and SSL automation.
You will deploy a scalable web server farm using Caddy as a reverse proxy and Docker for containerization. These steps target Ubuntu 24.04 with Docker Engine 27.x and Caddy 2.8.x.
Prerequisites
- Ubuntu 24.04 LTS server with root or sudo access
- Docker Engine 27.0 or newer installed
- Docker Compose plugin installed
- At least 2 GB of available RAM
- Static IP address or dynamic DNS provider for SSL certificates
Step 1: Install Docker and Docker Compose
Install the Docker Engine and Compose plugin to manage your containerized web farm. This ensures you have the necessary runtime environment to run Caddy and your backend applications.
apt update
apt install -y docker.io docker-compose-plugin
Reload the Docker daemon to apply the changes.
systemctl restart docker
Verify that Docker is running correctly.
docker info | grep "Server Version"
You will see output similar to this:
Server Version: 27.0.0
Step 2: Create the Docker Network
Define a bridge network to isolate your web farm containers from the host system. This network allows Caddy to communicate with backend application containers securely.
docker network create --driver bridge caddy-net
Confirm the network was created successfully.
docker network ls | grep caddy-net
The output should list your new network.
Step 3: Create the Caddy Dockerfile
Create a custom Dockerfile for Caddy to include your specific configuration. This file defines the container image and copies your Caddyfile into the container.
cat > /root/Caddyfile << 'EOF'
{
admin off
}
example.com {
reverse_proxy 172.18.0.2:8080
}
api.example.com {
reverse_proxy 172.18.0.3:8081
}
EOF
Replace the domain names and upstream IP addresses with your actual values.
Step 4: Build the Caddy Container Image
Build the Docker image using the Caddyfile you just created. This process compiles your configuration into a container image that can be deployed to any host.
docker build -t caddy-web-farm /root/
Verify that the image was built successfully.
docker images | grep caddy-web-farm
You will see an image with the tag `caddy-web-farm` listed.
Step 5: Run the Caddy Container
Start the Caddy container using the custom image and the bridge network you created earlier. This launches your reverse proxy server and begins handling incoming HTTP traffic.
docker run -d --name caddy --network caddy-net -p 80:80 -p 443:443 -v /root/Caddyfile:/etc/caddy/Caddyfile caddy-web-farm
Check the container status to ensure it is running.
docker ps | grep caddy
The output should show the container status as `Up`.
Step 6: Configure Automatic SSL with Caddy
Configure Caddy to automatically obtain and renew SSL certificates using Let's Encrypt. This ensures your web farm is accessible over HTTPS without manual certificate management.
docker exec caddy caddy reload
Verify that the Caddy process is listening on port 443.
docker logs caddy | grep "tls"
You should see logs indicating that TLS certificates are being managed.
Step 7: Deploy Backend Application Containers
Create a simple backend container to simulate a web application behind the proxy. This demonstrates how the reverse proxy routes traffic to different services.
docker run -d --name backend-app --network caddy-net -p 8080:80 nginx:alpine
Map the backend container to a specific port on the host.
docker run -d --name backend-api --network caddy-net -p 8081:80 nginx:alpine
Verify that both backend containers are running.
docker ps | grep backend
The output should list both `backend-app` and `backend-api` as running.
Verify the installation
Test that your web server farm is functioning correctly. Access the root domain and the API subdomain to confirm that Caddy is routing traffic to the correct backend containers.
curl -k https://example.com
curl -k https://api.example.com
You will receive HTTP 200 responses from both endpoints.
Troubleshooting
Check container logs if you encounter connection errors or SSL failures. Use these commands to inspect runtime errors and network connectivity issues.
docker logs caddy
docker logs backend-app
docker logs backend-api
Inspect the Docker network to verify that containers can reach each other.
docker network inspect caddy-net
Restart the Caddy container if configuration changes require a reload.
docker restart caddy
Ensure that firewall rules allow traffic on ports 80 and 443.
ufw allow 80/tcp
ufw allow 443/tcp
ufw reload
Verify that your Caddyfile syntax is correct by testing it locally before deploying.
caddy validate /root/Caddyfile
If validation fails, correct the syntax errors in the Caddyfile and rebuild the image.