Skip to content

feat: FrankenPHP runtime for Laravel and Symfony sites#229

Merged
geodro merged 2 commits intomainfrom
feat/frankenphp-runtime
Apr 22, 2026
Merged

feat: FrankenPHP runtime for Laravel and Symfony sites#229
geodro merged 2 commits intomainfrom
feat/frankenphp-runtime

Conversation

@geodro
Copy link
Copy Markdown
Owner

@geodro geodro commented Apr 20, 2026

Summary

Adds an opt-in FrankenPHP runtime as an alternative to the shared PHP-FPM container. Sites declare runtime: frankenphp (optionally runtime_worker: true) in .lerd.yaml, or switch at runtime with lerd runtime frankenphp [--worker]. Each FrankenPHP site spins up its own dunglas/frankenphp:php<version>-alpine container; nginx reverse-proxies to it on port 8000 and workers podman exec into it.

What's in

  • Site.Runtime / .lerd.yaml runtime: schema plus lerd runtime CLI that writes both fields and cleans them up on switch to fpm.
  • Framework.FrankenPHP adapter hook with Entrypoint, WorkerEntrypoint, Env, WorkerEnv, SupportsWorker. Built-in Laravel and Symfony adapters:
    • Laravel non-worker uses frankenphp php-server -r public/ for fresh-per-request dev ergonomics (code edits take effect immediately).
    • Laravel worker runs octane:start --server=frankenphp --workers=auto, with pcntl installed at container boot via the image's bundled install-php-extensions helper.
    • Symfony non-worker uses frankenphp php-server -r public/.
    • Symfony worker uses frankenphp php-server --worker=public/index.php --watch so file edits hot-reload without lerd restart.
    • Unknown frameworks fall back to frankenphp php-server rooted at the framework's public dir. mergeBuiltinFrankenPHP backfills the hook onto store framework defs that predate this feature.
  • Built-in Symfony framework detection (composer.json symfony/runtime, symfony/framework-bundle, or symfony.lock).
  • Per-site quadlet writer with restart-on-change so lerd isolate 8.3 and the dashboard's PHP picker actually pick up the new image tag.
  • Nginx FrankenPHP vhost reuses the custom-container template. secure, unsecure, pause/unpause, restart, unlink, and lerd start all handle the FrankenPHP branch.
  • Worker exec target branches three ways (FPM / custom container / FrankenPHP). SuccessExitStatus=1 130 143 on generated worker units so Symfony messenger:consume and similar workers stop cleanly without spurious Failed journal entries.
  • Web UI: orange FrankenPHP badge (with worker suffix in worker mode) next to the existing service badges; Xdebug toggle is hidden on FrankenPHP sites since shared FPM Xdebug state doesn't apply. API exposes runtime and runtime_worker on /api/sites. PHP version picker now routes FrankenPHP sites through the FrankenPHP link path instead of regenerating a broken FPM vhost.
  • TUI: runtime: frankenphp (worker) on the site detail info line.
  • site_runtime MCP tool plus updated SKILL / cursor / junie guidelines.
  • lerd init offers a FrankenPHP prompt when it detects signals (laravel/octane with OCTANE_SERVER=frankenphp, runtime/frankenphp, runtime/frankenphp-symfony, or a Containerfile.lerd FROM dunglas/frankenphp). lerd doctor surfaces the same hint on registered sites still on FPM.
  • docs/features/frankenphp.md covers runtime switching, worker vs non-worker tradeoffs, hot reload caveats per framework, and current limitations.

Tested

Scaffolded Laravel 12 + Octane and Symfony 7.4 + Messenger projects at ~/Code/lerd-fp-tests/{laravel-fp,symfony-fp}. Verified per request serving, worker-mode resident PHP, queue/messenger workers executing via podman exec into the FrankenPHP container, lerd runtime round-trips cleanly removing .lerd.yaml runtime fields, and hot reload works in non-worker mode for both frameworks plus Symfony worker mode.

Out of scope (follow-ups)

  • Xdebug support for FrankenPHP containers.
  • Custom extension install (lerd php:ext add) on FrankenPHP sites.
  • Laravel Octane --watch hot reload (needs chokidar-cli, ~150MB boot overhead). Workaround: non-worker mode for dev iteration, or php artisan octane:reload for quick worker refresh.

geodro added 2 commits April 20, 2026 20:52
Reconcile with #232 (slim MCP manifest): drop the per-site and per-service
tool variants (site_pause/unpause/restart/rebuild, service_pin/unpin) in
favor of main's site_control / service_control / stripe action dispatchers.
Keep site_runtime as a new standalone tool and add its row to
docs/features/mcp.md.
@geodro geodro merged commit ced9d05 into main Apr 22, 2026
3 checks passed
geodro added a commit that referenced this pull request Apr 22, 2026
…, install/uninstall polish (#241)

- Bump internal/version to 1.18.0-beta.1.
- CHANGELOG entry covering all 11 PRs since v1.17.1 (#229 through #240)
  in Keep-a-Changelog sections: Added / Changed / Fixed / Docs / CI.
  Breaking change is #232 (slim MCP tool manifest, merged action pairs).
- docs/getting-started/installation.md: uninstall section now describes
  the four opt-in prompts (data, MCP integration, mkcert CA, images) and
  --force semantics.
- docs/troubleshooting.md: new entry for the aardvark-dns drift case
  (every DNS lookup stalling ~5 seconds after a dual-stack migration).
- docs/usage/lifecycle.md: new info block describing stale-site
  auto-cleanup — fsnotify fast path on parked dirs, 30s sweep across
  the full registry, eventbus publish so the dashboard refreshes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant