Simplest possible Wordpress PHP-FPM setup

1. Output of caddy version:

v2.6.2 h1:wKoFIxpmOJLGl3QXoo6PNbYvGW4xLEgo32GPBEjWL8o=

2. How I run Caddy:

a. System environment:

Ubuntu 22.04.1 LTS with Docker

b. Command:

I’ve tried a number of things, all after running (I know it’s an old version of Wordpress on old PHP, but this is required for my specific use case, unfortunately):

docker build -t wordpress-test wordpress:5.7-php7.4-fpm-alpine
docker run -p 9000:9000 -e [insert working Wordpress database configuration here] wordpress-test

Caddy commands that I’ve tried are:

caddy reverse-proxy --from :8000 --to :9000
caddy run

c. Service/unit/compose file:

FROM wordpress:5.7-php7.4-fpm-alpine

I have this exact Wordpress site running live using wordpress:5.7 , and am using the exact same configuration here, so I’m fairly confident the problem is not with the way I’m configuring Wordpress.

d. My complete Caddy config:

http:// {
        php_fastcgi 127.0.0.1:9000
}

3. The problem I’m having:

Nothing that I’ve tried has resulted in a successful HTTP or HTTPS connection. I cannot get any response from Caddy or Wordpress when attempting to load the page. Aside from using PHP-FPM, everything is configured identically to a working Wordpress site that’s deployed using the standard Wordpress Apache Docker image. All I want to do is switch from Apache to Caddy/PHP-FPM, though at this point I’m considering using Nginx instead, just because I’ve got more experience with it.

4. Error messages and/or full log output:

When attempting to use reverse-proxy :

Server

❯ caddy reverse-proxy --from :8000 --to :9000
2022/12/08 18:34:42.304 WARN    admin   admin endpoint disabled
2022/12/08 18:34:42.305 INFO    tls.cache.maintenance   started background certificate maintenance      {"cache": "0xc000025420"}
2022/12/08 18:34:42.305 INFO    tls     cleaning storage unit   {"description": "FileStorage:/home/josho/.local/share/caddy"}
2022/12/08 18:34:42.305 INFO    tls     finished cleaning storage units
2022/12/08 18:34:42.305 INFO    autosaved config (load with --resume flag)      {"file": "/home/josho/.config/caddy/autosave.json"}
Caddy proxying http://:8000 -> :9000    
2022/12/08 18:34:45.756 ERROR   http.log.error  dial tcp :9000: connect: connection refused     {"request": {"remote_ip": "127.0.0.1", "remote_port": "41060", "proto": "HTTP/1.1", "method": "GET", "host": "localhost:8000", "uri": "/", "headers": {"User-Agent": ["curl/7.81.0"], "Accept": ["*/*"]}}, "duration": 0.00060134, "status": 502, "err_id": "3niq9u6zt", "err_trace": "reverseproxy.statusError (reverseproxy.go:1196)"}

Client

❯ curl -v localhost:8000
*   Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.81.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 502 Bad Gateway
< Server: Caddy
< Date: Thu, 08 Dec 2022 18:34:45 GMT
< Content-Length: 0
< 
* Connection #0 to host localhost left intact

When attempting to use php_fastcgi:

Server


❯ sudo caddy run -config Caddyfile
2022/12/08 18:40:14.728 INFO    using provided configuration    {"config_file": "Caddyfile", "config_adapter": ""}
2022/12/08 18:40:14.729 WARN    caddyfile       Site block has an unspecified IP address which only matches requests having that Host header; you probably want the 'bind' directive    configure the socket {"address": "0.0.0.0"}
2022/12/08 18:40:14.730 INFO    admin   admin endpoint started  {"address": "tcp/localhost:2019", "enforce_origin": false, "origins": ["//localhost:2019", "//[::1]:2019", "//127.0.0.1:2019"]}
2022/12/08 18:40:14.730 WARN    http    server is listening only on the HTTP port, so no automatic HTTPS will be applied to this server      {"server_name": "srv0", "http_port": 80}
2022/12/08 18:40:14.731 INFO    tls.cache.maintenance   started background certificate maintenance      {"cache": "0xc00032bb20"}
2022/12/08 18:40:14.731 INFO    tls     cleaning storage unit   {"description": "FileStorage:/root/.local/share/caddy"}
2022/12/08 18:40:14.731 INFO    autosaved config (load with --resume flag)      {"file": "/root/.config/caddy/autosave.json"}
2022/12/08 18:40:14.731 INFO    serving initial configuration2022/12/08 18:40:14.732 INFO    tls     finished cleaning storage units

