L lerd geodro/lerd v1.24.1…v1.25.0 ↗
ENGINEERING DIGEST · 2026-06-11 → 2026-06-17

v1.25.0

IDLE-SUSPEND · BUN RUNTIME · CUSTOM PHP IMAGES · FRANKENPHP PARITY

This release adds activity-driven worker suspension, makes bun a first-class JS runtime, and opens up how a PHP site's image is built. FrankenPHP reaches extension parity with FPM, the dashboard grows a Laravel Doctor, a Resources widget and embedded service dashboards, and the MCP server gains a logs tool. Underneath, the localhost/HTTPS, runtime-switch and shutdown paths get hardened.

66
commits
39
merged PRs
312
files
+24.9k
insertions
−1.4k
deletions
2
contributors
ui/web · dashboard + i18n 31% cli 19% watcher + idle 8% config + doctor 8% logsource + stats 7% podman · tui · mcp · rest 27%
01

Idle-Suspend

FEAT

lerd now watches each site for activity and, after a configurable idle timeout, gracefully suspends that site's suspendable workers — queue, scheduler, Horizon, Reverb, the Stripe listener, and the host Vite dev server — bringing them back the moment the site is used again. The engine lives in lerd-watcher; worktrees keep their own per-branch timers, and Vite builds its assets once on suspend so the site keeps serving while idle. Off by default.

ACTIVITY → SUSPEND → RESUMEper site · per worktree
signals
request · cli · mcp · file save
nginx access feed + watchers
──►
touch
last-active
idle-activity.json
──►
timeout?
elapsed > idle timeout
default off · e.g. 30m
──►
suspend
stop workers
Vite builds, then stops
next signal
resume
workers back up
suspended ≠ failed — the heal surfaces leave a suspended worker alone. off resumes everything, and the engine reconciles against reality across installs and restarts so nothing is stranded.
activity-driven worker suspensionbash
lerd idle on                 # enable globally (off by default)
lerd idle timeout 30m          # suspend after 30m of no activity
lerd idle pin myapp.test       # exempt a site from ever sleeping
lerd idle status              # per-site policy + last-active
lerd idle off                 # disable + resume every worker
FEAT

Suspend engine #525

Activity-driven suspension of a site's workers with a configurable timeout and per-site pinning.

internal/idle
FEAT

Moved into watcher #540

The engine runs inside lerd-watcher, with per-worktree timers and access-feed signalling.

internal/watcher/idle_engine.go
FIX

Enable / disable edges #541 #542

Sleep idle sites on enable, resume every worker on disable, robustly across restarts.

internal/idle
02

bun as a JS runtime

FEAT

bun can now drive a site's JS install, dev, and build on the host, auto-selected for bun projects or when Node is unmanaged and only bun is present — a one-click toggle in the Node dropdown, the TUI picker, or lerd js:runtime. lerd never installs or version-manages bun; the user installs it and bun upgrade self-updates.

RUNTIME SELECTION.lerd.yaml · js_runtime
js_runtime: auto
decide
the default
──►
bun project?
bun.lockb · bunfig · packageManager
yes → bun
──►
node unmanaged
& only bun?
yes → bun
no system node
──►
otherwise
fnm-managed Node
22 · 24
js_runtime: bun always uses bun · js_runtime: node always uses fnm Node — the picker just writes this field.
host pin + opt-in in-container bunbash
lerd js:runtime bun         # pin this site to bun (or node | auto)
lerd php:bun install        # musl bun into a persistent /root/.bun volume
lerd php:bun version        # report the in-container bun
lerd php:bun remove         # clear it and the shared volume
FEAT

Host bun runtime #506

Toggle a site's JS runtime to bun for install, dev, and build.

FEAT

js:runtime #522

Pin the runtime from the CLI, the equivalent of the dashboard's bun/Node toggle.

FEAT

php:bun remove #521

Clear the in-container bun and its shared persistent volume.

03

Shaping the PHP image

FEAT

A PHP site gains two new ways to control the image it runs on. lerd php:pkg bakes extra Alpine packages into the FPM image, persisted under php.packages so they survive php:rebuild and base-image updates. For anything more, a project ships its own Containerfile built FROM the lerd base and is served from it directly.

