frankenphp icon indicating copy to clipboard operation
frankenphp copied to clipboard

Server become slower or unresponsive with frankenphp worker mode and laravel octane

Open hannanmiah opened this issue 4 months ago • 4 comments

What happened?

Dockerfile

FROM dunglas/frankenphp

# Be sure to replace "your-domain-name.example.com" by your domain name
#ENV SERVER_NAME=api.********
ENV FRANKENPHP_CONFIG="worker /app/public/frankenphp-worker.php 8"
# If you want to disable HTTPS, use this value instead:
ENV SERVER_NAME=:80

# install packages
RUN apt-get update && apt-get install -y \
        git \
        curl \
        libpng-dev \
        libjpeg-dev \
        libfreetype6-dev \
        libzip-dev \
        libonig-dev \
        libpq-dev \
        nodejs \
        npm \
        supervisor \
        # Clean up apt caches to reduce image size
        && rm -rf /var/lib/apt/lists/*

# add additional extensions here:
RUN install-php-extensions \
	pdo_mysql \
	gd \
	intl \
    pcntl \
	zip \
	opcache \
    redis \
    imagick \
    xml \
    simplexml \
    iconv \
    @composer


# Set working directory
WORKDIR /app

# copy php.ini
#COPY ./.docker/php/php.ini /usr/local/etc/php/conf.d/zz-production.ini

php.ini

memory_limit = 256M
upload_max_filesize = 128M
post_max_size = 100M

display_errors = Off
display_startup_errors = Off
log_errors = On
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
expose_php = Off
date.timezone = UTC

[opcache]
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.validate_timestamps=1
opcache.revalidate_freq=2
opcache.jit_buffer_size=100M
opcache.jit=tracing

/app/public/frankenphp-worker.php

<?php

// Set a default for the application base path and public path if they are missing...
$_SERVER['APP_BASE_PATH'] = $_ENV['APP_BASE_PATH'] ?? $_SERVER['APP_BASE_PATH'] ?? __DIR__.'/..';
$_SERVER['APP_PUBLIC_PATH'] = $_ENV['APP_PUBLIC_PATH'] ?? $_SERVER['APP_BASE_PATH'] ?? __DIR__;

require __DIR__.'/../vendor/laravel/octane/bin/frankenphp-worker.php';

Docker compose file

services:
  app:
    image: dazzle-backend:latest
    build:
      context: .
    restart: always
    volumes:
      - ./:/app
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - redis
      - typesense
    networks:
      - dazzle_network
      - traefik_proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.laravel.rule=Host(`#####`)"
      - "traefik.http.routers.laravel.entrypoints=websecure"
      - "traefik.http.routers.laravel.tls.certresolver=letsencrypt"
      - "traefik.http.services.laravel.loadbalancer.server.port=80"
  redis:
    image: redis:alpine
    hostname: redis
    restart: always
    command: redis-server /usr/local/etc/redis/redis.conf
    volumes:
      - ./.docker/redis/redis.conf:/usr/local/etc/redis/redis.conf
    networks:
      - dazzle_network
  supervisor:
    image: dazzle-backend:latest
    entrypoint: ["./.docker/entrypoint.sh"]
    restart: always
    volumes:
      - ./:/app
    depends_on:
      - redis
      - typesense
      - app
    networks:
      - dazzle_network
      - traefik_proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.reverb.tls=true"
      - "traefik.http.routers.reverb.entrypoints=websecure"
      - "traefik.http.routers.reverb.rule=Host(`####`)"
      - "traefik.http.services.reverb.loadbalancer.server.port=8080"
      - "traefik.http.services.reverb.loadbalancer.server.scheme=http"
  typesense:
    image: typesense/typesense:27.1
    restart: on-failure
    environment:
      TYPESENSE_API_KEY: ${TYPESENSE_API_KEY}
    command: '--data-dir /data --api-key=typesense_dazzle'
    volumes:
      - typesense_data:/data
    networks:
      - dazzle_network
# Volumes needed for Caddy certificates and configuration
volumes:
  caddy_data:
  caddy_config:
  typesense_data:
networks:
  dazzle_network:
    driver: bridge
  traefik_proxy:
    external: true

When worker is enabled then backend becomes slow or unresponsive after certain load. But after disabling worker mode everything is working fine. Whats wrong with this setup? And what is correct setup with laravel, docker and frankenphp (woker mode)?

Build Type

Docker (Debian Bookworm)

Worker Mode

Yes

Operating System

GNU/Linux

CPU Architecture

x86_64

PHP configuration

memory_limit = 256M
upload_max_filesize = 128M
post_max_size = 100M

display_errors = Off
display_startup_errors = Off
log_errors = On
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
expose_php = Off
date.timezone = UTC

[opcache]
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.validate_timestamps=1
opcache.revalidate_freq=2
opcache.jit_buffer_size=100M
opcache.jit=tracing

Relevant log output


hannanmiah avatar Aug 21 '25 05:08 hannanmiah

How are you load-testing? How many CPU cores are you using? 8 workers might not be enough if you are doing IO.

Example minimal Caddyfile
{
	# Caddy is the web server that serves the Laravel application using FrankenPHP
	# https://caddyserver.com/docs/caddyfile
	# https://frankenphp.dev/docs/config

	admin localhost:2019

	frankenphp {
		max_threads {$MAX_THREADS:100} # max number of threads (conurrent requests)
		max_wait_time 30s # max time to wait for threads
		php_ini {
			max_execution_time 30 # you can add more php_ini configuration here
		}
	}
}

{$SERVER_NAME::80} {
	route {
		encode zstd br gzip

		php_server {
			root /app/public
			worker {
                file "/app/frankenphp-worker.php" # laravel Octane entry file
                num {$NUM_THREADS:8} # number of workers running from the start
                {$WATCH} # set 'WATCH=watch' in the docker.compose environment to enable watching
                match * # all requests go to the worker script
            }
		}
	}
}

AlliBalliBaba avatar Aug 21 '25 08:08 AlliBalliBaba

I'm using 4 core 8gb ram vps

hannanmiah avatar Aug 21 '25 08:08 hannanmiah

When exactly does the backend become slow? After some time or after you spam a certain endpoint? What does the endpoint do?

What Laravel version are you using? If you are you spamming an endpoint with sessions: What session driver are you using? If you are spamming an endpoint with Db, what Db are you using? Are you using php artisan octane:start somewhere or just the default entrypoint?

Your setup doesn't look too far off, you can try mounting the Caddyfile I provided above into /etc/frankenphp/Caddyfile and see if that makes a difference. Otherwise it's hard to say from just the info you provided. If the server is getting slower over time, it's also possible that there's a memory leak in Laravel or a library you are using.

AlliBalliBaba avatar Aug 21 '25 11:08 AlliBalliBaba

Can you also provide a minimal reproducer of the endpoint that became unresponsive?

AlliBalliBaba avatar Aug 23 '25 15:08 AlliBalliBaba