Application Servers Azure

FrankenPHP on Ubuntu 24.04 on Azure User Guide

| Product: FrankenPHP 1.12.4 on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of FrankenPHP on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. FrankenPHP is a modern PHP application server built on top of the Caddy web server. It embeds the PHP interpreter and Caddy into a single self contained binary, so you run high performance PHP without wiring together a separate web server and a PHP FPM pool. FrankenPHP adds worker mode for long running application workers, automatic HTTPS, early hints, and real time features such as server sent events and Mercure, while remaining fully compatible with your existing PHP applications.

The image installs FrankenPHP 1.12.4 from the official prebuilt release binary, run under systemd as an unprivileged frankenphp user with just the CAP_NET_BIND_SERVICE capability so it can bind the standard privileged web ports. FrankenPHP serves your application over HTTPS on port 443 and permanently redirects plain HTTP on port 80 to HTTPS. A small demo PHP application ships in the document root so a freshly launched instance immediately demonstrates end to end PHP execution.

Secure by default. FrankenPHP embeds Caddy, whose administration interface on port 2019 can rewrite the running configuration. This image disables that interface entirely (admin off), so it listens on no interface at all. On the first boot of every VM a one shot service generates a fresh per instance self signed TLS certificate bound to that instance address, so the server answers over HTTPS immediately. FrankenPHP has no login, so no shared or default credential of any kind ships in the image.

What is included:

  • FrankenPHP 1.12.4 from the official prebuilt release binary, run under systemd as the unprivileged frankenphp user (frankenphp.service)

  • An HTTPS endpoint on port 443 and an HTTP to HTTPS permanent redirect on port 80

  • A demo PHP application at /var/www/html/index.php that reports the runtime PHP version, proving the application server executes PHP (replace it with your own application)

  • The embedded PHP interpreter and Caddy web server as a single static binary with no runtime library dependencies and no separate PHP FPM socket to manage

  • The Caddy admin API disabled (admin off) so the config mutation endpoint listens on no interface

  • A per instance self signed TLS certificate generated on first boot, with a one line path to automatic trusted HTTPS for your own domain

  • The whole configuration in one readable file, /etc/frankenphp/Caddyfile, that you version control

Prerequisites

  • Active Azure subscription, SSH public key, VNet + subnet in target region

  • Subscription to the FrankenPHP listing on Azure Marketplace

  • Network Security Group rules allowing TCP 22 (admin) from your management network, and TCP 443 (HTTPS) plus TCP 80 (HTTP redirect) from the clients that will reach your application

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is ample for a demo or a small application. Size up for production PHP workloads and worker mode concurrency.

Step 1: Deploy from the Azure Portal

  1. Open the FrankenPHP offer on the Azure Marketplace and select Get It Now, then Create.
  2. Choose your subscription, resource group and region.
  3. Set the VM size (Standard_B2s or larger), your SSH public key and the admin username azureuser.
  4. On the Networking tab, attach a Network Security Group that allows inbound TCP 22 from your management network and TCP 443 and TCP 80 from your users.
  5. Review and create. When the deployment completes, note the VM public IP address.

Step 2: Deploy from the Azure CLI

az vm create \
  --resource-group my-rg \
  --name my-frankenphp \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_rsa.pub \
  --public-ip-sku Standard

# open HTTPS (443), the HTTP redirect (80) and SSH (22)
az vm open-port --resource-group my-rg --name my-frankenphp --port 443 --priority 1001
az vm open-port --resource-group my-rg --name my-frankenphp --port 80  --priority 1002

Step 3: First boot