IMAGE LAYERINGshared image vs custom site
base
ghcr.io php-fpm
per PHP version
──►
+ php:ext
compiled extensions
redis · gd · pdo · …
──►
+ php:pkg
apk packages
saved in config · re-applied on rebuild
or — custom
FROM lerd base + Containerfile
this site only
php:pkg layers onto the shared image, not the published base — the packages are reapplied on every rebuild.
extra Alpine packagesbash
lerd php:pkg add imagemagick ghostscript   # bake + rebuild
lerd php:pkg list                           # what's layered on this version
lerd php:pkg remove ghostscript             # drop + rebuild
FEAT

php:pkg #508

Add runtime Alpine packages (CLI tools, libraries) to the FPM image, persisted in config.

FEAT

Custom Containerfile #511

Serve a PHP site from a bespoke image built FROM the lerd base.

04

FrankenPHP & Octane

FEAT

The FrankenPHP runtime reaches extension and debug-tooling parity with FPM. lerd builds a derived image carrying the same runtime extension set the FPM image ships — present from first boot — and the dump bridge, Debug window and Xdebug toggle all apply to a FrankenPHP site. Only the SPX profiler stays FPM-only.

CapabilityFPMFrankenPHP
Runtime extensions (redis, gd, pdo, intl, imagick…)✓ new
dump bridge · Debug window · Xdebug toggle✓ new
Octane auto-reload on file changes octane:reload
In-container Pest browser testing shared FPM container
xdebug pause into a running worker— FPM-only (xdebugctl)
SPX profiler— per-request only
FEAT

Extension parity #527

A derived FrankenPHP image carries the full FPM extension set, baked once for the ZTS runtime.

FEAT

Octane auto-reload #518 #520

octane:reload on runs Octane under a file watcher, kept through lerd install.

FEAT

Pest browser · PHP picker #516 #523

In-container Pest browser testing on musl chromium; the FrankenPHP PHP picker only lists runnable versions, never downgrading 8.5.

05

Dashboard & MCP

FEATUI

The dashboard and the AI surface both grow. A Laravel Doctor runs the framework's own health checks with one-click fixes, a Resources widget accounts for lerd's whole footprint, and RabbitMQ and RedisInsight surface their management UIs same-origin inside lerd-ui. The MCP server gains an eleventh grouped tool, logs.

site
link · domains · group
service
start · presets · config
db
set · move · dump
env
setup · get · set
runtime
fpm ↔ frankenphp
worker
start · heal · mode
exec
artisan · composer · vendor
framework
install · scaffold · setup
diag
status · doctor · xdebug
logs ✦
sources · fetch · grep/since/level
worktree
add · list · remove
logs is new in v1.25.0 — read any log source with grep / since / until / level / lines filtering. #515
UI

Laravel Doctor #509 #528

On-demand health checks sourced from the framework definition, with one-click fixes. Worktree-aware, warns only on required env keys. Also a TUI tab.

UI

Resources widget #536

Total CPU and memory across the lerd-* containers plus lerd's own host processes, with a ranked list of the heaviest.

UI

Clear logs · APP_NAME #510 #507

Reclaim disk from the App Logs tab behind a confirm, and title site tiles by a custom Laravel APP_NAME.

FEAT

Embedded service dashboards #514

RabbitMQ and RedisInsight UIs proxied same-origin under /_svc/, no new tab.

UI

TUI 1.24 integrations #529

bun picker, FrankenPHP version limit, idle display, Doctor tab, open a service dashboard with O, workers grouped by site.

i18n

Six more languages #505

Simplified Chinese, Japanese, Romanian, Italian, Polish, Vietnamese — fourteen total.

06

Hardening

FIXSEC

Alongside the features, this release tightens the localhost/HTTPS, runtime-switch, FrankenPHP relink and shutdown paths, and clears dependency advisories.

FIX PIPELINEv1.24.1 → v1.25.0
1
Consistent localhost & HTTPS

.localhost mode matches across init, secure and the installer; a committed HTTPS choice survives the wizard; a secured project stays on http when managed DNS is off, with SetSecured gated so UI and MCP match the CLI. #533 #537

2
Runtime switching & FrankenPHP relinks

Workers stay healthy when a site switches PHP runtime; a stale FrankenPHP container or orphaned quadlet is reconciled on relink; a paused runtime site no longer autostarts at boot. #524 #534 #526

3
Reliable shutdown

lerd stop retries systemd jobs reported "canceled" so it leaves nothing running on Linux; FrankenPHP worker quadlets translate to launchd correctly when Exec= is quoted on macOS. #532 #519

4
Dependencies & review

esbuild, vite and form-data bumped to clear advisories, plus pre-release and smoke-test hardening across log filtering, the dashboard service proxy and idle reconciliation. #535 #543 #547