Documentation menu

Deployment

php-via runs as a long-lived OpenSwoole server process. Unlike traditional PHP-FPM setups, the process stays running — all state lives in memory. This page covers production setup with systemd, Caddy, and process management.

Requirements

  • PHP 8.4+
  • OpenSwoole extension (pecl install openswoole or packaged as php8.4-openswoole)
  • A reverse proxy (Caddy or nginx) to handle TLS and serve static assets

systemd service

Create a service unit at /etc/systemd/system/myapp.service:

[Unit]
Description=My php-via app
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/php /var/www/myapp/app.php
Restart=on-failure
RestartSec=5s
Environment=APP_ENV=production

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable myapp
systemctl start myapp
systemctl status myapp

Caddy reverse proxy

Caddy handles TLS automatically. The key requirement is disabling response buffering for SSE (the /_sse endpoint streams indefinitely):

myapp.example.com {
    # Serve static assets directly
    handle /public/* {
        root * /var/www/myapp
        file_server
    }

    # Proxy everything else to OpenSwoole
    reverse_proxy localhost:3000 {
        # Required for SSE: disable buffering
        flush_interval -1

        # Optional: pass real IP to app
        header_up X-Real-IP {remote_host}
    }
}

Production config

Tune php-via for production in your app.php:

$config = (new Config())
    ->withHost('127.0.0.1')    // bind locally, let Caddy handle external
    ->withPort(3000)
    ->withLogLevel(getenv('APP_ENV') === 'production' ? 'info' : 'debug')
    ->withTemplateDir(__DIR__ . '/templates')
    ->withStaticDir(__DIR__ . '/public')
    ->withSwooleSettings([
        'worker_num'        => swoole_cpu_num(),
        'max_request'       => 0,      // long-lived process
        'enable_coroutine'  => true,
    ]);

Graceful restart

To deploy new code without dropping connections, reload the service:

systemctl reload myapp
# or:
kill -USR1 $(cat /var/run/myapp.pid)

php-via listens for SIGTERM and SIGINT and calls your onShutdown callbacks before stopping. Register cleanup callbacks for any timers or open resources.

Multiple instances

php-via uses OpenSwoole's in-memory shared state for the scope system. This means multiple instances do not share state — a broadcast on instance A won't reach clients on instance B. For now, horizontal scaling requires a sticky-session load balancer and the understanding that per-route state won't be globally consistent.

Static assets

Configure a static directory to serve files directly from OpenSwoole in development:

$config->withStaticDir(__DIR__ . '/public');

In production, serve static files from Caddy (or nginx) directly — it's faster and avoids passing binary content through PHP.

Next steps