1. The problem I’m having:
Hello everyone, I’m trying to build a symfony app with caddy, frankenphp and docker. I can get caddy to work but I can’t get it to communicate with php. I tried a lot of things but none of them work, maybe you will see errors.
2. Error messages and/or full log output:
Here the error from the container caddy when i’m trying to access to https://localhost
{"level":"error","ts":1715438268.3024645,"logger":"http.log.error","msg":"dialing backend: dial tcp 172.26.0.6:9000: connect: connection refused","request":{"remote_ip":"172.26.0.1","remote_port":"58090","client_ip":"172.26.0.1","proto":"HTTP/2.0","method":"GET","host":"localhost","uri":"/","headers":{"Cache-Control":["max-age=0"],"Sec-Ch-Ua":["\"Microsoft Edge\";v=\"125\", \"Chromium\";v=\"125\", \"Not.A/Brand\";v=\"24\""],"Accept-Language":["fr,fr-FR;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"],"Priority":["u=0, i"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["navigate"],"Sec-Fetch-User":["?1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Upgrade-Insecure-Requests":["1"],"Sec-Fetch-Site":["none"],"Sec-Fetch-Dest":["document"],"Cookie":[],"Sec-Ch-Ua-Platform":["\"Windows\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"]},"tls":{"resumed":false,"version":772,"cipher_suite":4867,"proto":"h2","server_name":"localhost"}},"duration":0.000932566,"status":502,"err_id":"0em2ykev8","err_trace":"reverseproxy.statusError (reverseproxy.go:1267)"}
3. Caddy version:
caddy --version
v2.7.6 h1:w0NymbG2m9PcvKWsrXO6EEkY9Ru4FJK8uQbYcev1p3A=
4. How I installed and ran Caddy:
This is my docker-compose.yml
php:
build:
context: ./docker/frankenphp
args:
TIMEZONE: ${TIMEZONE}
restart: always
networks:
- phpapp
ports:
- '9000:9000'
volumes:
- ./symfony/:/var/www/symfony/:cached
- ./symfony/var/log/:/var/log/
links:
- db:db-host
- redis
- rabbitmq
depends_on:
- mailhog
- rabbitmq
env_file: symfony/.env
caddy:
image: caddy:2-alpine
restart: always
networks:
- phpapp
ports:
- '80:80'
- '443:443'
volumes:
- ./docker/frankenphp/Caddyfile:/etc/caddy/Caddyfile
- ./symfony:/usr/share/caddy/symfony:cached
- ./docker/frankenphp/letsencrypt:/etc/caddy/letsencrypt
links:
- php:php-host
command: /bin/sh -c "caddy fmt --overwrite /etc/caddy/Caddyfile && caddy run --config /etc/caddy/Caddyfile --adapter caddyfile"
networks:
phpapp:
name: phpapp
My caddyfile
:443 {
root * /usr/share/caddy/symfony/public
file_server
encode zstd gzip
# Handle PHP files using php_fastcgi
php_fastcgi php:9000 {
root /usr/share/caddy/symfony/public
}
# CORS preflight requests setup
@preflight method OPTIONS
handle @preflight {
header {
Access-Control-Allow-Origin *
Access-Control-Allow-Methods "GET, POST, DELETE, OPTIONS"
Access-Control-Allow-Headers "Authorization, DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range"
Access-Control-Max-Age 1728000
Content-Type "text/plain; charset=utf-8"
Content-Length 0
}
respond 204
}
# Regular PHP requests
@php path *.php
handle @php {
header {
Access-Control-Allow-Origin *
Access-Control-Allow-Methods "GET, POST, DELETE, OPTIONS"
Access-Control-Allow-Headers "Authorization, DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range"
Access-Control-Expose-Headers "Content-Length,Content-Range"
}
}
# SSL/TLS certificates (replace with actual paths)
tls /etc/caddy/letsencrypt/starter.com.cert /etc/caddy/letsencrypt/starter.com.key
# Security headers
header {
Strict-Transport-Security "max-age=31536000;"
# More headers as needed
}
}
My dockerfile
FROM dunglas/frankenphp:1-php8.2 AS frankenphp_upstream
ARG TIMEZONE
ARG JWT_PASSPHRASE
WORKDIR /var/www/symfony
# Install system dependencies
RUN apt-get update && apt-get install -y \
acl \
file \
gettext \
git \
vim \
nano \
wget \
unzip \
cron \
curl \
rsync \
sudo \
openssh-server \
ocaml \
expect \
libmcrypt-dev \
libicu-dev \
libxml2-dev libxslt1-dev \
libfreetype6-dev \
libjpeg62-turbo-dev \
libxrender1 \
libfontconfig1 \
libx11-dev \
libxtst6 \
libpng-dev \
zlib1g-dev \
libjpeg-dev \
libonig-dev \
libwebp-dev \
libqt5svg5 \
jpegoptim \
optipng \
webp \
gnupg2 \
libpq-dev \
libzip-dev \
&& docker-php-ext-configure hash --with-mhash \
&& docker-php-ext-configure zip \
&& docker-php-ext-install -j$(nproc) intl xsl zip pdo_mysql opcache soap bcmath \
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
&& mkdir /var/run/sshd \
&& apt-get install -y gnupg2 \
&& curl -sL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y nodejs \
&& curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - \
&& echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list \
&& apt-get update && sudo apt-get install -y yarn \
&& echo 'alias sf="php bin/console"' >> ~/.bashrc \
&& echo "StrictHostKeyChecking no" >> /etc/ssh/ssh_config \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Node.js and Yarn
RUN curl -sL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y nodejs \
&& curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
&& echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
&& apt-get update \
&& apt-get install -y yarn
# Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
&& composer --version
# PHP and Webserver Configurations
COPY ./conf.d/app.ini /usr/local/etc/php/php.ini
COPY ./conf.d/auth.json /var/www/.composer/
COPY ./docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
# Set up Xdebug
RUN pecl install xdebug-3.3.1 \
&& docker-php-ext-enable xdebug \
&& echo "xdebug.mode=debug,coverage" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
# Set up GD and PostgreSQL
RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \
&& docker-php-ext-install -j$(nproc) gd \
&& docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql \
&& docker-php-ext-install pdo pdo_pgsql
# Set timezone
RUN ln -snf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime && echo ${TIMEZONE} > /etc/timezone \
&& printf '[PHP]\ndate.timezone = "%s"\n', ${TIMEZONE} > /usr/local/etc/php/conf.d/tzone.ini
# Security tools
RUN curl -L https://github.com/fabpot/local-php-security-checker/releases/download/v2.0.6/local-php-security-checker_2.0.6_linux_amd64 --output local-php-security-checker \
&& mv local-php-security-checker /usr/local/bin/local-php-security-checker && chmod 755 /usr/local/bin/local-php-security-checker
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
CMD ["frankenphp", "run", "--config", "/etc/caddy/Caddyfile"]
EXPOSE 9000
my docker-entrypoint.sh
#!/bin/sh
set -e
if [ "$1" = 'frankenphp' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then
# Install the project the first time PHP is started
# After the installation, the following block can be deleted
if [ ! -f composer.json ]; then
rm -Rf tmp/
composer create-project "symfony/skeleton $SYMFONY_VERSION" tmp --stability="$STABILITY" --prefer-dist --no-progress --no-interaction --no-install
cd tmp
cp -Rp . ..
cd -
rm -Rf tmp/
composer require "php:>=$PHP_VERSION" runtime/frankenphp-symfony
composer config --json extra.symfony.docker 'true'
if grep -q ^DATABASE_URL= .env; then
echo "To finish the installation please press Ctrl+C to stop Docker Compose and run: docker compose up --build -d --wait"
sleep infinity
fi
fi
if [ -z "$(ls -A 'vendor/' 2>/dev/null)" ]; then
composer install --prefer-dist --no-progress --no-interaction
fi
if grep -q ^DATABASE_URL= .env; then
echo "Waiting for database to be ready..."
ATTEMPTS_LEFT_TO_REACH_DATABASE=60
until [ $ATTEMPTS_LEFT_TO_REACH_DATABASE -eq 0 ] || DATABASE_ERROR=$(php bin/console dbal:run-sql -q "SELECT 1" 2>&1); do
if [ $? -eq 255 ]; then
# If the Doctrine command exits with 255, an unrecoverable error occurred
ATTEMPTS_LEFT_TO_REACH_DATABASE=0
break
fi
sleep 1
ATTEMPTS_LEFT_TO_REACH_DATABASE=$((ATTEMPTS_LEFT_TO_REACH_DATABASE - 1))
echo "Still waiting for database to be ready... Or maybe the database is not reachable. $ATTEMPTS_LEFT_TO_REACH_DATABASE attempts left."
done
if [ $ATTEMPTS_LEFT_TO_REACH_DATABASE -eq 0 ]; then
echo "The database is not up or not reachable:"
echo "$DATABASE_ERROR"
exit 1
else
echo "The database is now ready and reachable"
fi
if [ "$( find ./migrations -iname '*.php' -print -quit )" ]; then
php bin/console doctrine:migrations:migrate --no-interaction --all-or-nothing
fi
fi
setfacl -R -m u:www-data:rwX -m u:"$(whoami)":rwX var
setfacl -dR -m u:www-data:rwX -m u:"$(whoami)":rwX var
fi
exec docker-php-entrypoint "$@"
Despite the network, my php dockers and caddy cannot communicate with each other and I don’t know why.
Thanks for your help !