Install the CLI. See the install docs.
Log in:
runway login
Add an SSH key. See key setup, or use the shortcut:
runway local key setup
Most PHP apps are a single web process, and Runway starts that for you without any configuration. Some apps need more:
init process to apply database migrations on every deploy, before the
new code starts serving traffic;worker process to consume a queue in the background: a Symfony
Messenger transport, a Laravel queue, or a custom consumer.Runway orchestrates this with a Procfile. The process types and their general
semantics are the same for every language; see the
multi-process guide and the
Procfile reference for the
full picture. This page covers the PHP-specific parts and gives a working
example.
Every Procfile needs a web: process.
When there is no Procfile, Runway starts that default web process for you:
php-fpm together with the web server. To run the same web process from a Procfile, use:
web: procmgr-binary /layers/paketo-buildpacks_php-start/php-start/procs.yml
procmgr-binary and the generated procs.yml both come from Runway’s PHP buildpack.
That YAML file already contains the correct php-fpm and web-server
commands for the server you build with, so you don’t have to hand-write them.
A minimal, framework-free app with all three process types. The layout:
.
├── Procfile
├── bin
│ ├── init.php
│ └── worker.php
└── htdocs
└── index.php
<?php
echo "Hello from the web process!\n";
init runs once per deploy, before web starts. If it exits non-zero, the
deploy fails and the previous version keeps serving. This is where migrations
belong: Runway has no separate “release” phase.
<?php
// A real app runs its migration tool here and lets a failure abort the deploy.
fwrite(STDERR, "running migrations...\n");
// ... apply migrations ...
fwrite(STDERR, "migrations done\n");
worker runs continuously and does not listen on a port. It can reach the
web process over the loopback interface on PORT. Keep it running: if it
exits, the whole app restarts.
<?php
while (true) {
// pull one job off your queue and handle it
fwrite(STDERR, sprintf("worker tick %s\n", date(DATE_ATOM)));
sleep(5);
}
Because the Procfile replaces the default process list entirely, web: is
declared explicitly next to init and worker:
web: procmgr-binary /layers/paketo-buildpacks_php-start/php-start/procs.yml
init: php bin/init.php
worker: php bin/worker.php
$ runway app create
$ runway app config set BP_PHP_SERVER=httpd
$ git add -A && git commit -m "initial commit"
runway app deploy
Run runway app open to see it live, or check the Runway UI. Check runway app logs if something’s off.
Check that all three process types are running:
$ runway app ps
$ runway app logs
In a real project the init and worker commands call your framework’s tools
instead of the stand-in scripts above. The web: line stays the same.
web: procmgr-binary /layers/paketo-buildpacks_php-start/php-start/procs.yml
init: php bin/console doctrine:migrations:migrate --no-interaction
worker: bin/worker
bin/console is Symfony’s CLI entrypoint. A Messenger consumer has to stop and
come back periodically: after a transient database error Doctrine’s
EntityManager can be left broken and fail every following message.
messenger:consume --time-limit does the stopping, but the process has to be
brought straight back up itself, so point the worker at a small committed
script that loops:
#!/bin/sh
# Restart loop for the Messenger consumer: --time-limit exits are intentional
# (poisoned EntityManager recovery), the process must come back up.
while true; do
php bin/console messenger:consume async --time-limit=300 --memory-limit=256M
sleep 1
done
web: procmgr-binary /layers/paketo-buildpacks_php-start/php-start/procs.yml
init: php artisan migrate --force
worker: php artisan queue:work
--force lets migrate run without the interactive confirmation Laravel asks
for in production. queue:work runs the queue in one long-lived process. If a
job can leave the database connection broken, wrap it in the same loop shown for
Symfony above.
All processes run from the same image and share the configuration you set
with runway app config set, but each runs in its own environment and cannot
share local memory with the others.
worker process can reach the web process over the loopback interface
on the port web listens on.web process has access to the /data volume.To inspect what is running, and to open a shell in the worker container:
runway app ps
runway app exec -w sh
Procfile reference: all supported process types and rulesphp-startpdo_pgsql and other extensions your worker may need