Deployment guide

RoadRunner

Overview

RoadRunner is a high-performance PHP application server and process manager written in Go. Instead of re-bootstrapping the PHP interpreter on every request the way PHP-FPM does, it starts a pool of long-lived PHP worker processes once and keeps them warm. It then dispatches each incoming request to a worker over an efficient pipes relay. The result is lower per-request overhead, predictable memory use and built-in supervision, all from a single static binary that also adds HTTP/2, gRPC, background jobs and key-value plugins.

This SecureAnyCloud image runs RoadRunner on Ubuntu 24.04 on Azure. It includes a minimal demo PSR-7 worker application that is already vendored and running, so the whole stack works the moment the instance boots. RoadRunner binds only to loopback, and nginx on port 80 is the only way in from the network. nginx is ready for your TLS certificate.

What you get:

  • RoadRunner 2025.1.15 (MIT), the official prebuilt Go binary at /usr/local/bin/rr, SHA256-verified at build time
  • PHP 8.3 CLI with the extensions the workers need, plus Composer
  • A demo PSR-7 worker application at /var/www/roadrunner-demo, with its dependencies spiral/roadrunner-http and nyholm/psr7 already vendored
  • Configuration at /etc/roadrunner/.rr.yaml, with the HTTP, status and RPC listeners all bound to 127.0.0.1
  • The roadrunner.service systemd unit, which starts on boot and runs as the roadrunner system user
  • An nginx.service reverse proxy on TCP 80 that forwards to the RoadRunner HTTP listener on 127.0.0.1:8080
  • The roadrunner-firstboot.service systemd oneshot, which resolves the customer-facing URL and writes a service-info note on first boot
  • Listeners: TCP 80 (nginx, public), 127.0.0.1:8080 (RoadRunner HTTP), 127.0.0.1:2114 (status and health), 127.0.0.1:6001 (RPC control channel)
  • An Ubuntu 24.04 LTS base with the latest security patches applied at build time
  • The Azure Linux Agent for cloud integration and SSH key injection
  • 24/7 SecureAnyCloud support with a guaranteed 24-hour response SLA

Prerequisites

  • An active Azure subscription
  • A subscription to the RoadRunner on Ubuntu 24.04 listing on Azure Marketplace
  • An SSH key pair for administrative access
  • Basic familiarity with the Linux command line, systemd and PHP
1

Deploy from the Azure Portal

Sign in to the Azure Portal. Search the Marketplace for RoadRunner on Ubuntu 24.04 LTS and select the SecureAnyCloud listing.

Choose Create, then select or create a resource group and region.

Pick a VM size. Standard_B2s (2 vCPU, 4 GiB) is a comfortable starting point for the demo application. For production workloads, scale up the size and the num_workers setting together.

Enter an administrative username (azureuser) and paste your SSH public key.

On the Networking tab, allow inbound TCP 22 (SSH) from your management IP and TCP 80 (HTTP) from the clients that will reach the application.

Review and create the VM. RoadRunner and nginx start automatically once the instance boots.

2

Deploy from the Azure CLI

Deploy the same image from the command line. Replace the values in angle brackets with your own, then open the HTTP port to the clients that need it.

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:roadrunner-ubuntu-24-04:default:latest
az vm create \
  --resource-group my-roadrunner-rg \
  --name my-roadrunner-vm \
  --image sac1647283583153:roadrunner-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard
az vm open-port --resource-group my-roadrunner-rg --name my-roadrunner-vm --port 80 --priority 900
3

Connect over SSH

Connect to the instance as azureuser with your private key:

ssh azureuser@<vm-ip>
4

Verify the RoadRunner service

RoadRunner and nginx are enabled and start on boot. Confirm that both units are active:

sudo systemctl start roadrunner nginx
sudo systemctl is-active roadrunner nginx

Both should report active. The image also writes a per-VM service-info note that you can read at any time:

sudo cat /root/roadrunner-info.txt
5

Confirm the secure-by-default listeners

Every RoadRunner listener is bound to 127.0.0.1. Only nginx on port 80 faces the network. Confirm the binding:

sudo ss -tlnp | grep -E '127.0.0.1:(8080|2114|6001)|:80'

The output lists 127.0.0.1:8080 (RoadRunner HTTP), 127.0.0.1:2114 (status and health), 127.0.0.1:6001 (RPC control channel) and nginx listening on port 80. Nothing exposes RoadRunner directly to the network. nginx is the single public entry point and is ready for your TLS certificate.

6

Check the RoadRunner version and health endpoint

Confirm the installed RoadRunner version, then query the built-in status plugin's health endpoint on loopback:

rr --version curl -s -o /dev/null -w 'health: HTTP %{http_code}\n' 'http://127.0.0.1:2114/health?plugin=http'

The version prints as rr version 2025.1.15. The health endpoint returns HTTP 200 when the HTTP plugin has ready workers. Use this loopback endpoint for readiness and liveness checks.

7

Inspect the worker pool

