Deploying frankenphp on render.com

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:

Screenshot 2024-08-21 at 15 12 39

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

That screenshot link doesn’t work.

What do you mean? You must have a Caddyfile?

What do you mean? You must have logs. Caddy always emits logs to stdout.

The screenshot up here still doesn’t work. Is it behind your private github account or something?

I still don’t see any evidence of a problem. Show a request with curl -v.

From your Caddy logs, it looks like Caddy issued a cert for localhost, meaning it’s listening for HTTPS with the hostname localhost. You probably mean to set the SERVER_NAME env var to change that to your actual domain.

I don’t know how <render.com> works.