Client


❯ curl -v localhost
*   Trying 127.0.0.1:80...
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.81.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: Caddy
< Date: Thu, 08 Dec 2022 19:05:19 GMT
< Content-Length: 0
< 
* Connection #0 to host localhost left intact

5. What I already tried:

I’ve read through the documentation, and tried modifying my configuration based on various topics on this forum and elsewhere, but I suspect that I’m just missing some detail about how FPM and Caddy work together. I am a Python developer who’s inherited this Wordpress site, so other than having a lot of experience with general web development, I’m pretty out of my element with this.

The simplest config for most PHP apps looks like this:

example.com {
	root * /srv
	encode gzip
	php_fastcgi unix//run/php/php-8.1-fpm.sock
	file_server
}

(Using a unix socket here for PHP-FPM, but a TCP socket works fine too, e.g. localhost:9000)

You need to make the webroot of WordPress accessible to Caddy, so it can serve static files (CSS, JS, images, etc), and so Caddy can know what PHP files exist (it needs to know that index.php exists so it can tell PHP-FPM to run that script).

Oh. Duh… Looking back at some of the examples I found online, I can now see that they were mounting the same volume to /var/www/html to both the Caddy and Wordpress containers. I guess I’m just so used to simply serving static files through WSGI with Python apps (and/or using a CDN, which is what I’m doing for all uploads with Wordpress), that I somehow just forgot about that part of it.

I’ll give it a shot this way, thanks!

Ok, so now I’ve got it partially working… For now I’m just loading it all into a single Docker container. The new Dockerfile looks like this:

FROM caddy:2.6.2 as caddy
FROM wordpress:5.7-php7.4-fpm-alpine

COPY wp-content/plugins/ /var/www/html/wp-content/plugins/
COPY wp-content/themes/ /var/www/html/wp-content/themes/
COPY wp-config.php /var/www/html/wp-config.php

RUN mkdir /app
WORKDIR /app

COPY --from=caddy /usr/bin/caddy /usr/bin
COPY Caddyfile .
COPY run.sh .
RUN chmod 744 run.sh

CMD /app/run.sh

My Caddyfile looks like this:

:80 {
	root * /var/www/html
	php_fastcgi 127.0.0.1:9000
	file_server
    log
}

And the run.sh script just very simply starts php-fpm and caddy, like so:

#!/bin/bash

set -m
php-fpm &
caddy run &
fg %1

When I build and run the container, it successfully serves static files:

gitpod /workspace/ot-wordpress (php-fpm) $ curl -v localhost:8000/wp-content/themes/metaphor2010/license.txt | head
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying ::1:8000...
* TCP_NODELAY set
* Connected to localhost (::1) port 8000 (#0)
> GET /wp-content/themes/metaphor2010/license.txt HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 15129
< Content-Type: text/plain; charset=utf-8
< Etag: "rml1f7bo9"
< Last-Modified: Thu, 08 Dec 2022 17:18:43 GMT
< Server: Caddy
< Date: Fri, 09 Dec 2022 13:29:46 GMT
< 
{ [15129 bytes data]
100 15129  100              GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991
1
5 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 
1              51 Franklin St, Fifth Floor, Boston, MA 02110, USA
2
9 Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
 
                            Preamble
 0     0  7387k      0 --:--:-- --:--:-- --:--:-- 7387k
* Connection #0 to host localhost left intact
(23) Failed writing body

However, if I try and fetch the home page of the site, it 404s:

gitpod /workspace/ot-wordpress (php-fpm) $ curl -v localhost:8000
*   Trying ::1:8000...
* TCP_NODELAY set
* Connected to localhost (::1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 Not Found
< Server: Caddy
< Date: Fri, 09 Dec 2022 13:26:14 GMT
< Content-Length: 0
< 
* Connection #0 to host localhost left intact

Presumably this is because Caddy is attempting to serve the file directly, not passing it on to PHP. If I instead try to fetch a PHP file, it successfully passes that along to PHP-FPM, and returns a 500 error, as it should.

gitpod /workspace/ot-wordpress (php-fpm) $ curl -v localhost:8000/wp-content/themes/metaphor2010/404.php
*   Trying ::1:8000...
* TCP_NODELAY set
* Connected to localhost (::1) port 8000 (#0)
> GET /wp-content/themes/metaphor2010/404.php HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 500 Internal Server Error
< Content-Type: text/html; charset=UTF-8
< Server: Caddy
< Status: 500 Internal Server Error
< X-Powered-By: PHP/7.4.21
< Date: Fri, 09 Dec 2022 13:31:20 GMT
< Content-Length: 0
< 
* Connection #0 to host localhost left intact

If I were using Nginx for my reverse proxy, I’d think it was because I was failing to specify my index as index.php, so it was 404ing trying to serve index.html or something, but my understanding was that the php_fastcgi directive would handle all that for me.

I don’t think that’s a good way to do it. With this, both apps won’t receive signals like SIGTERM correctly so they won’t gracefully shut down. And I think you won’t be getting all the logs written out correctly.

You could use a plugin like GitHub - Baldinof/caddy-supervisor: Run and supervise background processes from Caddy which would have Caddy itself trigger running php-fpm, and it can forward signals to it when it is itself shutting down, etc.

I don’t see an index.php here? :thinking:

You can turn on the debug global option to see more details about what Caddy is doing. Add this at the top of your Caddyfile:

{
	debug
}

I know this isn’t the best way to manage the processes, I’m just trying to get Caddy to successfully work with PHP-FPM before I bother with anything else. This successfully gets Caddy and PHO-FPM running in the container together, so it’s good enough for testing.

It’s not necessary for me to add an index.php, because the Docker image already has a functional Wordpress install. I’m just adding in some plugins and other things. That part of it works just fine with the official Apache-served image, so I’m pretty confident it’s not the problem here.

I’ll turn on debug and see if that provides any useful information.

Ok, I got it working! Turns out the problem was that my run.sh script was running php-fpm directly, instead of using the Docker entrypoint script, which prevented Wordpress from getting set up properly.

The following configuration successfully serves a Wordpress site using PHP-FPM from within a single Docker container. It also successfully deploys and runs on Fly.io. Now that I’ve got it working, I’ll probably switch to caddy-supervisor, as it’s a much better solution than my bash script.

Caddyfile

:80 {
        root * /var/www/html
        php_fastcgi 127.0.0.1:9000
        file_server
        log
}

Dockerfile


FROM caddy:2.6.2 as caddy
FROM wordpress:5.7-php7.4-fpm-alpine

COPY wp-content/plugins/ /var/www/html/wp-content/plugins/
COPY wp-content/themes/ /var/www/html/wp-content/themes/
COPY wp-config.php /var/www/html/wp-config.php

COPY --from=caddy /usr/bin/caddy /usr/bin
COPY Caddyfile .
COPY run.sh .
RUN chmod 744 run.sh

CMD /var/www/html/run.sh

run.sh


#!/bin/bash

set -m
/usr/local/bin/docker-entrypoint.sh php-fpm &
caddy run &
fg %1
1 Like

A much simpler, better configuration using caddy-supervisor!

Caddyfile

{
        supervisor {
                /usr/local/bin/docker-entrypoint.sh php-fpm {
                        dir /var/www/html
                }
        }
}

:80 {
        root * /var/www/html
        php_fastcgi 127.0.0.1:9000
        file_server
        log
}

Dockerfile

FROM caddy:2.6.2-builder AS builder
RUN xcaddy build --with github.com/baldinof/caddy-supervisor@v0.6.0

FROM wordpress:5.7-php7.4-fpm-alpine

COPY wp-content/plugins/ /var/www/html/wp-content/plugins/
COPY wp-content/themes/ /var/www/html/wp-content/themes/
COPY wp-config.php /var/www/html/wp-config.php

COPY --from=builder /usr/bin/caddy /usr/bin/caddy
COPY Caddyfile /usr/local/etc/caddy/Caddyfile

CMD caddy run --config /usr/local/etc/caddy/Caddyfile
1 Like

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