Skip to content

Commit 47e0c38

Browse files
docs: add migration guide (#2275)
People at conferences often come by and ask how it is to migrate from FPM to FrankenPHP. I think that a page "Migrating from..." would be very reassuring for newcomers. It only covers simple cases, but most complex ones can be handled by LLMs if necessary.
1 parent 0ac11f1 commit 47e0c38

File tree

1 file changed

+168
-0
lines changed

1 file changed

+168
-0
lines changed

docs/migrate.md

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# Migrating from PHP-FPM
2+
3+
FrankenPHP replaces both your web server (Nginx, Apache) and PHP-FPM with a single binary.
4+
This guide covers a basic migration for a typical PHP application.
5+
6+
## Key Differences
7+
8+
| PHP-FPM setup | FrankenPHP equivalent |
9+
|---|---|
10+
| Nginx/Apache + PHP-FPM | Single `frankenphp` binary |
11+
| `php-fpm.conf` pool settings | [`frankenphp` global option](config.md#caddyfile-config) |
12+
| Nginx `server {}` block | `Caddyfile` site block |
13+
| `php_value` / `php_admin_value` | [`php_ini` Caddyfile directive](config.md#php-config) |
14+
| `pm = static` / `pm.max_children` | `num_threads` |
15+
| `pm = dynamic` | [`max_threads auto`](performance.md#max_threads) |
16+
17+
## Step 1: Replace Your Web Server Config
18+
19+
A typical Nginx + PHP-FPM configuration:
20+
21+
```nginx
22+
server {
23+
listen 80;
24+
server_name example.com;
25+
root /var/www/app/public;
26+
index index.php;
27+
28+
location / {
29+
try_files $uri $uri/ /index.php$is_args$args;
30+
}
31+
32+
location ~ \.php$ {
33+
fastcgi_pass unix:/run/php/php-fpm.sock;
34+
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
35+
include fastcgi_params;
36+
}
37+
}
38+
```
39+
40+
Becomes a single `Caddyfile`:
41+
42+
```caddyfile
43+
example.com {
44+
root /var/www/app/public
45+
php_server
46+
}
47+
```
48+
49+
That's it. The `php_server` directive handles PHP routing, `try_files`-like behavior, and static file serving.
50+
51+
## Step 2: Migrate PHP Configuration
52+
53+
Your existing `php.ini` works as-is. See [Configuration](config.md) for where to place it depending on your installation method.
54+
55+
You can also set directives directly in the `Caddyfile`:
56+
57+
```caddyfile
58+
{
59+
frankenphp {
60+
php_ini memory_limit 256M
61+
php_ini max_execution_time 30
62+
}
63+
}
64+
65+
example.com {
66+
root /var/www/app/public
67+
php_server
68+
}
69+
```
70+
71+
## Step 3: Adjust Pool Size
72+
73+
In PHP-FPM, you tune `pm.max_children` to control the number of worker processes.
74+
In FrankenPHP, the equivalent is `num_threads`:
75+
76+
```caddyfile
77+
{
78+
frankenphp {
79+
num_threads 16
80+
}
81+
}
82+
```
83+
84+
By default, FrankenPHP starts 2 threads per CPU. For dynamic scaling similar to PHP-FPM's `pm = dynamic`:
85+
86+
```caddyfile
87+
{
88+
frankenphp {
89+
num_threads 4
90+
max_threads auto
91+
}
92+
}
93+
```
94+
95+
## Step 4: Docker Migration
96+
97+
A typical PHP-FPM Docker setup using two containers (Nginx + PHP-FPM) can be replaced by a single container:
98+
99+
**Before:**
100+
101+
```yaml
102+
services:
103+
nginx:
104+
image: nginx:1
105+
volumes:
106+
- ./nginx.conf:/etc/nginx/conf.d/default.conf
107+
- .:/var/www/app
108+
ports:
109+
- "80:80"
110+
111+
php:
112+
image: php:8.5-fpm
113+
volumes:
114+
- .:/var/www/app
115+
```
116+
117+
**After:**
118+
119+
```yaml
120+
services:
121+
php:
122+
image: dunglas/frankenphp:1-php8.5
123+
volumes:
124+
- .:/app/public
125+
- caddy_data:/data
126+
- caddy_config:/config
127+
ports:
128+
- "80:80"
129+
- "443:443"
130+
- "443:443/udp"
131+
132+
volumes:
133+
caddy_data:
134+
caddy_config:
135+
```
136+
137+
If you need additional PHP extensions, see [Building Custom Docker Image](docker.md#how-to-install-more-php-extensions).
138+
139+
For framework-specific Docker setups, see [Symfony Docker](https://github.com/dunglas/symfony-docker) and [Laravel](laravel.md#docker).
140+
141+
## Step 5: Consider Worker Mode (Optional)
142+
143+
In [classic mode](classic.md), FrankenPHP works like PHP-FPM: each request boots the application from scratch. This is a safe starting point for migration.
144+
145+
For better performance, you can switch to [worker mode](worker.md), which boots your application once and keeps it in memory:
146+
147+
```caddyfile
148+
example.com {
149+
root /var/www/app/public
150+
php_server {
151+
root /var/www/app/public
152+
worker index.php 4
153+
}
154+
}
155+
```
156+
157+
> [!CAUTION]
158+
>
159+
> 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](worker.md#symfony-runtime), [Laravel](laravel.md#laravel-octane), and [API Platform](https://api-platform.com) have native support for this mode.
160+
161+
## What You Can Remove
162+
163+
After migrating, you no longer need:
164+
165+
- Nginx or Apache
166+
- PHP-FPM (`php-fpm` service/process)
167+
- FastCGI configuration
168+
- Self-managed TLS certificates (Caddy handles them automatically)

0 commit comments

Comments
 (0)