L lerd lerd-env/lerd v1.28.0…v1.29.0 ↗
ENGINEERING DIGEST · 2026-07-08 → 2026-07-15

v1.29.0

OUT OF GO, INTO THE STORE · and onto atomic images

The 1.29.0 line pushes more of lerd's behaviour out of Go and into versioned store YAML: a framework definition now declares the nginx config it needs, an env file that is a returned PHP array rather than a dotenv, the php.ini its CLI runs under, the services it cannot run without, and what its worktrees need to come up, so Magento and the like are wired through data, not name-checks in Go. On top of that sites group into workspaces, worker self-heal spots a worker that is running but no longer serving, and the dashboard and TUI grow request-timing surfaces. The stable release also makes lerd a first-class citizen on Fedora Silverblue, Bazzite and the other ostree spins, the 90-second container-start stall is gone and browser HTTPS trust is explained rather than silently skipped.

70
commits
58
merged PRs
408
files
+23,386
insertions
−2,385
deletions
2
contributors
internal/ui 26% internal/cli 14% internal/tui 10% internal/config 10% internal/podman 7% rest 33%
01

Store-Driven Frameworks

FEAT

The design law is that no feature knows a framework's name in Go, behaviour is driven by versioned YAML in the store. This cycle moved five more decisions across the line: a definition now declares the nginx snippet it needs, an env format that is a returned PHP array instead of a dotenv, the php.ini its CLI runs under, the services it cannot run without, and what its worktrees need to come up. Magento is the proof: it comes up on its own domain, from its own env.php, with an isolated database cloned from main and app:config:import run for it, and nothing in Go knows what any of that means.

frameworks/<name>/<version>.yamlone definition drives the whole site
definition
magento/2.yaml
versioned store YAML
──►
nginx
vhost snippet
its own rewrite / asset routes
env
php-array
returned env.php, not dotenv
php.ini
cli directives
memory / limits for the CLI
──►
worktree
comes up alone
isolated db + console commands
No name-checks in Go — the same env, worktree and doctor machinery reads the definition, so a new framework or major version is a YAML file, not a release.
FEAT

nginx config + php-array env #845

A definition carries an nginx: snippet spliced into the vhost, so a framework that needs its own routes stops depending on lerd knowing its name, and php-array joins dotenv as an env format so a returned PHP array like Magento's env.php flows through the same env machinery as everyone else.

internal/nginx · internal/envfile
FEAT

Required services, declared #847

A definition lists the services it cannot run without, and linking a project that needs them offers to install them rather than leaving the site to fail on first request.

FEAT

The php.ini its CLI needs #848

The CLI container picks up the definition's php.cli_ini, so a framework needing a different memory limit or directive set than FPM gets it without a global override.

FEAT

What a worktree needs to come up #892

A definition names the database isolation a worktree wants, what an isolated database is cloned from, and the console commands to run once the file and database are in place. Magento worktrees now come up on their own domain with nothing to run by hand, where before the store 500'd on a config hash the shared database disagreed with.

internal/siteops
02

Sites Group Into Workspaces

FEAT

A workspace is a named bucket of sites that survives across the dashboard, the overview grouping and the TUI's sort: workspace, so a machine carrying three clients' projects stops presenting them as one flat list. It is distinct from site groups, which stay a domain-level concept.

FEAT

Workspaces across the stack #827

Assign sites to a workspace and the dashboard, the overview and the TUI all honour it. Three clients, three buckets, one machine.

internal/grouping
FEAT

An AI assistant can organise them #852

MCP gains a twelfth grouped tool, workspace (list, create, rename, delete, assign, move), so an assistant can organise a site list rather than only read it, and preset_list now carries each preset's category, icon and admin_for. The handshake reports lerd's real version, and the reference no longer calls lerd a Laravel-only environment.

internal/mcp
03

Service Presets Know Their Job

FEATUI

Presets now carry category, icon and admin_for, so an admin dashboard is presented as the front end for the service it fronts rather than as another unrelated tile. phpMyAdmin administers mysql and mariadb, and that is a different relationship than depending on them.

FEATUI

Category, icon and what it administers #852 #822

Installed services pick up the preset card shell, and the site overview's service cards offer to open a suggested admin dashboard directly, backed by admin_for rather than a guess.

UI

Open the dashboard from the card #825

A running service's card links straight to its admin front end, so the phpMyAdmin, pgAdmin, RedisInsight and RabbitMQ dashboards are one click from the site they belong to.

04

Self-Heal & Node

FEAT

A worker whose unit is happily active but whose dev server has died no longer reads as healthy, and a Node project is driven through whichever package manager it actually pins instead of having npm forced on it.

FEAT

Running, but no longer serving #841

The probe dials the URL the worker publishes and flags it unreachable, and per-worktree worker units are covered by the same pass, so a dead per-worktree Vite is healed like any other, not left reading green.

internal/workerheal
FEAT

The package manager you pin #812

A project that pins pnpm or yarn, via packageManager or its lockfile, is driven through corepack with pnpm install --frozen-lockfile or yarn install --immutable, and the setup wizard's step labels follow the manager it detected.

05

