1. The problem I’m having:
Hi there,
I’m trying to get a pretty standard Laravel app deployed on a Render web service instance with FrankenPHP.
Here’s my Dockerfile:
FROM dunglas/frankenphp
RUN install-php-extensions \
pdo \
gd \
intl \
zip \
opcache
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
COPY . /app
RUN composer install --no-interaction --no-scripts --no-suggest
RUN php artisan config:cache
RUN php artisan route:cache
RUN php artisan migrate --force
# Line below needed otherwise build exits with a permissions error
RUN setcap -r /usr/local/bin/frankenphp
Locally, if I run (from the docs):
docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp
Everything works as expected.
On Render, when I deploy everything seems to build and start correctly, I even get the default Laravel uptime endpoint (/up
) returning something positive as the healthcheck passes, however if I try to visit the site in a browser I get the “TOO MANY REDIRECTS” error.
I’ve tried clearing my cookies, different browsers and tweaking the ports exposed but nothing seems to help. I don’t really have anything special set up in the environment as shown below:
I think the issue might be something to do with the default port binding and/or proxying from HTTPS → HTTP but I don’t know enough about this to get it working.
Any ideas welcomed! Thanks in advance
2. Error messages and/or full log output:
{"level":"info","ts":1724311880.1020823,"msg":"using config from file","file":"/etc/caddy/Caddyfile"}
{"level":"info","ts":1724311880.1030648,"msg":"adapted config to JSON","adapter":"caddyfile"}
{"level":"warn","ts":1724311880.103078,"msg":"Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies","adapter":"caddyfile","file":"/etc/caddy/Caddyfile","line":10}
{"level":"info","ts":1724311880.1043901,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
{"level":"info","ts":1724311880.1045024,"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}
{"level":"info","ts":1724311880.1045158,"logger":"http.auto_https","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
{"level":"info","ts":1724311880.1072965,"msg":"FrankenPHP started 🐘","php_version":"8.3.10","num_threads":1}
{"level":"warn","ts":1724311880.1073344,"logger":"pki.ca.local","msg":"installing root certificate (you might be prompted for password)","path":"storage:pki/authorities/local/root.crt"}
{"level":"info","ts":1724311880.1077294,"msg":"warning: \"certutil\" is not available, install \"certutil\" with \"apt install libnss3-tools\" or \"yum install nss-tools\" and try again"}
{"level":"info","ts":1724311880.107743,"msg":"define JAVA_HOME environment variable to use the Java trust"}
{"level":"info","ts":1724311880.1972446,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc0002d8400"}
{"level":"info","ts":1724311880.3050828,"logger":"tls","msg":"cleaning storage unit","storage":"FileStorage:/data/caddy"}
{"level":"info","ts":1724311880.305352,"logger":"tls","msg":"finished cleaning storage units"}
{"level":"info","ts":1724311887.9194095,"msg":"certificate installed properly in linux trusts"}
{"level":"info","ts":1724311887.9196007,"logger":"http","msg":"enabling HTTP/3 listener","addr":":443"}
{"level":"info","ts":1724311887.9196875,"msg":"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."}
{"level":"info","ts":1724311887.9198284,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
{"level":"info","ts":1724311887.9198742,"logger":"http.log","msg":"server running","name":"remaining_auto_https_redirects","protocols":["h1","h2","h3"]}
{"level":"info","ts":1724311887.9198818,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["localhost"]}
{"level":"info","ts":1724311887.9204469,"logger":"tls.obtain","msg":"acquiring lock","identifier":"localhost"}
{"level":"info","ts":1724311887.9207993,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
{"level":"info","ts":1724311887.9208128,"msg":"serving initial configuration"}
{"level":"info","ts":1724311887.9785044,"logger":"tls.obtain","msg":"lock acquired","identifier":"localhost"}
{"level":"info","ts":1724311887.9786289,"logger":"tls.obtain","msg":"obtaining certificate","identifier":"localhost"}
{"level":"info","ts":1724311887.9799352,"logger":"tls.obtain","msg":"certificate obtained successfully","identifier":"localhost","issuer":"local"}
{"level":"info","ts":1724311887.980022,"logger":"tls.obtain","msg":"releasing lock","identifier":"localhost"}
{"level":"warn","ts":1724311887.9803834,"logger":"tls","msg":"stapling OCSP","error":"no OCSP stapling for [localhost]: no OCSP server specified in certificate","identifiers":["localhost"]}
3. Caddy version:
v2.8.4 (this is from dunglas/frankenphp
latest Docker image)
4. How I installed and ran Caddy:
Via dunglas/frankenphp
a. System environment:
Docker
b. Command:
Default CMD
for dunglas/frankenphp
c. Service/unit/compose file:
As above
d. My complete Caddy config:
From frankenPHP
{
{$CADDY_GLOBAL_OPTIONS}
frankenphp {
#worker /path/to/your/worker.php
{$FRANKENPHP_CONFIG}
}
}
{$CADDY_EXTRA_CONFIG}
{$SERVER_NAME:localhost} {
#log {
# # Redact the authorization query parameter that can be set by Mercure
# format filter {
# request>uri query {
# replace authorization REDACTED
# }
# }
#}
root * public/
encode zstd br gzip
# Uncomment the following lines to enable Mercure and Vulcain modules
#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}
php_server
}
5. Links to relevant resources:
As linked to above