1. The problem I’m having:
I want to create a single container with Caddy and PHP-FPM. To do so I’ve created a Docker image based on ‘php:8.3.2-fpm-alpine’ with Caddy installed as dependency. To keep PHP-FPM up and running I rebuilt Caddy in order to use Module supervisor instead of using SupervisorD.
With this approach the domain https://demo.localhost
is signed and properly accessed from inside the container but cannot be accessed from the host.
2. Error messages and/or full log output:
[+] Running 2/1
✔ Network microservice-caddy-php-v2_default Created 0.1s
✔ Container app Created 0.0s
Attaching to app
app | {"level":"info","ts":1707931901.4808557,"msg":"using provided configuration","config_file":"/etc/caddy/Caddyfile","config_adapter":"caddyfile"}
app | {"level":"info","ts":1707931901.482843,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
app | {"level":"info","ts":1707931901.4831343,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc00039fc00"}
app | {"level":"info","ts":1707931901.4859724,"logger":"http.auto_https","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443}
app | {"level":"info","ts":1707931901.4859855,"logger":"http.auto_https","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
app | {"level":"warn","ts":1707931901.4859886,"logger":"http.auto_https","msg":"server is listening only on the HTTP port, so no automatic HTTPS will be applied to this server","server_name":"srv1","http_port":80}
app | {"level":"warn","ts":1707931901.5037982,"logger":"pki.ca.local","msg":"installing root certificate (you might be prompted for password)","path":"storage:pki/authorities/local/root.crt"}
app | {"level":"info","ts":1707931901.505052,"msg":"define JAVA_HOME environment variable to use the Java trust"}
app | {"level":"info","ts":1707931901.5050712,"msg":"not NSS security databases found"}
app | {"level":"info","ts":1707931901.529142,"msg":"certificate installed properly in linux trusts"}
app | {"level":"info","ts":1707931901.5295074,"logger":"http","msg":"enabling HTTP/3 listener","addr":":443"}
app | {"level":"info","ts":1707931901.529732,"msg":"failed to sufficiently increase receive buffer size (was: 208 kiB, wanted: 2048 kiB, got: 416 kiB). See https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes for details."}
app | {"level":"info","ts":1707931901.5305398,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
app | {"level":"info","ts":1707931901.5305696,"logger":"http.log","msg":"server running","name":"srv1","protocols":["h1","h2","h3"]}
app | {"level":"info","ts":1707931901.5305738,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["demo.localhost"]}
app | {"level":"info","ts":1707931901.530703,"logger":"tls","msg":"cleaning storage unit","storage":"FileStorage:/root/.local/share/caddy"}
app | {"level":"info","ts":1707931901.53072,"msg":"autosaved config (load with --resume flag)","file":"/root/.config/caddy/autosave.json"}
app | {"level":"info","ts":1707931901.530726,"msg":"serving initial configuration"}
app | {"level":"info","ts":1707931901.5308032,"logger":"tls.obtain","msg":"acquiring lock","identifier":"demo.localhost"}
app | {"level":"info","ts":1707931901.530809,"logger":"tls","msg":"finished cleaning storage units"}
app | {"level":"info","ts":1707931901.530929,"logger":"supervisor","msg":"process started","command":["php-fpm","--nodaemonize"],"replica":0,"pid":23}
app | {"level":"info","ts":1707931901.531587,"logger":"tls.obtain","msg":"lock acquired","identifier":"demo.localhost"}
app | {"level":"info","ts":1707931901.5316172,"logger":"tls.obtain","msg":"obtaining certificate","identifier":"demo.localhost"}
app | {"level":"info","ts":1707931901.5326047,"logger":"tls.obtain","msg":"certificate obtained successfully","identifier":"demo.localhost"}
app | {"level":"info","ts":1707931901.5326388,"logger":"tls.obtain","msg":"releasing lock","identifier":"demo.localhost"}
app | {"level":"warn","ts":1707931901.5328276,"logger":"tls","msg":"stapling OCSP","error":"no OCSP stapling for [demo.localhost]: no OCSP server specified in certificate","identifiers":["demo.localhost"]}
app | [14-Feb-2024 17:31:41] NOTICE: fpm is running, pid 23
app | [14-Feb-2024 17:31:41] NOTICE: ready to handle connections
Accessing the domain from Inside the container
$ docker exec -it app curl https://demo.localhost
OK
The
index.php
file just print out an ‘OK’ so this output is the expected response.
Accessing the domain from the host
$ curl https://demo.localhost
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
3. Caddy version:
$ docker exec -it app caddy --version
v2.7.6 h1:w0NymbG2m9PcvKWsrXO6EEkY9Ru4FJK8uQbYcev1p3A=
4. How I installed and ran Caddy:
Dockerfile
# syntax=docker/dockerfile:1
# STAGE #########################################################
FROM caddy:2.7.6-builder-alpine as caddy-builder
RUN xcaddy build \
--with github.com/baldinof/caddy-supervisor
# STAGE #########################################################
FROM php:8.3.2-fpm-alpine as base
RUN apk update && apk add --no-cache \
caddy \
fcgi \
nss-tools
COPY --from=caddy-builder /usr/bin/caddy /usr/sbin/caddy
WORKDIR /code
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
# STAGE #########################################################
FROM php:8.3.2-fpm-alpine as build-development-extensions
RUN curl -sSL https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions -o - | sh -s \
pcov \
uopz
# STAGE #########################################################
FROM base as build-development
ARG HOST_USER_ID=1000
ARG HOST_USER_NAME=host-user-name
ARG HOST_GROUP_ID=1000
ARG HOST_GROUP_NAME=host-group-name
ENV ENV=DEVELOPMENT
COPY --from=composer /usr/bin/composer /usr/bin/composer
COPY --from=build-development-extensions /usr/local/lib/php/extensions/*/* /usr/local/lib/php/extensions/no-debug-non-zts-20230831
COPY --from=build-development-extensions /usr/local/etc/php/conf.d/* /usr/local/etc/php/conf.d/
RUN apk update && apk add --no-cache \
bash \
util-linux
RUN addgroup --gid ${HOST_GROUP_ID} ${HOST_GROUP_NAME} \
&& adduser --shell /bin/bash --uid ${HOST_USER_ID} --ingroup ${HOST_GROUP_NAME} --ingroup www-data --disabled-password --gecos '' ${HOST_USER_NAME}
COPY ./build/www.conf /usr/local/etc/php-fpm.d/www.conf
RUN sed -i -r "s/USER-NAME/${HOST_USER_NAME}/g" /usr/local/etc/php-fpm.d/www.conf \
&& sed -i -r "s/GROUP-NAME/${HOST_GROUP_NAME}/g" /usr/local/etc/php-fpm.d/www.conf
a. System environment:
- Laptop: Asus ZenBook 13
- CPU: 11th Gen Intel(R) Core™ i7-1165G7 @ 2.80GHz
- RAM: 16Gb
- OS: Linux ZenBook-UX325EA 6.6.0-14-generic #14-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 30 10:27:29 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
- Docker version: 24.0.7, build 24.0.7-0ubuntu1
b. Command:
Build the image
$ docker-compose build --build-arg="HOST_USER_NAME=$(id --user --name)" --build-arg="HOST_GROUP_NAME=$(id --user --name)"
Start the service
$ docker-compose up --remove-orphans
c. Service/unit/compose file:
docker-compose.yml
version: '3.9'
volumes:
caddy_data:
caddy_config:
services:
app:
container_name: app
build:
context: .
dockerfile: Dockerfile
target: build-development
restart: unless-stopped
volumes:
- ./build/Caddyfile:/etc/caddy/Caddyfile
- ./src:/code
- caddy_data:/data
- caddy_config:/config
ports:
- 80:80
- 443:443
- 443:443/udp
healthcheck:
test: ["CMD", "wget", "--tries=1", "--spider", "https://demo.localhost/metrics"]
interval: 10s
timeout: 1s
retries: 3
d. My complete Caddy config:
{
supervisor {
php-fpm --nodaemonize {
env DEBUG false
redirect_stdout stdout
redirect_stderr stdout
restart_policy on_failure
}
}
}
:80 {
respond "Hi!"
}
demo.localhost {
metrics /metrics
tls internal
encode zstd gzip
root * /code/public
php_fastcgi :9000
file_server
}
e. The PHP-FPM config file
[www]
user=USER-NAME
group=GROUP-NAME
listen=:9000
ping.path=/status/ping
pm=dynamic
pm.max_children=300
pm.max_requests=10240
pm.max_spare_servers=35
pm.min_spare_servers=5
pm.process_idle_timeout=0s;
pm.start_servers=20
pm.status_path=/status/php-fpm
request_slowlog_timeout=5
request_terminate_timeout=1200
rlimit_files=65535
slowlog=/var/log/$pool.log.slow
5. Links to relevant resources:
If I check the path where the certificates are supposed to be generated, that path is empty:
$ docker exec -it app ls -la /data/
total 8
drwxr-xr-x 2 root root 4096 Feb 14 16:07 .
drwxr-xr-x 1 root root 4096 Feb 14 18:12 ..