Wordpress with Caddy v2 on Docker

Continuing the discussion from Discourse with Caddy v2 on Docker:

1. Caddy version (2.0 / 2.1.0 beta):

2. How I run Caddy:

a. System environment:

Docker on Ubuntu 20.04

b. Command:

docker-compose up

c. Service/unit/compose file:

version: '3.3'

services:
  caddy:
    # env_file:
    #   - .env
    container_name: caddy
    build: .
    ports:
      - "80:80"
      - "443:443"
      - "80:80/udp"
      - "443:443/udp"
    volumes:
      - /var/discourse/shared/web-only:/sock
      - /var/caddy/Caddyfile:/etc/caddy/Caddyfile
      - /var/caddy:/root/.caddy
      - /var/caddy/data:/data
      - /var/caddy/config:/config
    restart: always

  wp1:
    env_file:
      - .env  
    image: $WP_IMAGE
    container_name: wordpress
    hostname: dev.xxxxxxxx.com
    # ports:
    #   - 8080:80
    # depends_on:
    #  - db1
    restart: always
    environment:
      WORDPRESS_DB_HOST: db1
      WORDPRESS_DB_USER: wpdrone
      WORDPRESS_DB_PASSWORD: $MYSQL_PASS_1
      WORDPRESS_DB_NAME: db-wp1
    volumes:
      - /var/www/wp1:/var/www/html

  db1:
    env_file:
      - .env  
    image: $DB_IMAGE
    restart: always
    environment:
      MYSQL_DATABASE: db-wp1
      MYSQL_USER: wpdrone
      MYSQL_PASSWORD: $MYSQL_PASS_1
      MYSQL_ROOT_PASSWORD: $MYSQL_PASS_1
#      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    volumes:
      - /var/mysql/wp1:/var/lib/mysql

d. My complete Caddyfile or JSON config:

Working v1 Caddyfile, basically

forums.example.com {
  proxy / unix:/sock/nginx.http.sock {
    transparent
  }
}

dev.example.com {
  proxy / wordpress {
    transparent
  }
}

v2 Caddyfile, actual

{
	debug
}

forum.xxxxxxxx.com {
    reverse_proxy unix//sock/nginx.http.sock
    tls {
        dns cloudflare APIKEY
    }
}

dev.xxxxxxxx.com {
    # root * /var/www/wp1
    # root * /var/www/html
    reverse_proxy / wordpress
    # php_fastcgi / wp1
    # reverse_proxy wordpress
    # php_fastcgi wordrpess
    # php_fastcgi wordrpess:9000
    tls {
        dns cloudflare APIKEY
    }
}

3. The problem I’m having:

This is an attempt to move from working v1 to v2.x.

Discourse is working via the same Caddy docker-compose, but I get a blank page for Wordpress. Internal network issue?

I was getting apache errors from the Wordpress service about it not being able to determing the FQDN before specifying via this line in docker-compose. Not sure if something changed (I’ve never needed that before), or if it’s a symptom of another error:

hostname: dev.xxxxxxxx.com

…after adding that, all service containers build with no error messages.

Of course, these bits of the error log stand out, because they’re similar to the problem I was having with the Discourse setup, which ended up being a syntax error. But I’m not even finding a backslash to change.

upstream wordpress:: invalid dial address wordpress:: invalid start port: strconv.ParseUint: parsing \"\": invalid syntax"

Full error log below

4. Error messages and/or full log output:

invalid dial address, etc.

caddy | {“level”:“error”,“ts”:1593380833.556185,“logger”:“http.log.error”,“msg”:“making dial info: upstream wp1:: invalid dial address wp1:: invalid start port: strconv.ParseUint: parsing "": invalid syntax”,“request”:{“method”:“GET”,“uri”:“/”,“proto”:“HTTP/1.1”,“remote_addr”:“172.68.132.228:44600”,“host”:“dev.xxxxxxxx.com”,“headers”:{“Cookie”:[“__cfduid=dc88b5462c50f9e7efa2c00ee103b2a071593329757”],“Cf-Connecting-Ip”:[“2001:5b0:49cc:8b0c:49e8:6443:305d:48f8”],“X-Forwarded-For”:[“2001:5b0:49cc:8b0c:49e8:6443:305d:48f8”],“X-Forwarded-Proto”:[“https”],“Accept”:[“text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8”],“Cf-Visitor”:[“{"scheme":"https"}”],“User-Agent”:[“Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0”],“Upgrade-Insecure-Requests”:[“1”],“Cf-Request-Id”:[“039e7f20ed000002df5f066200000001”],“Accept-Encoding”:[“gzip”],“Cf-Ray”:[“5aaa9ae17a0302df-SJC”],“Connection”:[“Keep-Alive”],“Dnt”:[“1”],“Cache-Control”:[“max-age=0”],“Cf-Ipcountry”:[“US”],“Accept-Language”:[“en-US,en;q=0.5”],“Cdn-Loop”:[“cloudflare”]},“tls”:{“resumed”:false,“version”:772,“ciphersuite”:4867,“proto”:“”,“proto_mutual”:true,“server_name”:“dev.xxxxxxxx.com”}},“duration”:0.000070497}

