Trisul's Blog

Network & Cloud Engineering Insights

Deploying Web Apps with Caddy and Docker on a Custom Domain

Written by Trisul ·

Combining Caddy and Docker is one of the fastest ways to deploy modern web applications with automatic HTTPS and clean configuration. In this guide, you’ll learn how to set up Caddy as a reverse proxy in Docker, connect it to your app, and serve it over your custom domain with SSL—all in just a few steps.

Why Caddy + Docker?

  • Automatic HTTPS: Caddy gets and renews SSL certificates for your domain, hands-free.
  • Simple Config: The Caddyfile is human-friendly and powerful for routing, proxying, and more.
  • Portability: Docker lets you run your stack anywhere, identically.
  • Resilience: Docker and Caddy can auto-restart and recover from failures.

Step 1: Point Your Domain to Your Server

Log in to your domain registrar and set an A record for your domain (e.g., yourdomain.com) to your server’s public IP address.
Example:

Type: A
Name: @
Value: YOUR_SERVER_IP
TTL: Automatic

Wait for DNS propagation (usually a few minutes to an hour).

Step 2: Project Structure

Organize your files like this:

my-caddy-app/
  ├── Caddyfile
  ├── docker-compose.yml
  └── site/
      └── index.html

Step 3: Write Your Caddyfile

For a static site:

yourdomain.com {
  root * /usr/share/caddy
  file_server
}

For a backend app (e.g., Node.js on port 3000):

yourdomain.com {
  reverse_proxy app:3000
}

Step 4: Docker Compose Setup

Sample docker-compose.yml for a static site:

version: "3.7"
services:
  caddy:
    image: caddy:latest
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./site:/usr/share/caddy
      - caddy_data:/data
      - caddy_config:/config
volumes:
  caddy_data:
  caddy_config:

For a backend app:

version: "3.7"
services:
  caddy:
    image: caddy:latest
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - app
  app:
    image: your-backend-image
    expose:
      - "3000"
volumes:
  caddy_data:
  caddy_config:

Step 5: Redirect All Traffic to Your Domain (Recommended)

To ensure all HTTP/HTTPS and www traffic redirects to your main domain, add this to your Caddyfile above your main site block:

http://, https:// {
  redir https://yourdomain.com{uri} permanent
}
yourdomain.com {
  reverse_proxy app:3000
}

This ensures all requests are redirected to your canonical domain with HTTPS.

Step 6: Start Everything Up

From your project directory, run:

docker compose up -d

Caddy will automatically obtain SSL certificates for your domain and serve your app securely.

Step 7: Visit Your Site

Open https://yourdomain.com in your browser. You should see your app live with a valid SSL certificate—no manual certificate management required!

Troubleshooting

  • DNS not propagating? Double-check your A record and wait a bit longer.
  • SSL errors? Make sure ports 80 and 443 are open and not blocked by a firewall.
  • Changed your config? Run docker compose restart caddy to reload Caddy with your new settings.

Conclusion

Caddy and Docker together offer a modern, secure, and fast way to deploy web apps on your own domain. With automatic HTTPS, simple configuration, and Docker’s portability, you can focus on building your application—not managing infrastructure.

Questions or want a template for your stack? Leave a comment or contact me!