Skip to content

Latest commit

 

History

History
172 lines (133 loc) · 4.47 KB

File metadata and controls

172 lines (133 loc) · 4.47 KB

Migrating from Nginx/PHP-FPM

FrankenPHP replaces both your web server (Nginx, Apache) and PHP-FPM with a single binary. This guide covers a basic migration for a typical PHP application.

Key Differences

PHP-FPM setup FrankenPHP equivalent
Nginx/Apache + PHP-FPM Single frankenphp binary
php-fpm.conf pool settings frankenphp global option
Nginx server {} block Caddyfile site block
php_value / php_admin_value php_ini Caddyfile directive
pm = static / pm.max_children num_threads
pm = dynamic max_threads auto

Step 1: Replace Your Web Server Config

A typical Nginx + PHP-FPM configuration:

server {
    listen 80;
    server_name example.com;
    root /var/www/app/public;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Becomes a single Caddyfile:

example.com {
    root /var/www/app/public
    php_server
}

That's it. The php_server directive handles PHP routing, try_files-like behavior, and static file serving.

Step 2: Migrate PHP Configuration

Your existing php.ini works as-is. See Configuration for where to place it depending on your installation method.

You can also set directives directly in the Caddyfile:

{
    frankenphp {
        php_ini memory_limit 256M
        php_ini max_execution_time 30
    }
}

example.com {
    root /var/www/app/public
    php_server
}

Step 3: Adjust Pool Size

In PHP-FPM, you tune pm.max_children to control the number of worker processes. In FrankenPHP, the equivalent is num_threads:

{
    frankenphp {
        num_threads 16
    }
}

By default, FrankenPHP starts 2 threads per CPU. For dynamic scaling similar to PHP-FPM's pm = dynamic:

{
    frankenphp {
        num_threads 4
        max_threads auto
    }
}

Step 4: Docker Migration

A typical PHP-FPM Docker setup using two containers (Nginx + PHP-FPM) can be replaced by a single container:

Before:

services:
  nginx:
    image: nginx:1
    volumes:
      # mount Nginx config into the container
      - ./config:/etc/nginx/conf.d
      - .:/var/www/app
    ports:
      - "80:80"
      - "443:443"

  php:
    image: php:8.5-fpm
    volumes:
      - .:/var/www/app

After:

services:
  php:
    image: dunglas/frankenphp:1-php8.5
    volumes:
      - .:/var/www/app
      # mount the Caddyfile into the container
      - ./config:/etc/frankenphp
      - caddy_data:/data
      - caddy_config:/config
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"

volumes:
  caddy_data:
  caddy_config:

If you need additional PHP extensions, see Building Custom Docker Image.

For framework-specific Docker setups, see Symfony Docker and Laravel.

Step 5: Consider Worker Mode (Optional)

In classic mode, FrankenPHP works like PHP-FPM: each request boots the application from scratch. This is a safe starting point for migration.

For better performance, you can switch to worker mode, which boots your application once and keeps it in memory:

example.com {
    root /var/www/app/public
    php_server {
        root /var/www/app/public
        worker index.php 4
    }
}

Caution

Worker mode keeps your application in memory between requests. Make sure your code does not rely on global state being reset between requests. Frameworks like Symfony, Laravel, and API Platform have native support for this mode.

What You Can Remove

After migrating, you no longer need:

  • Nginx or Apache
  • PHP-FPM (php-fpm service/process)
  • FastCGI configuration
  • Self-managed TLS certificates (Caddy handles them automatically)