1. The problem I’m having:
I’m trying to use Caddy with non-root privileges in a Docker-Environment, which lead to some permission problems. Some of them I already fixed. The current issue I’m trying so solve is the error, which can be seen in the log-output below. So I have two questions: Firstly, where is the corresponding section of code, which produces this error, so that I can debug the error and maybe following errors. And secondly, what could I try to do, to resolve the current error (but firstly is more important to me).
My Current Setup:
I’m using a modified symfony-docker template, which uses in turn frankenphp. However this is by default starting Caddy as root, which I changed.
Maybe I should not reach to the caddy community, but rather to the frankenphp community. But I feel somehow, that I’ll find a solution to the current problem rather here. If I’m wrong, please tell me.
2. Error messages and/or full log output:
The docker container runs a version of debian 12, which seems not to have systemd installed, or at least no journalctl command. So the following is the output of the docker log.
Excerpt of Log with Error:
ERROR pki.ca.local failed to install root certificate {"error": "failed to execute tee: exit status 1", "certificate_file": "storage:pki/authorities/local/root.crt"}
Rest of Log
dashboard_php_db | 2025-01-03 11:16:19+00:00 [Note] [Entrypoint]: MariaDB upgrade not required
dashboard_php_db | 2025-01-03 11:16:19 0 [Note] Starting MariaDB 11.6.1-MariaDB-ubu2404 source revision 05fe3f1c186a221c4455b4d83a9d59f09f2dfadb server_uid Vd/ZhQ9/1UDIkuJiKG/mWrlg94k= as process 1
dashboard_php_db | 2025-01-03 11:16:19 0 [Note] InnoDB: Compressed tables use zlib 1.3
dashboard_php_db | 2025-01-03 11:16:19 0 [Note] InnoDB: Number of transaction pools: 1
dashboard_php_db | 2025-01-03 11:16:19 0 [Note] InnoDB: Using crc32 + pclmulqdq instructions
dashboard_php_db | 2025-01-03 11:16:19 0 [Warning] mariadbd: io_uring_queue_init() failed with errno 0
dashboard_php_db | 2025-01-03 11:16:19 0 [Warning] InnoDB: liburing disabled: falling back to innodb_use_native_aio=OFF
dashboard_php_db | 2025-01-03 11:16:19 0 [Note] InnoDB: Initializing buffer pool, total size = 128.000MiB, chunk size = 2.000MiB
dashboard_php_db | 2025-01-03 11:16:19 0 [Note] InnoDB: Completed initialization of buffer pool
dashboard_php_db | 2025-01-03 11:16:19 0 [Note] InnoDB: File system buffers for log disabled (block size=512 bytes)
dashboard_php_db | 2025-01-03 11:16:19 0 [Note] InnoDB: End of log at LSN=231419
dashboard_php_db | 2025-01-03 11:16:19 0 [Note] InnoDB: Opened 3 undo tablespaces
dashboard_php_db | 2025-01-03 11:16:19 0 [Note] InnoDB: 128 rollback segments in 3 undo tablespaces are active.
dashboard_php_db | 2025-01-03 11:16:19 0 [Note] InnoDB: Setting file './ibtmp1' size to 12.000MiB. Physically writing the file full; Please wait ...
dashboard_php_db | 2025-01-03 11:16:19 0 [Note] InnoDB: File './ibtmp1' size is now 12.000MiB.
dashboard_php_db | 2025-01-03 11:16:19 0 [Note] InnoDB: log sequence number 231419; transaction id 231
dashboard_php_db | 2025-01-03 11:16:19 0 [Note] Plugin 'FEEDBACK' is disabled.
dashboard_php_db | 2025-01-03 11:16:19 0 [Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool
dashboard_php_db | 2025-01-03 11:16:19 0 [Note] Plugin 'wsrep-provider' is disabled.
dashboard_php_db | 2025-01-03 11:16:19 0 [Note] InnoDB: Buffer pool(s) load completed at 250103 11:16:19
dashboard_php_app | 2025/01/03 11:16:21.795 INFO using config from file {"file": "/etc/caddy/Caddyfile"}
dashboard_php_app | 2025/01/03 11:16:21.799 INFO adapted config to JSON {"adapter": "caddyfile"}
dashboard_php_app | 2025/01/03 11:16:21.799 WARN Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies {"adapter": "caddyfile", "file": "/etc/caddy/Caddyfile", "line": 12}
dashboard_php_app | 2025/01/03 11:16:21.802 INFO admin admin endpoint started {"address": "localhost:2019", "enforce_origin": false, "origins": ["//[::1]:2019", "//127.0.0.1:2019", "//localhost:2019"]}
dashboard_php_app | 2025/01/03 11:16:21.803 INFO http.auto_https 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}
dashboard_php_app | 2025/01/03 11:16:21.803 INFO http.auto_https enabling automatic HTTP->HTTPS redirects {"server_name": "srv0"}
dashboard_php_app | 2025/01/03 11:16:21.803 WARN http.auto_https server is listening only on the HTTP port, so no automatic HTTPS will be applied to this server {"server_name": "srv1", "http_port": 80}
dashboard_php_app | 2025/01/03 11:16:21.803 INFO tls.cache.maintenance started background certificate maintenance {"cache": "0xc00078ef80"}
dashboard_php_app | 2025/01/03 11:16:21.804 WARN http.handlers.mercure Setting the transport_url or the MERCURE_TRANSPORT_URL environment variable is deprecated, use the "transport" directive instead
dashboard_php_app | 2025/01/03 11:16:21.804 WARN http.handlers.mercure Setting the transport_url or the MERCURE_TRANSPORT_URL environment variable is deprecated, use the "transport" directive instead
dashboard_php_app | 2025/01/03 11:16:21.805 WARN http.handlers.mercure Setting the transport_url or the MERCURE_TRANSPORT_URL environment variable is deprecated, use the "transport" directive instead
dashboard_php_app | 2025/01/03 11:16:21.806 WARN http.handlers.mercure Setting the transport_url or the MERCURE_TRANSPORT_URL environment variable is deprecated, use the "transport" directive instead
dashboard_php_app | 2025/01/03 11:16:21.807 WARN http.handlers.mercure Setting the transport_url or the MERCURE_TRANSPORT_URL environment variable is deprecated, use the "transport" directive instead
dashboard_php_app | 2025/01/03 11:16:21.808 WARN http.handlers.mercure Setting the transport_url or the MERCURE_TRANSPORT_URL environment variable is deprecated, use the "transport" directive instead
dashboard_php_app | 2025/01/03 11:16:21.808 WARN pki.ca.local installing root certificate (you might be prompted for password) {"path": "storage:pki/authorities/local/root.crt"}
dashboard_php_app | 2025/01/03 11:16:21.809 INFO warning: "certutil" is not available, install "certutil" with "apt install libnss3-tools" or "yum install nss-tools" and try again
dashboard_php_app | 2025/01/03 11:16:21.809 INFO define JAVA_HOME environment variable to use the Java trust
dashboard_php_app | 2025/01/03 11:16:21.811 ERROR pki.ca.local failed to install root certificate {"error": "failed to execute tee: exit status 1", "certificate_file": "storage:pki/authorities/local/root.crt"}
dashboard_php_app | 2025/01/03 11:16:21.811 INFO http.log server running {"name": "srv1", "protocols": ["h1", "h2", "h3"]}
dashboard_php_app | 2025/01/03 11:16:21.812 INFO http enabling HTTP/3 listener {"addr": ":443"}
dashboard_php_app | 2025/01/03 11:16:21.812 INFO failed to sufficiently increase receive buffer size (was: 208 kiB, wanted: 7168 kiB, got: 416 kiB). See https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes for details.
dashboard_php_app | 2025/01/03 11:16:21.812 INFO http.log server running {"name": "srv0", "protocols": ["h1", "h2", "h3"]}
dashboard_php_app | 2025/01/03 11:16:21.812 INFO http enabling automatic TLS certificate management {"domains": ["localhost"]}
dashboard_php_app | 2025/01/03 11:16:21.812 WARN tls stapling OCSP {"error": "no OCSP stapling for [localhost]: no OCSP server specified in certificate", "identifiers": ["localhost"]}
dashboard_php_app | 2025/01/03 11:16:21.813 INFO tls storage cleaning happened too recently; skipping for now {"storage": "FileStorage:/data/caddy", "instance": "f721fee5-453d-4477-8932-b28bd2318b74", "try_again": "2025/01/04 11:16:21.813", "try_again_in": 86399.99999964}
dashboard_php_app | 2025/01/03 11:16:21.813 INFO tls finished cleaning storage units
dashboard_php_app | 2025/01/03 11:16:21.834 INFO FrankenPHP started 🐘 {"php_version": "8.3.15", "num_threads": 8}
dashboard_php_app | 2025/01/03 11:16:21.835 INFO autosaved config (load with --resume flag) {"file": "/config/caddy/autosave.json"}
dashboard_php_app | 2025/01/03 11:16:21.835 INFO serving initial configuration
dashboard_php_app | 2025/01/03 11:16:21.838 INFO watcher watching config file for changes {"config_file": "/etc/caddy/Caddyfile"}
3. Caddy version:
I didn’t find out, where caddy was installed in the docker container. However the output of frankenphp --version
is:
FrankenPHP v1.3.6 PHP 8.3.15 Caddy v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=
4. How I installed and ran Caddy:
a. System environment:
The content of /etc/os-release
is:
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
b. Command:
I have to admit, that I do not know exactly how caddy is run by frankenphp under the hood.
c. Service/unit/compose file:
compose.yaml
services:
php:
container_name: $DASHBOARD_PHP_APP_CONTAINER_NAME
image: $DOCKER_IMAGE_REPOSITORY/php:$APP_VERSION
restart: unless-stopped
environment:
SERVER_NAME: ${SERVER_NAME:-localhost}, php:80
MERCURE_PUBLISHER_JWT_KEY: ${CADDY_MERCURE_JWT_SECRET:-!...!}
MERCURE_SUBSCRIBER_JWT_KEY: ${CADDY_MERCURE_JWT_SECRET:-!...!}
# Run "composer require symfony/orm-pack" to install and configure Doctrine ORM
DATABASE_URL: $MARIADB_URL
# Run "composer require symfony/mercure-bundle" to install and configure the Mercure integration
MERCURE_URL: ${CADDY_MERCURE_URL:-http://php/.well-known/mercure}
MERCURE_PUBLIC_URL: ${CADDY_MERCURE_PUBLIC_URL:-https://${SERVER_NAME:-localhost}/.well-known/mercure}
MERCURE_JWT_SECRET: ${CADDY_MERCURE_JWT_SECRET:-!...!}
# The two next lines can be removed after initial installation
SYMFONY_VERSION: ${SYMFONY_VERSION:-}
STABILITY: ${STABILITY:-stable}
volumes:
- ./volumes/caddy/data:/data
- ./volumes/caddy/config:/config
- ./volumes/var:/app/var
- ./frankenphp/docker-entrypoint.sh:/usr/local/bin/docker-entrypoint
- ./proxychains4.conf:/etc/proxychains4.conf
#- caddy_data:./caddy/data
#- caddy_config:./caddy/config
ports:
# HTTP
- target: 80
published: ${HTTP_PORT:-80}
protocol: tcp
# HTTPS
- target: 443
published: ${HTTPS_PORT:-443}
protocol: tcp
# HTTP/3
- target: 443
published: ${HTTP3_PORT:-443}
protocol: udp
# Websocket
- "3001:3001"
compose.override.yaml
services:
php:
build:
context: .
target: frankenphp_dev
args:
MANAGER_UID: $MANAGER_UID
MANAGER_GID: $MANAGER_GID
volumes:
- ./:/app
- ./frankenphp/Caddyfile:/etc/caddy/Caddyfile:ro
- ./frankenphp/conf.d/20-app.dev.ini:/usr/local/etc/php/app.conf.d/20-app.dev.ini:ro
# If you develop on Mac or Windows you can remove the vendor/ directory
# from the bind-mount for better performance by enabling the next line:
#- /app/vendor
environment:
MERCURE_EXTRA_DIRECTIVES: demo
# See https://xdebug.org/docs/all_settings#mode
XDEBUG_MODE: "${XDEBUG_MODE:-off}"
extra_hosts:
# Ensure that host.docker.internal is correctly defined on Linux
- host.docker.internal:host-gateway
tty: true
Dockerfile
#syntax=docker/dockerfile:1.4
# Versions
FROM dunglas/frankenphp:1-php8.3 AS frankenphp_upstream
# The different stages of this Dockerfile are meant to be built into separate images
# https://docs.docker.com/develop/develop-images/multistage-build/#stop-at-a-specific-build-stage
# https://docs.docker.com/compose/compose-file/#target
# Base FrankenPHP image
FROM frankenphp_upstream AS frankenphp_base
WORKDIR /app
# persistent / runtime deps
# hadolint ignore=DL3008
RUN apt-get update && apt-get install -y --no-install-recommends \
acl \
file \
gettext \
git \
proxychains4 \
&& rm -rf /var/lib/apt/lists/*
# supported extensions:
# https://github.com/mlocati/docker-php-extension-installer?tab=readme-ov-file#supported-php-extensions
RUN set -eux; \
install-php-extensions \
@composer \
apcu \
intl \
opcache \
zip \
pcntl \
pdo_mysql \
ds \
ldap \
;
ENV PHP_INI_SCAN_DIR=":$PHP_INI_DIR/app.conf.d"
###> recipes ###
###> doctrine/doctrine-bundle ###
RUN install-php-extensions pdo_pgsql
###< doctrine/doctrine-bundle ###
###< recipes ###
COPY --link frankenphp/conf.d/10-app.ini $PHP_INI_DIR/app.conf.d/
COPY --link --chmod=755 frankenphp/docker-entrypoint.sh /usr/local/bin/docker-entrypoint
COPY --link frankenphp/Caddyfile /etc/caddy/Caddyfile
ENTRYPOINT ["docker-entrypoint"]
HEALTHCHECK --start-period=60s CMD curl -f http://localhost:2019/metrics || exit 1
CMD [ "frankenphp", "run", "--config", "/etc/caddy/Caddyfile" ]
# Dev FrankenPHP image
FROM frankenphp_base AS frankenphp_dev
ARG MANAGER_UID
ARG MANAGER_GID
ENV APP_ENV=dev XDEBUG_MODE=off
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
RUN set -eux; \
install-php-extensions \
xdebug \
;
COPY --link frankenphp/conf.d/20-app.dev.ini $PHP_INI_DIR/app.conf.d/
#RUN groupadd -g $manager_gid manager; \
# useradd -u $manager_uid -g $manager_gid manager;
RUN groupmod -g $MANAGER_GID www-data; \
usermod -u $MANAGER_UID -g $MANAGER_GID www-data;
RUN chown -R www-data:www-data /data
USER www-data
CMD [ "frankenphp", "run", "--config", "/etc/caddy/Caddyfile", "--watch" ]
# Prod FrankenPHP image
# https://getcomposer.org/doc/03-cli.md#composer-allow-superuser
# ENV COMPOSER_ALLOW_SUPERUSER=1
FROM frankenphp_base AS frankenphp_prod
ENV APP_ENV=prod
ENV FRANKENPHP_CONFIG="import worker.Caddyfile"
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY --link frankenphp/conf.d/20-app.prod.ini $PHP_INI_DIR/app.conf.d/
COPY --link frankenphp/worker.Caddyfile /etc/caddy/worker.Caddyfile
# prevent the reinstallation of vendors at every changes in the source code
COPY --link composer.* symfony.* ./
RUN set -eux; \
composer install --no-cache --prefer-dist --no-dev --no-autoloader --no-scripts --no-progress
# copy sources
COPY --link . ./
RUN rm -Rf frankenphp/
VOLUME /app/var/
RUN set -eux; \
mkdir -p var/cache var/log; \
composer dump-autoload --classmap-authoritative --no-dev; \
composer dump-env prod; \
composer run-script --no-dev post-install-cmd; \
chmod +x bin/console; sync;
d. My complete Caddy config:
Caddyfile
{
{$CADDY_GLOBAL_OPTIONS}
frankenphp {
{$FRANKENPHP_CONFIG}
}
}
{$CADDY_EXTRA_CONFIG}
(symfony) {
root * /app/public
encode zstd br gzip
mercure {
# Transport to use (default to Bolt)
transport_url {$MERCURE_TRANSPORT_URL:bolt:///data/mercure.db}
# Publisher JWT key
publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} {env.MERCURE_PUBLISHER_JWT_ALG}
# Subscriber JWT key
subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY} {env.MERCURE_SUBSCRIBER_JWT_ALG}
# Allow anonymous subscribers (double-check that it's what you want)
anonymous
# Enable the subscription API (double-check that it's what you want)
subscriptions
# Extra directives
{$MERCURE_EXTRA_DIRECTIVES}
}
vulcain
{$CADDY_SERVER_EXTRA_DIRECTIVES}
# Disable Topics tracking if not enabled explicitly: https://github.com/jkarlin/topics
header ?Permissions-Policy "browsing-topics=()"
php_server
}
{$SERVER_NAME:localhost} {
log {
# Redact the authorization query parameter that can be set by Mercure
format filter {
request>uri query {
replace authorization REDACTED
}
}
}
root /app/frontend/WebContent
encode zstd br gzip
file_server
# Reverse Proxy to the Websocket
reverse_proxy /RADDASH/* localhost:1003
# Backend API
handle_path /api/* {
import symfony
}
# Log viewer bundle
handle_path /log-viewer/* {
import symfony
}
# assets for bundles, e.g. the log-viewer
handle /bundles/* {
import symfony
}
}
worker.Caddyfile
worker {
file ./public/index.php
env APP_RUNTIME Runtime\FrankenPhpSymfony\Runtime
}