RoadRunner keeps a pool of warm PHP workers. List them over the loopback RPC channel:

rr workers -c /etc/roadrunner/.rr.yaml

The output shows one row per worker (four by default) with its PID, status, number of executions, memory use and age. These are long-lived processes. RoadRunner dispatches requests to them and recycles a worker only after it hits max_jobs or the memory limit, which keeps per-request overhead low.

8

The demo application and warm-worker reuse

A minimal demo PSR-7 worker application is already running. It renders a small status page and a JSON health route. Confirm that it answers through nginx:

curl -s -o /dev/null -w 'GET / -> HTTP %{http_code}\n' http://localhost/
curl -s http://localhost/healthz

The /healthz route returns the serving worker's PID and a per-worker request counter. Call it several times and watch the counter climb on a reused worker. This shows that the interpreter stays warm between requests instead of restarting:

for i in 1 2 3 4 5; do curl -s http://localhost/healthz; echo; done
9

Review the configuration

The RoadRunner configuration lives at /etc/roadrunner/.rr.yaml. It holds no secrets and binds every listener to loopback:

cat /etc/roadrunner/.rr.yaml

The key settings are:

  • server.command: the PHP worker entry point
  • http.address: the loopback HTTP listener nginx proxies to
  • http.pool.num_workers: how many warm workers to keep
  • status.address: the loopback health endpoint
  • rpc.listen: the control channel used by rr workers and rr reset
10

Replace the demo app with your own PSR-7 application

The demo worker is a starting point. To run your own application, replace the worker entry point, or point server.command in /etc/roadrunner/.rr.yaml at your framework's RoadRunner worker. Then reload RoadRunner. Most modern PHP frameworks ship a RoadRunner integration.

Copy your worker in and vendor its dependencies with Composer:

sudo cp your-worker.php /var/www/roadrunner-demo/worker.php cd /var/www/roadrunner-demo && sudo -u roadrunner composer require your/framework

Then reload the workers gracefully, with zero downtime. Running requests finish on the old workers while new requests go to fresh ones:

sudo rr reset -c /etc/roadrunner/.rr.yaml
11

Enable HTTPS for your domain

nginx is the public entry point and is ready for TLS. Point a DNS record at the instance, then obtain a certificate for <your-domain>. Configure the nginx server block to terminate HTTPS while it continues to proxy to 127.0.0.1:8080.

A typical approach uses the nginx plugin for a Let's Encrypt client:

sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <your-domain>

Certbot edits the nginx configuration to add the TLS listener and sets up automatic renewal. RoadRunner itself is unchanged. It keeps serving the application on loopback while nginx terminates TLS.

12

Managing the service

systemd manages RoadRunner. Check the service status:

sudo systemctl status roadrunner --no-pager --lines=0

To reload after a configuration or application change without dropping connections, prefer rr reset over a full restart, as in Step 10. It recycles the worker pool gracefully while the server keeps accepting requests.

To stop, start or restart the service, or to follow the structured JSON logs when troubleshooting, use the standard systemd commands:

sudo systemctl restart roadrunner
sudo journalctl -u roadrunner -f
13

Troubleshooting

The application returns 502 through nginx. RoadRunner may not be running. Check sudo systemctl status roadrunner and the journal with sudo journalctl -u roadrunner -n 50 --no-pager. A PHP fatal error in the worker shows up there.

rr workers shows no workers. The workers failed to boot. Run sudo -u roadrunner php /var/www/roadrunner-demo/worker.php briefly to surface a PHP error, or check the journal.

The health endpoint returns 500. The HTTP plugin has no ready workers. Inspect the journal and confirm that server.command in /etc/roadrunner/.rr.yaml points at a valid worker script.

A worker leaks memory. Lower http.pool.supervisor.max_worker_memory in /etc/roadrunner/.rr.yaml so RoadRunner recycles the worker sooner. Then run sudo rr reset -c /etc/roadrunner/.rr.yaml.

14

Security recommendations

  • Keep every RoadRunner listener on loopback (the default) and expose the application only through nginx.
  • Terminate TLS at nginx (Step 11) and restrict the inbound port 80 and 443 rules to the clients that need them.
  • Restrict inbound SSH (TCP 22) to your management IP range in the Azure network security group.
  • Apply OS updates regularly with sudo apt-get update && sudo apt-get upgrade.
  • Run your application code as the unprivileged roadrunner system user (the default), not as root.
15

Support and Licensing

RoadRunner is distributed under the MIT license. This SecureAnyCloud image is a repackaged open source product, with additional charges for SecureAnyCloud support services.

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.

Deploy on Azure

Find this image on the Azure Marketplace and deploy it in minutes. SecureAnyCloud maintains the image with security patches and provides a paired deployment guide for every release.

Need Help?

SecureAnyCloud provides 24/7 support with a guaranteed 24-hour response SLA. Contact support through the details on your Azure Marketplace listing.

Deploy on Azure

Launch RoadRunner on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure