Can't figure out Caddy + Docker reverse proxy

1. The problem I’m having:

I’m trying to set up Caddy as a reverse proxy in a Docker environment. After starting the containers I see no errors (container starts successfully) and one of the services I’m trying to access works (Vaultwarden), the other errors out.

2. Error message.

{
   "level": "error",
   "ts": 1699874409.0697393,
   "logger": "http.log.error",
   "msg": "dial tcp 172.18.0.3:80: connect: connection refused",
   "request": {
      "remote_ip": "83.24.245.224",
      "remote_port": "46364",
      "client_ip": "83.24.245.224",
      "proto": "HTTP/2.0",
      "method": "GET",
      "host": "testowy.site",
      "uri": "/",
      "headers": {
         "Accept": [
            "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"
         ],
         "Accept-Encoding": [
            "gzip, deflate, br"
         ],
         "Sec-Fetch-Dest": [
            "document"
         ],
         "Sec-Fetch-Mode": [
            "navigate"
         ],
         "Sec-Fetch-User": [
            "?1"
         ],
         "User-Agent": [
            "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"
         ],
         "Accept-Language": [
            "pl,en-US;q=0.7,en;q=0.3"
         ],
         "Upgrade-Insecure-Requests": [
            "1"
         ],
         "Sec-Fetch-Site": [
            "none"
         ],
         "Te": [
            "trailers"
         ]
      },
      "tls": {
         "resumed": false,
         "version": 772,
         "cipher_suite": 4865,
         "proto": "h2",
         "server_name": "testowy.site"
      }
   },
   "duration": 0.0014571,
   "status": 502,
   "err_id": "sq25yrguc",
   "err_trace": "reverseproxy.statusError (reverseproxy.go:1265)"
}

I use Docker compose file on a Rocky 9 server. Here’s the Caddyfile:

testowy.site {
    reverse_proxy ghostsite
}

pass.testowy.site {
    reverse_proxy vaultwarden
}

pass.testowy.site works, testowy.site errors out.

3. Caddy version:

v2.7.5

4. How I installed and ran Caddy:

  • it’s installed via Docker compose. Here’s the compose.yaml file:
services:
  caddy:
    image: caddy:latest
    container_name: caddy
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - $PWD/Caddyfile:/etc/caddy/Caddyfile:ro
      - $PWD/site:/srv
      - $PWD/caddy_data:/data
      - $PWD/caddy_config:/config
    networks:
      - robisznet

  ghostsite:
    image: ghost:latest
    container_name: ghostsite
    restart: always
    environment:
      database__client: sqlite3
      database__connection__filename: content/data/ghost.db
      NODE_ENV: production
      url: https://testowy.site
      mail__transport: SMTP
      mail__options__host: mail.smtp2go.com
      mail__options__port: 2525 
      mail__options__service: SMTP2Go
      mail__options__auth__user: noreply-testowy.site
      mail__options__auth__pass: /run/secrets/mail_password
    volumes:
      - ~/new/ghost:/var/lib/ghost/content:z
    networks:
      - robisznet
    secrets:
      - mail_password
    depends_on:
      - caddy

  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: always
    environment:
      WEBSOCKET_ENABLED: "true"  # Enable WebSocket notifications.
      SHOW_PASSWORD_HINT: "false"
      DOMAIN: "https://pass.testowy.site"
      SIGNUPS_ALLOWED: "false"
      SMTP_HOST: "mail.smtp2go.com"
      SMTP_FROM: "myemail@testowy.site"
      SMTP_PORT: "465"
      SMTP_SECURITY: "force_tls"
      SMTP_USERNAME: "testowysite"
      SMTP_PASSWORD: /run/secrets/vault_mail_pass
    volumes:
      - ./vw-data:/data
    networks:
      - robisznet
    secrets:
      - vault_mail_pass
    depends_on:
      - caddy

networks:
  robisznet:

secrets:
  mail_password:
    file: ./.ENV/mail.txt
  vault_mail_pass:
    file: ./.ENV/vault_mail.txt

a. System environment:

It’s running Docker on Rocky 9.

I’ve tried disabling SELinux and UFW, but that didn’t help.

All suggestions are greatly appreciated - I’m inexperienced when it comes to setting up proxies.

Checked whether containers indeed share the same network with:

docker network inspect -f '{{range .Containers}}{{.Name}} {{end}}' testowe_robisznet

and I got:

caddy ghostsite vaultwarden

When proxying, you need to use the port number that the service actually listens on.

For example, see the docs on https://hub.docker.com/_/ghost/ which explains that the default port is 2368, so you should use reverse_proxy ghostsite:2368 instead.

1 Like

thank you Francis!
Ok, so now I have Ghost working with the following entry in Caddyfile:

my.site {
	reverse_proxy ghostsite:2368 {
		header_up Host {host}
	}
}

Let’s say I have more Ghost blogs, each in its own container - does every one need to have a different port? I assumed not, as I thought it’s being handled by the Docker’s networking stack (each container has its own address).

How about several Wordpress sites? Would it be something like:

wp1.my.site {
	reverse_proxy wp1:80
}

wp2.my.site {
	reverse_proxy wp2:80
}

wp3.my.site {
	reverse_proxy wp3:80
}

Ans so on? How does https works in this scenario?

Do I have to set up “ports” section of the compose.yml file? More specifically - do I need to bind container port to some specific host one? If yes - should the port be different for each service (container / docker compose entry)?

I know it is probably pretty basic, RTFM kind of stuff - thus I appreciate your answer even more.
I’m a bit overwhelmed as I’m trying to learn to many things at once.

Remove this; you don’t need it, Caddy already passes through the Host as-is.

If they’re all the same base image, then they’ll all be using the same port. They’re isolated from eachother.

Caddy issues certs for each domain you list in your config.

All you need is this, which you already have:

1 Like

Ok, so possibly the last thing to figure out. If I decide to have multiple Wordpress instances how do I deal with mouting /var/www/html ?

So with single Wordpress the container with it has:

    volumes:
      - $PWD/php.ini:/usr/local/etc/php/conf.d/custom.ini
      - $PWD/wordpress:/var/www/html

and the one with Caddy:

    volumes:
      - $PWD/wordpress:/var/www/html

With following entry in Caddyfile:

some.site {
        root * /var/www/html
        php_fastcgi wordpress:9000
        file_server
}

– thus I’m not sure how I add another one.

If you use a wordpress apache container, you can avoid that entirely by simply proxying to it.

But if you use php-fpm, yes that is something to resolve.

Mount it to separate directories in Caddy like /srv/wordpress1 and /srv/wordpress2, set those as your root. Then in php_fastcgi use the root option to set php-fpm’s root to /var/www/html. That way Caddy will read files from /srv and such, and when it tells php-fpm to run the PHP scripts it will read from /var/www/html in its own container.