RoadRunner on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and configuration of RoadRunner on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. 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, RoadRunner starts a pool of long-lived PHP worker processes once and keeps them warm, then dispatches each incoming request to a worker over an efficient pipes relay. The result is dramatically 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.
The image ships the official prebuilt RoadRunner binary (version 2025.1.15, MIT), PHP 8.3 and Composer, plus a minimal demo PSR-7 worker application that is already vendored and running so the whole stack is proven the moment the instance boots. RoadRunner binds only to loopback and is exposed to the network exclusively through an nginx reverse proxy on port 80 that is ready for your TLS certificate.
What is included:
-
RoadRunner 2025.1.15 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(dependenciesspiral/roadrunner-http+nyholm/psr7already vendored) -
Configuration at
/etc/roadrunner/.rr.yamlwith the HTTP, status and RPC listeners all bound to127.0.0.1 -
roadrunner.servicesystemd unit auto-starting on boot, running as theroadrunnersystem user -
nginx.servicereverse proxy on TCP 80 forwarding to the RoadRunner HTTP listener on127.0.0.1:8080 -
roadrunner-firstboot.servicesystemd oneshot that 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/health),127.0.0.1:6001(RPC control channel) -
Ubuntu 24.04 LTS base with latest security patches applied at build time
-
Azure Linux Agent for seamless cloud integration and SSH key injection
-
24/7 cloudimg support with 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
Step 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 cloudimg 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; scale the size and thenum_workerssetting up together for production workloads. -
Provide an administrative username (
azureuser) and 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. RoadRunner and nginx start automatically once the instance boots.
Step 2: Deploy from the Azure CLI
You can deploy the same image from the command line. Replace the placeholders with your own values, then open the HTTP port to the clients that need it.
az vm create \
--resource-group my-roadrunner-rg \
--name my-roadrunner-vm \
--image <cloudimg-roadrunner-image-urn> \
--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
Step 3: Connect via SSH
Connect to the instance as azureuser with your private key:
ssh azureuser@<vm-ip>
Step 4: Verify the RoadRunner service
RoadRunner and nginx are enabled and start on boot. Confirm 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 you can read at any time:
sudo cat /root/roadrunner-info.txt

Step 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'
You will see 127.0.0.1:8080 (RoadRunner HTTP), 127.0.0.1:2114 (status/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.
Step 6: Check the RoadRunner version and health endpoint
Confirm the installed RoadRunner version and 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 and the health endpoint returns HTTP 200 when the HTTP plugin has ready workers. Use this loopback endpoint for readiness and liveness checks.

Step 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
You will see 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 is what keeps per-request overhead low.
Step 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 it answers through nginx and observe the same warm worker serving repeated requests:
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 — proof that the interpreter is kept warm between requests rather than restarted:
for i in 1 2 3 4 5; do curl -s http://localhost/healthz; echo; done

Step 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
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) and rpc.listen (the control channel used by rr workers and rr reset).

Step 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
Step 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> and configure the nginx server block to terminate HTTPS while continuing 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 handles TLS termination.
Step 12: Managing the service
RoadRunner is managed by systemd. 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 — it recycles the worker pool gracefully while the server keeps accepting requests (as used in Step 10). For a full stop, start or restart of the service, and to follow the structured JSON logs when troubleshooting, use the standard systemd commands:
sudo systemctl restart roadrunner
sudo journalctl -u roadrunner -f
Step 13: Troubleshooting
-
The application returns 502 through nginx — RoadRunner may not be running. Check
sudo systemctl status roadrunnerand the journal withsudo journalctl -u roadrunner -n 50 --no-pager. A PHP fatal error in the worker will show here. -
rr workersshows no workers — the workers failed to boot. Runsudo -u roadrunner php /var/www/roadrunner-demo/worker.phpbriefly to surface a PHP error, or check the journal. -
Health endpoint returns 500 — the HTTP plugin has no ready workers. Inspect the journal and confirm
server.commandin/etc/roadrunner/.rr.yamlpoints at a valid worker script. -
A worker leaks memory — lower
http.pool.supervisor.max_worker_memoryin/etc/roadrunner/.rr.yamlso RoadRunner recycles the worker sooner, thensudo rr reset -c /etc/roadrunner/.rr.yaml.
Step 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/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
roadrunnersystem user (the default) rather than root.
Step 15: Support and Licensing
RoadRunner is distributed under the MIT license. This cloudimg image is a repackaged open source product with additional charges for cloudimg 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. cloudimg maintains the image with security patches and provides a paired deployment guide for every release.
Need Help?
cloudimg provides 24/7 support with a guaranteed 24 hour response SLA. Contact support through the details on your Azure Marketplace listing.