iTranslated by AI
Installing FrankenPHP on Debian 12 Bookworm

In this article, it is assumed that the installation and configuration of Debian have been completed. The final target configuration is as follows.
- OS: Debian 12 bookworm
- web server + PHP: FrankenPHP (Caddy + PHP8.3) - This article
- Database: MariaDB
Firewall Configuration
Open ports 80 and 443.
$ sudo ufw allow 'WWW Full' && sudo ufw reload && sudo ufw status
Rule added
Rule added (v6)
Status: active
To Action From
-- ------ ----
SSH LIMIT Anywhere
WWW Full ALLOW Anywhere
SSH (v6) LIMIT Anywhere (v6)
WWW Full (v6) ALLOW Anywhere (v6)
If the status is active and "WWW Full" is in the list, you are done.
Deleting Rules Once Added
To delete a rule that was added, execute the following command.
$ sudo ufw delete allow 'WWW Full' && sudo ufw reload
To delete them individually:
$ sudo ufw status numbered
Status: active
To Action From
-- ------ ----
[ 1] WWW Full LIMIT IN Anywhere
[ 2] WWW Full (v6) LIMIT IN Anywhere (v6)
Specify the number displayed above.
$ sudo ufw delete 2 && sudo ufw reload
Installing haveged
Take measures to prevent the entropy pool from being depleted[1].
$ sudo apt update
$ sudo apt install haveged
$ sudo systemctl enable --now haveged
Installing FrankenPHP
Generally, when people think of web hosting environments, Nginx + PHP-FPM is the most common setup. However, because the hurdles for environment construction and maintenance are high, I have high expectations for FrankenPHP.
Add the installation path to your PATH
Add /usr/local/sbin to your PATH.
$ vi ~/.bashrc
Add the following:
+ export PATH=/usr/local/sbin:$PATH
Apply the changes.
$ source ~/.bashrc
Download and place the binary from GitHub
$ wget https://github.com/dunglas/frankenphp/releases/download/v1.5.0/frankenphp-linux-x86_64 && \
sudo mv ~/frankenphp-linux-x86_64 /usr/local/sbin/caddy && \
sudo chmod +x /usr/local/sbin/caddy && \
/usr/local/sbin/caddy -v
Configuring FrankenPHP
The following steps are typical tasks, similar to those for Nginx or Apache.
Changing User Settings
Create a user for FrankenPHP and add them to the www-data group.
- In the example below, the working user
debianis also added to thewww-datagroup.
$ sudo useradd -mU -s /usr/sbin/nologin -d /opt/caddy caddy && \
sudo usermod -aG www-data caddy && \
sudo usermod -aG www-data debian
Creating the Document Root
Change the permissions of the web site's document root so that members of the www-data group can add, update, and delete files.
$ sudo mkdir /var/www && \
sudo chown -R caddy:www-data /var/www && \
sudo chmod -R 2775 /var/www && \
sudo find /var/www -type d -exec sudo chmod 2775 {} \; && \
sudo find /var/www -type f -exec sudo chmod 2664 {} \; && \
exit
You should have been logged out of the server, so please log in again.
Configuring the Caddyfile
FrankenPHP's web server uses Caddy, which is written in Go. The configuration file syntax is the same as Caddy's.
$ sudo vi /etc/Caddyfile
(encode) {
encode zstd br gzip
php_server
}
(header-cache-control) {
@static {
file
path *.css *.js *.jpeg *.jpg *.png *.gif *.webp *.avif *.svg *.eot *.ttf *.woff *.woff2
}
header @static Cache-Control "public, max-age=691200"
}
(header-security) {
header Referrer-Policy no-referrer-when-downgrade
header X-Content-Type-Options nosniff
header X-Download-Options noopen
header X-Frame-Options SAMEORIGIN
}
(header-cors) {
@origin header Origin {args[0]}
header @origin Access-Control-Allow-Origin "{args[0]}"
header @origin Access-Control-Allow-Methods "GET,POST,PUT,DELETE,OPTIONS"
}
(header-https) {
header Content-Security-Policy "default-src https: data: 'unsafe-eval' 'unsafe-inline'"
header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
header X-SSL-Status https
}
(createlog) {
log {
hostnames {args[0]}
output file /opt/caddy/log/access-{args[0]}.log {
roll_size 1gb
roll_keep 5
roll_keep_for 7d
}
}
log {
hostnames {args[0]}
output file /opt/caddy/log/error-{args[0]}.log {
roll_size 1gb
roll_keep 5
roll_keep_for 7d
}
level ERROR
}
}
{
frankenphp {
num_threads 16
}
order php_server before file_server
http_port 80
https_port 443
auto_https disable_redirects
}
http:// {
root * /var/www/exmaple.com/public
import header-cache-control
import header-security
import header-cors 192.168.10.20
import encode
}
exmaple.com:80,
exmaple.com:443 {
redir https://www.{host}{uri} permanent
import header-cache-control
import header-security
import header-cors {host}
import encode
}
www.exmaple.com:80,
www.exmaple.com:443 {
root * /var/www/{host}/public
import header-cache-control
import header-security
import header-cors {host}
import header-https
import encode
import createlog {host}
}
Registering FrankenPHP as a Service
$ sudo vi /etc/systemd/system/caddy.service
Paste the following:
# caddy.service
#
# For using Caddy with a config file.
#
# Make sure the ExecStart and ExecReload commands are correct
# for your installation.
#
# See https://caddyserver.com/docs/install for instructions.
#
# WARNING: This service does not use the --resume flag, so if you
# use the API to make changes, they will be overwritten by the
# Caddyfile next time the service is restarted. If you intend to
# use Caddy's API to configure it, add the --resume flag to the
# `caddy run` command or use the caddy-api.service file instead.
[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target
[Service]
Type=notify
User=caddy
Group=www-data
ExecStart=/usr/local/sbin/caddy run --environ --config /etc/Caddyfile
ExecReload=/bin/kill -USR1 $MAINPID
ExecStop=/usr/local/sbin/caddy stop
Restart=on-failure
RestartSec=5s
TimeoutStopSec=5s
LimitNOFILE=65536
LimitNPROC=512
PrivateTmp=true
PrivateDevices=true
ProtectSystem=full
ReadWriteDirectories=/opt/caddy
Environment=CADDYPATH=/opt/caddy
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
Register FrankenPHP as a service.
$ sudo systemctl enable caddy && \
sudo systemctl start caddy && \
sudo systemctl status caddy
The website should now be live.
Creating PHP.ini
Unless you built the FrankenPHP binary yourself, the location for php.ini is under /lib. Since FrankenPHP does not provide a php.ini file, you need to prepare it yourself.
$ sudo vi /lib/php.ini
Change the /run/mysqld/mysqld.sock part according to your environment.
For MariaDB, you can check it in /etc/mysql/mariadb.cnf.
[PHP]
engine = On
short_open_tag = Off
precision = 14
output_buffering = 4096
implicit_flush = Off
serialize_precision = -1
zend.enable_gc = On
expose_php = Off
max_execution_time = 30
max_input_time = 60
memory_limit = 128M
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
error_log = /var/log/php_errors.log
display_errors = Off
display_startup_errors = Off
log_errors = On
log_errors_max_len = 1024
ignore_repeated_errors = On
ignore_repeated_source = Off
report_memleaks = On
track_errors = Off
html_errors = Off
request_order = "GP"
auto_globals_jit = On
post_max_size = 4M
upload_max_filesize = 4M
max_file_uploads = 20
default_mimetype = "text/html"
file_uploads = On
allow_url_fopen = Off
default_socket_timeout = 60
open_basedir = "/var/www/:/tmp/"
[Date]
date.timezone = "Asia/Tokyo"
[mbstring]
mbstring.language = Japanese
mbstring.detect_order = ASCII,ISO-2022-JP,UTF-8,eucJP-win,SJIS-win
mbstring.encoding_translation = Off
mbstring.substitute_character = none
mbstring.func_overload = 0
[mail function]
SMTP = localhost
smtp_port = 25
sendmail_path = /usr/sbin/sendmail -t -i
mail.add_x_header = On
[Session]
session.save_handler = files
session.save_path = "/tmp/php/session"
session.use_cookies = 1
session.use_only_cookies = 1
session.name = PHPSESSID
session.cookie_lifetime = 0
session.cookie_path = /
session.cookie_domain =
session.cookie_httponly =
session.serialize_handler = php
session.gc_probability = 1
session.gc_divisor = 1000
session.gc_maxlifetime = 1440
session.cache_limiter = nocache
session.cache_expire = 180
session.use_trans_sid = 0
session.hash_function = 0
session.hash_bits_per_character = 5
[Pdo_mysql]
pdo_mysql.default_socket = /run/mysqld/mysqld.sock
[MySQLi]
mysqli.default_socket = /run/mysqld/mysqld.sock
[OPcache]
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8M
opcache.max_accelerated_files=4000
opcache.revalidate_freq=0
opcache.fast_shutdown=1
[apcu]
apc.enabled=1
apc.enable_cli=1
apc.shm_size=64M
apc.ttl=3600
apc.gc_ttl=3600
apc.user_ttl=7200
apc.cache_by_default=1
apc.filters=.*\.php
apc.stat=1
apc.serializer=php
apc.use_request_time=1
apc.include_once_override=0
[Memcached]
;extension=memcached.so
Related Articles
- Initial setup to perform after installing Debian 12 bookworm
- Install MariaDB on Debian 12 bookworm and Hello World!
-
This can occur when there is a lack of randomness (entropy) while establishing an SSL/TLS connection, preventing cURL or OpenSSL from establishing secure communication (cURL error 35: Insufficient randomness).
In SSL communication, secure encryption is not possible if "truly random values" cannot be generated.
General-purpose PCs accumulate "entropy" (randomness) from keyboard/mouse activity or disk I/O. However, on servers, these sources are extremely limited, which can lead to /dev/random failing to provide enough random numbers and causing processes to hang.
If Intel/AMD RDRAND instructions are available, rng-tools is also effective. ↩︎
Discussion