Running k0smos with FrankenPHP — Quickstart
Status: ALPHA. Native FrankenPHP support is experimental and not yet a production-blessed runtime for k0smos. It runs in classic request mode only (no worker mode). On Windows it is web-only (no
queue:work, no bundledredisextension). Validate it in a staging environment before any production use, and keep the supported runtimes (php -Sfor local dev, Docker FrankenPHP) as the fallback.The native runtime files live under
deploy/frankenphp/. Environment/deployment details: Complete k0smos reference § 13.1.
What this gives you
A single k0smos instance served by the native FrankenPHP binary (FrankenPHP =
Caddy + an embedded thread-safe PHP). The server config lives in
deploy/frankenphp/Caddyfile and is parameterized by two Caddy environment
placeholders resolved at config load:
SERVER_NAME— the Caddy site address (defaulthttp://localhost:8080). The scheme decides TLS:http://…serves plain HTTP; a bare hostname such ask0smos.example.comturns on Caddy automatic HTTPS.K0SMOS_PUBLIC_ROOT— the document root (defaultpublic, correct from the repo root).
Prerequisites (do these first)
frankenphp run is not a fresh-install command. Before starting it:
- Install Composer dependencies on the host (
composer install). - Configure
JWT_SECRETin.env.local, process env, or the service-manager env. - Ensure the target tenant JSON exists under
config/tenants/and select it viaTENANT_ENV=<host>(or rely on HTTP host routing). - Install/migrate that tenant's database intentionally (
php bin/console migrate). - Make runtime directories writable:
var/db,var/cache,log,tmp/sessions, and any tenant storage/upload paths. - Have Redis reachable only if queue/AI-async/rate-limit features are enabled.
- Build the active theme's public assets.
Linux / macOS
From the repository root:
# local plain HTTP on http://localhost:8080
TENANT_ENV=localhost APP_ENV=production APP_DEBUG=false \
frankenphp run --config deploy/frankenphp/Caddyfile
# production HTTPS for a real hostname (Caddy manages the certificate)
SERVER_NAME=k0smos.example.com TENANT_ENV=k0smos.example.com \
APP_ENV=production APP_DEBUG=false \
frankenphp run --config deploy/frankenphp/Caddyfile
Windows (web-only, alpha)
FrankenPHP embeds a thread-safe (TS) PHP. If the host also has a
non-thread-safe (NTS) PHP (e.g. Scoop's php-nts), it exports PHPRC /
PHP_INI_SCAN_DIR; FrankenPHP inherits them and every extension fails to
load (Module compiled with …NTS… / PHP compiled with …TS…). Give FrankenPHP
its own php.ini once:
# 1. Copy the template and edit extension_dir to your FrankenPHP ext folder
# (default C:\Users\<you>\.frankenphp\ext)
Copy-Item deploy/frankenphp/php.windows.ini.example deploy/frankenphp/php.windows.ini
# 2. Point FrankenPHP at it and clear the NTS scan dir (this process only)
$env:PHPRC = "C:\ws\www\dev\k0smos\deploy\frankenphp\php.windows.ini"
$env:PHP_INI_SCAN_DIR = ""
# 3. Run (default site address is plain HTTP http://localhost:8080)
$env:TENANT_ENV = "localhost"
$env:APP_ENV = "production"
$env:APP_DEBUG = "false"
& "$env:USERPROFILE\.frankenphp\frankenphp.exe" run --config deploy/frankenphp/Caddyfile
Windows limitations: persistent queue:work supervision is unsupported because
Windows has no pcntl signal handling, and php_redis.dll is not bundled.
Redis-backed features must run under Docker or Linux. A bounded DBAL-backed
worker can be invoked periodically through Windows Task Scheduler:
frankenphp php-cli bin/console queue:work `
--host=k0smos.example.com `
--queue=default `
--max-idle=10 `
--max-jobs=100
Schedule one task per queue, prevent overlapping task instances, and set the
project root as the working directory. --max-idle is required for an empty
worker to exit; --max-jobs only counts completed or failed jobs. This
workaround is suitable for low- or medium-volume queues, adds up to one
scheduling interval of latency, and does not replace supervised Linux workers.
Set $env:SERVER_NAME = "k0smos.example.com" for production HTTPS.
Verify it works
# embedded PHP CLI bootstraps the app
frankenphp php-cli bin/console list
# config parses
frankenphp validate --config deploy/frankenphp/Caddyfile
# while the server runs (default plain HTTP):
# GET / -> 200 (home page renders, tenant resolved)
# GET /admin -> 302 -> /login?redirect=%2Fadmin
# GET /favicon.ico -> 200 (static file_server)
# GET /<unknown> -> 404
On Windows PowerShell, use Invoke-WebRequest http://localhost:8080/ to check.
If SERVER_NAME is a bare hostname, Caddy serves HTTPS — request https://…
(plain HTTP against the TLS listener returns 400).
Not in scope (alpha)
- Worker mode (in-memory app state across requests) — k0smos stays in classic mode until a request-state reset contract exists.
- Single-binary packaging.
- Native Windows production / Redis-backed features on Windows.