5. What I already tried:

  1. Building from Caddy v2.0.0 and v2.1.0 beta Docker images. Both seem to build fine. Side-note: v2.1.0 is released on regular builds. I don’t see a non-beta in Docker hub yet. Is that pending?
  2. Wordpress default (apache) and wordpress:php7.4-fpm. The FPM image seems to respond similarly whether I use php_fastcgi or reverse_proxy in Caddyfile. Pretty sure I was getting the same errors at from both at one point, but currently I don’t get errors with FPM, its just doesn’t seem to do anything.
  3. Various syntax, for example reverse_proxy / wp1 vs. reverse_proxy wp1
  4. Commented variations in Caddyfile and docker-compose.yml.
  5. Referencing variations in Docker network hostnames in Caddyfile. For example, the service name, specifying a container name in compose, using the name reported by Docker (usually has _1 appended to the service name), etc.
  6. I dunno. I’ve now been shuffling bits and pieces around since yesterday.

Compose output for wordpress fpm:

wordpress | [28-Jun-2020 23:02:19] NOTICE: fpm is running, pid 1
wordpress | [28-Jun-2020 23:02:19] NOTICE: ready to handle connections

And for wordpress apache:

wordpress | [Sun Jun 28 23:13:35.526415 2020] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.38 (Debian) PHP/7.4.7 configured -- resuming normal operations
wordpress | [Sun Jun 28 23:13:35.526536 2020] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'

6. Links to relevant resources:

Thread where I eventually had this working in v1 2 years ago:

Try reverse_proxy http://wordpress or reverse_proxy wordpress:80. I don’t think Caddy assumes a port for the proxy upstream. If you specify http it will assume port 80, or you can explicitly specify port 80.