Dashboard & TUI Surfaces

FEATUI

Request timing, resources and disk grow new surfaces across both the web UI and the terminal dashboard, and the sites list learns to order by real traffic.

FEAT

Reclaimable disk, cleaned in place #857

The resources widget carries a reclaimable-disk figure alongside CPU and memory, with a cleanup modal behind it, so the space lerd cleanup would reclaim is visible without running it first.

FEAT

Order by real request traffic #859

Recently-used and most-used sort modes are backed by the request store, so the site being worked on surfaces without being pinned by hand.

UI

The Env tab names its file #865

The toolbar shows the dotenv path the framework actually resolves to, which for a worktree is the worktree's own file, so it is no longer ambiguous which env a change lands in.

FEAT

A request-timing pane in the TUI #868

Request timing joins the site overview and logs get a tab of their own rather than sharing a pane.

UI

A responsive TUI overview #872

The overview lays out as a grid that reflows with the terminal, so a narrow tmux split and a full-screen window both read cleanly.

06

First-Class On Atomic Images

FIX

Fedora Silverblue, Bazzite, Kinoite and the other ostree spins are a growing home for developers, and this release makes lerd behave on them. The headline is a 90-second stall gone from every container start, and the second half is telling the truth about browser HTTPS trust instead of silently skipping it.

rootless quadlet start on an atomic imagethe inherited wait unit never fires
before
podman-user-wait-network-online
nothing pulls network-online.target
──►
timeout
90s per start
drags the whole boot with it
──►
after
no-op drop-in
lerd only binds loopback
Gated to hosts where the target never activates — ordinary distributions are untouched, and lerd doctor reports the condition.
FIX

Clean boot, no 90s stall #928

systemd offers no way to strip an inherited ordering dependency, so lerd writes a drop-in that turns podman's network-online wait into a no-op where the target never comes up. lerd start and lerd install both apply it.

internal/systemd
FIX

The tray fails gracefully #928

lerd-tray links a library these images do not carry, so install reads the helper's ELF dependencies and, when one will not resolve, leaves the tray unit on disk but stopped and disabled instead of letting a failed unit leave the systemd user session degraded.

FIX

Browser HTTPS trust, explained #931

certutil cannot be layered without a reboot, so mkcert trusts the CA in the system store but skips the browser NSS databases and only warns. lerd surfaces the missing certutil in install, dns:enable and lerd doctor, and the bootstrap installer consults ID_LIKE so a spin like Bazzite is recognised.

internal/certs
FIX

A project stops listing twice #932

On atomic images /home is a symlink to /var/home, so the working directory can come back under either spelling and a project linked through each registered as two sites. Paths are resolved before they are stored and compared now.

07

Fixes & Polish

FIXFEAT

Correctness fixes against previously shipped code, plus two small features that round out the release.

FIX

Symfony credentials stayed out of git #883

Setup wrote DATABASE_URL into the tracked .env instead of .env.local because two resolvers disagreed for Symfony. Both call sites resolve through the same store-first definition now.

FIX

Worktrees serve from the right root #878

A worktree vhost hardcoded public, so a Magento, Drupal or CakePHP worktree 404'd. The root follows the framework's public_dir now.

FIX

Resolving a framework stopped writing #906

The resolver repinned framework_version in .lerd.yaml on every vhost render and poll. It is read-only now, and the commands that own the file sync the version explicitly.

FIX

.lerd.yaml is written atomically #924

A bare write could truncate the file and silently drop a site's pinned versions, domains and workers. It goes through the atomic writer now and encodes to a stable, canonically-ordered form.

FIX

Console commands run in worktrees #921

lerd artisan and lerd console inherit the parent site's framework for worktree paths now, the same way the worker commands already do, and the MCP console tool is fixed with them.

FIX

check stops flagging valid workers #927

The Stripe listener and host-proxy dev server legitimately run outside a framework, so lerd check recognises the built-in workers through a shared helper instead of failing a healthy site.

FIX

Profile localhost over http #935

The timing overview built the profile URL with a hardcoded https scheme, so profiling a .localhost route failed on a certificate error. The URL follows the site's TLS state now.

FIX

The CLI accent follows your theme #919

The interactive accent shared the brand red with the failure colour, so a spinner read as an error. Signal colours use ANSI terminal-theme codes now, green for running, red for failures, yellow for warnings.

FEAT

Copy a query, bindings inlined #925

The Queries lens gets a copy button that resolves the ? placeholders into escaped SQL literals, strings quoted, NULL for nulls, JSON for structured values, so the copied query runs in a SQL editor as-is.

FEAT

A high-contrast tray icon #917

On mixed themes the adaptive icon can vanish into the panel. An opt-in high-contrast style draws the running icon as a single green L that reads on any background, set with lerd tray icon high-contrast.

FIX

A scheduled worker reads as healthy #850

A scheduled worker is a oneshot driven by a timer, inactive between ticks, so its liveness is the timer's state now while a failed last run still surfaces.

FIX

The CLI PHP matches FPM #889

composer, console and php resolve the registered site's PHP version now instead of re-detecting it, and the published images only advertise extensions they actually load.