Using Caddy in Docker to reverse proxy to localhost apps

1. Caddy version (caddy version):

Caddy v2

2. How I run Caddy:

via Docker caddy:alpine image.

a. System environment:

Docker Desktop v2.3.0.4
macOS Catalina v10.15.6

b. Command:

docker run -d --name caddy -p 80:80 -p 443:443 -p 2019:2019 caddy:alpine

c. Service/unit/compose file:

Not relevant.

d. My complete Caddyfile or JSON config:

No config set

3. The problem I’m having:

Not really having a problem, more of looking for guidance on achieving something. I want to use Caddy running in a docker container on localhost to proxy my-app.local to localhost:9000 and write a little shell script that allows me to add more virtual hosts via the api, for example vhost second-app.local 9001 would then tell Caddy to proxy second-app.local to localhost:9001.

4. Error messages and/or full log output:

No errors.

5. What I already tried:

Never tried anything yet.

6. Links to relevant resources:

No links.

Hi Josh!

The Caddy API is just what you need. <— that’s a link to the API tutorial, here are the full docs: API — Caddy Documentation

So a basic Caddyfile reverse proxy looks like this:


reverse_proxy localhosts:9000

That is roughly equivalent to this JSON (caddy adapt):

	"apps": {
		"http": {
			"servers": {
				"vhosts": {
					"@id": "proxy",
					"listen": [":443"],
					"routes": [{
						"match": [{"host": ["my-app.local"]}],
						"handle": [{
							"handler": "reverse_proxy",
							"upstreams": [{"dial": "localhost:9000"}]

Note that I’ve annotated it with "@id": "proxy" which makes it easier to write API queries.

You can then add a proxy upstream like this:

POST localhost:2019/id/proxy/routes
Content-Type: application/json

	"match": [{"host": ["second-app.local"]}],
	"handle": [{
		"handler": "reverse_proxy",
		"upstreams": [{"dial": "localhost:9001"}]

That’s it. You can see that it worked with a simple:

GET localhost:2019/config/

and you’ll see:


Similarly, you can remove routes with DELETE requests:

DELETE localhost:2019/id/proxy/routes/1
1 Like

FYI since you’re running in Docker, your initial config will need to set the admin address to (default is localhost:2019) because the default will not accept requests from outside the container. You could get a shell into the container and make the request though.

Also, be aware that if you try to reverse-proxy to services running outside of the docker network (i.e. on your host), you’ll need to use docker’s host IP (which is mildly annoying to find, but a quick google should point you in the right direction), or use the host networking option for the caddy container to get around that (but this throws away many of the advantages of docker networking). Host networking would also get around the admin endpoint issue above.

If most of your services are not running in docker, then it would probably just be easier to run Caddy directly on your mac.


If you are running your stack in Docker, I’d suggest considering GitHub - lucaslorentz/caddy-docker-proxy: Caddy as a reverse proxy for Docker which would let you configure Caddy from the docker labels of your other services. That way Caddy would be automatically configured based on the running services. This is much simpler, and totally integrated, so you wouldn’t need extra scripting or need to use the Caddy API.

Or, you could just use the Caddyfile… because each service is as simple as adding 3 lines to your Caddyfile (with possibly a bit more to use internal TLS, depending on your usecase):

my-app.local {
	reverse_proxy localhost:9000

second-app.local {
	reverse_proxy localhost:9001
1 Like

This topic was automatically closed after 30 days. New replies are no longer allowed.