On the first boot the frankenphp-firstboot.service one shot unit generates a fresh per instance self signed TLS certificate (bound to this VM's IP address), writes an informational note to /root/frankenphp-info.txt, then starts frankenphp.service. This takes a few seconds. SSH in as azureuser:

ssh azureuser@<vm-public-ip>

Step 4: Confirm the service is running

Confirm frankenphp.service is active, check the FrankenPHP, PHP and Caddy versions, and confirm the server is listening on port 443 (HTTPS) and port 80 (the redirect):

sudo systemctl is-active frankenphp
HOME=/var/lib/frankenphp /opt/frankenphp/frankenphp version | head -1
sudo ss -tlnp | grep -E ':443 |:80 '

You should see active, a version line reporting FrankenPHP v1.12.4 PHP 8.5.8 Caddy v2.11.4, and the server listening on *:443 and *:80.

frankenphp.service reports active, the frankenphp binary reports version FrankenPHP v1.12.4 with bundled PHP 8.5.8 and Caddy v2.11.4, and ss shows the server listening on port 443 for HTTPS and port 80 for the HTTP to HTTPS redirect on all interfaces

Step 5: Verify PHP execution over HTTPS

The demo application at /var/www/html/index.php is processed by PHP: the served page reports the exact runtime PHP version and a computed value, so you can prove the FrankenPHP application server is executing PHP rather than serving a static file. Fetch it over HTTPS (the -k flag accepts the per instance self signed certificate):

curl -ks https://127.0.0.1/ | grep -iE 'rendered by PHP|execution check'

The response reports that the page was rendered by PHP 8.5.8 and shows the PHP execution check (6 x 7) evaluating to 42 on the server.

the curl command fetches the demo page over HTTPS and the output shows the page was rendered by PHP 8.5.8, the PHP SAPI is frankenphp, and the PHP execution check of 6 times 7 evaluates to 42, proving the FrankenPHP application server executed the PHP code

Open https://<vm-public-ip>/ in a browser (your browser will warn about the self signed certificate until you switch to a trusted certificate in Step 8). The demo landing page confirms the runtime PHP version and provides quickstart notes:

a web browser showing the demo FrankenPHP landing page served over HTTPS, headed FrankenPHP on cloudimg, confirming the page was rendered by PHP 8.5.8 with a table of the PHP version, SAPI frankenphp, server FrankenPHP and the six times seven execution check equal to 42, plus quickstart instructions for deploying your own PHP app

Step 6: HTTP to HTTPS redirect and the disabled admin API

Plain HTTP on port 80 permanently redirects (301) to HTTPS. Confirm the redirect, then confirm the Caddy admin API on port 2019 is not listening on any interface (secure by default):

curl -ksI http://127.0.0.1/ | grep -iE 'HTTP/|location'
sudo ss -tln | grep ':2019' || echo 'admin API disabled - nothing listening on :2019'

You should see HTTP/1.1 301 Moved Permanently with a Location: header pointing at the HTTPS URL, and confirmation that nothing is listening on port 2019.

the curl command against port 80 returns HTTP 301 Moved Permanently with a Location header pointing to the HTTPS URL, and the ss command confirms nothing is listening on port 2019 because the Caddy admin API is disabled, so the image is secure by default

Step 7: Deploy your own PHP application

The document root is /var/www/html. Copy your PHP application there (or point the Caddyfile root at another directory), then restart the service. Because the admin API is disabled, config and code changes are applied with a brief restart:

# copy your application into the document root
sudo rsync -a ./my-php-app/ /var/www/html/

# apply the change
sudo systemctl restart frankenphp

The php_server directive serves index.php as the front controller, so modern PHP frameworks such as Laravel and Symfony work out of the box once their public directory is the document root.

Step 8: Enable trusted automatic HTTPS for your own domain

FrankenPHP inherits Caddy's automatic HTTPS. If you own a domain and point its DNS A record at this instance, you can switch from the per instance self signed certificate to a trusted, automatically renewed certificate with a single Caddyfile edit. Replace the :443 block in /etc/frankenphp/Caddyfile with your domain name:

your-domain.com {
    root * /var/www/html
    php_server
}

Then restart the service. On the first request to your domain, FrankenPHP provisions a certificate automatically and renews it before expiry, with no Certbot dependency:

sudo systemctl restart frankenphp

Step 9: Review the configuration and info note

The whole server configuration is one readable file, and the first boot note records this instance's endpoint details:

sudo cat /etc/frankenphp/Caddyfile
sudo cat /root/frankenphp-info.txt

The Caddyfile shows the admin off global option, the :80 redirect block and the :443 block serving php_server from /var/www/html. The info note records the HTTPS and HTTP URLs for this instance and confirms that FrankenPHP has no login and no shared credential.

Step 10: Security recommendations

  • Restrict the management port. Allow inbound TCP 22 only from your management network, never 0.0.0.0/0.
  • Front your users with HTTPS only. Expose TCP 443 to your users and TCP 80 only so the redirect works. Switch to a trusted certificate for your own domain (Step 8) so browsers do not warn.
  • Keep the admin API disabled. This image ships with the Caddy admin API disabled (admin off). Leave it disabled unless you have a specific need, and if you enable it, bind it to loopback only, never to a network interface.
  • Keep the OS patched. The image ships fully patched with unattended-upgrades enabled, so security updates continue automatically. Reboot after kernel updates.
  • Run your application as least privilege. frankenphp.service already runs as the unprivileged frankenphp user with a minimal capability set and systemd sandboxing.

Step 11: Support and Licensing

FrankenPHP is open source software distributed under the MIT License. This cloudimg image bundles FrankenPHP 1.12.4 with its embedded PHP 8.5 interpreter and the Caddy 2 web server on Ubuntu 24.04 LTS, fully patched and hardened. cloudimg provides the packaging, configuration, security hardening and support; it does not modify the upstream FrankenPHP licence.

Deploy on Azure

Launch FrankenPHP on Ubuntu 24.04 from the cloudimg Azure Marketplace listing and follow this guide to deploy your PHP application in minutes.

Need Help?

cloudimg provides 24/7 technical support for this FrankenPHP image. Contact support@cloudimg.co.uk for help with worker mode tuning, Caddyfile configuration, HTTPS and certificate options, and deploying PHP frameworks.

All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.