FrankenPHP + Caddy + Ubuntu 24.04 + CakePHP Worker

by May 24, 2025IT Tips0 comments

This is getting FrankenPHP running with Caddy on Ubuntu 24.04 LTS without Docker

Installing a FrankenPHP environment on Ubuntu 24.04 LTS

Install some pre-reqs

1
sudo apt install zip unzip curl -y

Install PHP8.4 and Caddy

1
2
3
4
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
sudo apt install caddy php8.4 php8.4-cli php8.4-{bz2,curl,mbstring,intl,xml} -y
php -v

Install frankenphp

1
2
curl https://frankenphp.dev/install.sh | sh
sudo mv frankenphp /usr/local/bin/

Create a php.ini

1
2
3
4
5
6
7
8
# create the /etc/frankenphp dir if you need to
cp /etc/php/8.4/fpm/php.ini /etc/frankenphp/
 
# uncomment and edit the error_log value
error_log=/var/log/php_errors.log
 
sudo touch /var/log/php_errors.log
sudo chown www-data:www-data /var/log/php_errors.log

Create a config file for Caddy

/etc/caddy/Caddyfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# The Caddyfile is an easy way to configure your Caddy web server.
#
# Unless the file starts with a global options block, the first
# uncommented line is always the address of your site.
#
# To use your own domain name (with automatic HTTPS), first make
# sure your domain's A/AAAA DNS records are properly pointed to
# this machine's public IP, then replace ":80" below with your
# domain name.
#
{
        metrics
        frankenphp {
                worker {
                        env DEBUG false
                        file /var/www/html/webroot/index.php
                        watch /var/www/html/webroot
                        watch /var/www/html/vendor
                        num 6
                }
        }
}
 
tgnyt.australiaeast.cloudapp.azure.com,
fphp.toggen.com.au {
        # Set this path to your site's directory.
        root * /var/www/html/webroot
 
        encode zstd br gzip
        file_server
 
        # Or serve a PHP site through php-fpm:
        # php_fastcgi localhost:9000
        php_server {
                env PATH /usr/local/bin:/usr/bin
                env DEBUG true
        }
 
        log {
                format console
                output file /var/log/caddy.log
        }
}
 
# Refer to the Caddy docs for more information:

To format the Caddyfile use

1
sudo caddy fmt --overwrite

Create a systemd .service file

/etc/systemd/system/frankenphp.service

Don't think CAP_NET_ADMIN is needed to remove this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[Unit]
Description=FrankenPHP Server
After=network.target network-online.target
Requires=network-online.target
 
[Service]
Restart=on-failure
Type=notify
User=www-data
Group=www-data
ExecStartPre=/usr/local/bin/frankenphp validate --config /etc/caddy/Caddyfile
ExecStart=/usr/local/bin/frankenphp run --config /etc/caddy/Caddyfile --adapter caddyfile
ExecReload=/usr/local/bin/frankenphp reload --config /etc/caddy/Caddyfile --force
TimeoutStopSec=5s
LimitNOFILE=1048576
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
 
[Install]
WantedBy=multi-user.target

Add writeable dirs for the user running frankenphp

1
2
sudo mkdir /var/www/{.config,.local}
sudo chown www-data:www-data /var/www/{.config,.local}

The contents of the above directories once frankenphp starts

1
2
3
4
5
6
7
# autosave
/var/www/.config/caddy/autosave.json
 
# certs letsencrypt config
/var/www/.local/share/caddy/certificates
/var/www/.local/share/caddy/acme
/var/www/.local/share/caddy/acme/acme-v02.api.letsencrypt.org-directory

Enable and start the service

1
2
systemctl enable frankenphp.service
systemctl start frankenphp.service

Install CakePHP

1
2
3
4
cd /var/www/html
composer create-project --prefer-dist cakephp/app:~5.0 .
# optional upgrade
composer require cakephp/cakephp:~5.2

Deploy the worker script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
 
use App\Application;
use App\Http\Server;
use Cake\Core\PluginApplicationInterface;
$workerServer = $_SERVER;
 
ignore_user_abort(true);
 
require  dirname(__DIR__) . '/vendor/autoload.php';
$myApp = new Application( dirname(__DIR__)  . '/config');
$myApp->bootstrap();
 
$server = new Server($myApp);
 
error_log('Worker started');
 
$handler = static function () use ($server, $myApp)  {
        error_log('Worker handler');
        # DebugKit won't show up and will throw an error without this
        if ($myApp instanceof PluginApplicationInterface) {
                $myApp->pluginBootstrap();
        }
        $server->emit($server->run());
};
 
$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 1);
 
for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) {
    error_log('Worker handling request');
    $keepRunning = \frankenphp_handle_request($handler);
 
    gc_collect_cycles();
 
    if (!$keepRunning) break;
}

Modify Cake\Http\Server.php

1
2
mkdir src/Http
cp vendor/cakephp/cakephp/src/Http/Server.php src/Http/

Change the following

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# change namespace
# namespace Cake\Http;
# to
namespace App\Http;
 
 
# add references to the Cake\Http classes
use Cake\Http\Runner;
use Cake\Http\MiddlewareQueue;
use Cake\Http\ResponseEmitter;
use Cake\Http\ServerRequestFactory;
 
# comment out the call to bootstrap the app
 public function run(
        ?ServerRequestInterface $request = null,
        ?MiddlewareQueue $middlewareQueue = null,
    ): ResponseInterface {
#        $this->bootstrap();

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

The reCAPTCHA verification period has expired. Please reload the page.