FYI Caddy v2 differs from v1 in that path matching is now exact-match, so if you put / as the path matcher, it will only match requests to the root of your site and nothing else. Omitting it is the same as * meaning “match all requests”. You can suffix a path matcher with * to match anything that has the given path prefix, e.g. /foo/*

1 Like

Worked!

    reverse_proxy wordpress:80

This is the line in the Upgrade Guide I should have listened to more closely. :laughing:

  • It might be best to go into Caddy 2 with no assumptions carried over from Caddy 1.
2 Likes

To explain this error message, this might read in a misleading manner because the error message is a JSON encoded string. " characters need to be escaped in JSON strings because " is the JSON character for enclosing strings; so the " double quotes are prefixed with \ as an escape character to tell JSON parsers "no, this isn’t the end of the string, it’s just a literal " here. So really the error message is this:

upstream wordpress:: invalid dial address wordpress:: invalid start port: strconv.ParseUint: parsing "": invalid syntax

Which is essentially saying “couldn’t parse an integer number out of an the empty string” or just “couldn’t read a port number”.

I think it’s somewhat of a misleading error message, I’ll look into improving that. Or maybe we’ll assume port 80 like Caddy v1 did in a future release (but Caddy tends to lean towards “HTTPS by default” so that might not happen). TBD.

That error does make sense in retrospect. It also said invalid port, which is pretty clear. I definitely held on to some incorrect assumptions from v1 while working through it.

Any similarly simple solutions for the FPM version? I did try specifying port 9000 with php_fastcgi before posting the thread.

Yeah - just something like this:

root * /var/www/wp1
php_fastcgi php-fpm:9000
file_server

Assumes you have a /var/www/wp1 volume shared with the Caddy container and that your FPM image is named php-fpm.

Thank you for reading the docs :pray: :smiley:

And yes I wish more people read this page! It is very very helpful despite what people say when they complain about our docs.

Anyway, from the directive docs, this part seems relevant:

Upstream addresses can take the form of a conventional Caddy network address or a URL that contains only scheme and host/port

And one of the examples shows example.com as a valid upstream, though now I wonder if that’s still true, and this is maybe leftover from a very early iteration of the reverse_proxy directive, when upstream address parsing was more naive. It could simply be a problem with our docs at this point. I am not sure it is a good idea for Caddy to assume a port to a backend. So I think it’s actually working as intended, just our docs need to be fixed there. Oops.

1 Like

I remember that somewhere in the github repo there used to be something like “code examples”, but that may have been on the v1 Docker repo. It’s possible that exists, and I just missed it, but a list of snippets for common apps with known-good docker-compose bits and the corresponding Caddyfile bits might be helpful. I have no complaints regarding the docs, but my brain sometimes does better adapting known-working configs than going from a generic example to a specific container/app.

My impression was that Docker was less official in v1. Now that the images live in an official Caddy repo on Docker hub, maybe a generic docker-compose.yml on the main page with links to example configs on GitHub? It’s sure helpful to me when the hub pages (like the official Wordpress) have a vanilla docker-compose that can get up and running in 90 seconds.

Y’all are doing great. Your work and responsiveness is appreciated.

Ah, that’s exactly what our wiki is for. We link to it on every page here in the forum and from our main docs nav as well.

Correct! We had no official Docker image in v1. But now we do. I’ll let @francislavoie and @hairyhenderson respond to your suggestion. Thanks!

Ha! Fair enough. I’m on it. I guess if my previous configuration hadn’t been stable for 2 years I would have been paying more attention regularly. Break my sites more often*, and I’ll keep up better. Geez. :grin:

*Caddy didn’t in any way break my sites, but that ruins the hyperbole.

1 Like

Yeah, a docker-compose example in the Docker config is definitely on my TODO list. Haven’t gotten around to that yet because the docs are on the docker-library repo which is a bit more tedious to make changes to than the caddy-docker repo.

Not sure why I still can’t get Wordpress FPM to work.

Temporary URL

EXPIRED

Basing my Caddyfile on:

And Matt’s Wiki - Example: WordPress:

Current Caddyfile:

{
	debug
}

{$FORUM_TEST_1}, {$FORUM_TEST_2}, {$FORUM_TEST_3} {
    reverse_proxy unix//sock/nginx.http.sock
    tls {
        dns cloudflare {$CLOUDFLARE_API_TOKEN}
    }
}

{$SITE_1} {
    root * /var/www/{$WP_1}/
    # php_fastcgi unix//run/php/php7.4-fpm.sock
    php_fastcgi {$DOCKER_HOST_1}:9000 
    file_server
    # reverse_proxy {$DOCKER_HOST_1}:{$WP_PORT}
    tls {
        dns cloudflare {$CLOUDFLARE_API_TOKEN}
    }
}

{$SITE_2} {
    reverse_proxy {$DOCKER_HOST_2}:{$WP_PORT}
    tls {
        dns cloudflare {$CLOUDFLARE_API_TOKEN}
    }
}

The 3 Discourse forum tests at the top work. Site 2 works (Wordpress Apache). But Site 1 is a blank page.

Unlike the others, Site 1 doesn’t show anything in the terminal when I try to load the page. It’s running according to Docker:

wp1 | [24-Jul-2020 06:01:35] NOTICE: fpm is running, pid 1
wp1 | [24-Jul-2020 06:01:35] NOTICE: ready to handle connections

And it’s showing up with port 9000 after docker ps

CONTAINER ID        IMAGE                               COMMAND                  CREATED              STATUS              PORTS
da3b802c1ed8        wordpress:5.4.2-php7.4-fpm-alpine   "docker-entrypoint.s…"   About a minute ago   Up About a minute   9000/tcp
docker-compose.yml
version: '3.3'

services:
  caddy:
    env_file:
      - .env
    container_name: caddy
    build: ./caddy
    ports:
      - "80:80"
      - "443:443"
      - "80:80/udp"
      - "443:443/udp"
    volumes:
      - ${DISCOURSE_PATH}/shared/web-only:/sock
      - ${FARM_PATH}/caddy/Caddyfile:/etc/caddy/Caddyfile
      - ${FARM_PATH}/var/caddy:/root/.caddy
      - ${FARM_PATH}/caddy/data:/data
      - ${FARM_PATH}/caddy/config:/config
    restart: always

  wp1:
    image: $WP_ALPINE
    # build:
    #   context: .
    #   dockerfile: Dockerfile-alternate
    container_name: ${WP_1}-wp
    # networks:
    #   - $DEFAULT_NETWORK
    # ports:
    #   - "9000:9000"
    hostname: $SITE_1
    depends_on:
      - db1
    restart: always
    environment:
      WORDPRESS_DB_HOST: ${WP_1}-db
      WORDPRESS_DB_USER: $DB_USER
      WORDPRESS_DB_PASSWORD: $DB_PASSWORD
      WORDPRESS_DB_NAME: $DB_NAME
    volumes:
      - ${WWW_PATH}/${WP_1}:/var/www/html

  db1:
    image: $DB_IMAGE
    container_name: ${WP_1}-db
    # networks:
    #   - $DEFAULT_NETWORK
    restart: always
    environment:
      MYSQL_DATABASE: $DB_NAME
      MYSQL_USER: $DB_USER
      MYSQL_PASSWORD: $DB_PASSWORD
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    volumes:
      - ${FARM_PATH}/mysql/${WP_1}:/var/lib/mysql
  wp2:
    image: $WP_PHP
    # build:
    #   context: .
    #   dockerfile: Dockerfile-alternate
    container_name: ${WP_2}-wp
    # networks:
    #   - $DEFAULT_NETWORK
    # ports:
    #   - "9000:9000"
    hostname: $SITE_2
    depends_on:
      - db1
    restart: always
    environment:
      WORDPRESS_DB_HOST: ${WP_2}-db
      WORDPRESS_DB_USER: $DB_USER
      WORDPRESS_DB_PASSWORD: $DB_PASSWORD
      WORDPRESS_DB_NAME: $DB_NAME
    volumes:
      - ${WWW_PATH}/${WP_2}:/var/www/html

  db2:
    image: $DB_IMAGE
    container_name: ${WP_2}-db
    # networks:
    #   - $DEFAULT_NETWORK
    restart: always
    environment:
      MYSQL_DATABASE: $DB_NAME
      MYSQL_USER: $DB_USER
      MYSQL_PASSWORD: $DB_PASSWORD
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    volumes:
      - ${FARM_PATH}/mysql/${WP_2}:/var/lib/mysql

If I change the standard Wordpress image in docker-compose.yml and set it to reverse-proxy, Site 1 works again.

Hm. Through some voodoo, Caddy did get hit once and I got some info back. I cleared the Cloudflare cache around the same time, but I can’t reproduce this error – it’s still returning a blank page.

Terminal output
caddy    | {"level":"debug","ts":1595571390.4981623,"logger":"http.reverse_proxy.transport.fastcgi","msg":"roundtrip","request":{"method":"GET","uri":"/wp-admin/install.php","proto":"HTTP/1.1","remote_addr": ***REDACTED*** ,"SERVER_PORT":"","SERVER_PROTOCOL":"HTTP/1.1","SERVER_SOFTWARE":"Caddy/v2.1.1","SSL_CIPHER":"TLS_CHACHA20_POLY1305_SHA256","SSL_PROTOCOL":"TLSv1.3"}}


wp1 | 192.168.48.3 -  24/Jul/2020:06:16:30 +0000 "GET /wp-admin/install.php" 404

caddy    | {"level":"debug","ts":1595571390.5104325,"logger":"http.handlers.reverse_proxy","msg":"upstream roundtrip","upstream":"wp1:9000","request":{"method":"GET","uri":"/wp-admin/install.php","proto":"HTTP/1.1","remote_addr": ***REDACTED*** ,"duration":0.036427617,"headers":{"Status":["404 Not Found"],"X-Powered-By":["PHP/7.4.8"],"Content-Type":["text/html; charset=UTF-8"]},"status":404}^CGracefully stopping... (press Ctrl+C again to force)

There’s a 404 in there on /wp-admin/install.php. That file is definitely there, so maybe it’s pointing at the wong thing? But the paths all look right in the output.

Caddy needs the /var/www/html volume as well to serve it if you use php-fpm. Caddy needs to be able to find the files to serve in the filesystem.

1 Like

Yahtzee!

Added this to Caddy volumes in docker-compose.yml

- ${WWW_PATH}/${WP_1}:/var/www/html

Changed Caddyfile line from:

    root * /var/www/{$WP_1}/

To:

    root * /var/www/html/

Everything looks like mush to my tiny brain after moving everything to .env files while migrating 4 forums, 2 Wordpress sites, and 4 Ghost blogs. :upside_down_face:

Thanks again!

2 Likes

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