I recently discovered that using caddy-docker-proxy is an excellent solution for setting up local domains for my Docker containers. It has become my go-to reverse proxy setup for Docker containers.
Nginx Proxy Manager VS Caddy Docker Proxy
Before this, I was using Nginx Proxy Manager, which works great if you’re using verified public domains. But for local development, it’s needs extra work, which I don’t have time for. While I did consider using self-signed SSL certificates, the setup process seemed more trouble than it was worth.
Then I remembered that Caddy supports self-signed certificates out of the box. After some digging, I found caddy-docker-proxy — and it worked like a charm.
Step 1: Point Your Local Domain to 127.0.0.1
First, I added an entry to my /etc/hosts
file to point a custom domain to my local machine:
127.0.0.1 ynrfin.local
Step 2: Create a Docker Network
Next, I created a dedicated Docker network called caddy
, which both the proxy and my services will share:
docker network create caddy
Step 3: Run the caddy-docker-proxy
Container
Here’s the basic docker-compose.yml
config I use:
version: "3.7"
services:
caddy:
image: lucaslorentz/caddy-docker-proxy:ci-alpine
ports:
- 80:80
- 443:443
environment:
- CADDY_INGRESS_NETWORKS=caddy
networks:
- caddy
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- caddy_data:/data
restart: unless-stopped
networks:
caddy:
external: true
volumes:
caddy_data: {}
Important notes:
- Ports 80 (HTTP) and 443 (HTTPS) must be exposed. This is the entry point for every request to other containers.
- The proxy must be connected to the external
caddy
network.
Step 4: Configure Another Container (e.g. WordPress)
Here’s how I configured a WordPress container to work with the proxy:
services:
wordpress:
image: wordpress:latest
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
volumes:
- wordpress_data:/var/www/html
depends_on:
- db
networks:
- wp_internal
- caddy
labels:
caddy: ynrfin.local
caddy.tls: internal
caddy.reverse_proxy: "{{upstreams 80}}"
db:
...
networks:
wp_internal:
internal: true
caddy:
external: true
Key points:
- The
labels
section inside thewordpress
service defines how caddy-docker-proxy handles routing. - I didn’t expose WordPress directly, caddy-docker-proxy will route the request and response for me.
- caddy-docker-proxy inspects other containers via Docker and looks for
caddy
labels to know where to route requests.
Handling Multiple Ports (e.g. Laravel + Vite)
For Laravel projects using both port 80
(PHP server) and 5173
(Vite dev server), here’s how I handle it:
labels:
caddy_0: botlog.local
caddy_0.reverse_proxy: "{{upstreams 80}}"
caddy_0.tls: internal
caddy_1: botlog.local:5173
caddy_1.reverse_proxy: "{{upstreams 5173}}"
caddy_1.tls: internal
Explanation:
caddy_0
handles HTTP (port 80)caddy_1
handles Vite assets (port 5173)
Final Thoughts
Using caddy-docker-proxy
has made my local development workflow much smoother. I no longer have to remember which app use what port, as I can use more humane address using domain
If you’re dealing with local Docker containers and want proper domains and HTTPS — especially for multiple projects setups — I highly recommend giving this a try.