Skip to content

Add first-class podman support to vip dev-env - #3039

Closed
abdullah-kasim wants to merge 10 commits into
trunkfrom
shadow-vipodman-08
Closed

Add first-class podman support to vip dev-env#3039
abdullah-kasim wants to merge 10 commits into
trunkfrom
shadow-vipodman-08

Conversation

@abdullah-kasim

@abdullah-kasim abdullah-kasim commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Makes podman (machine on macOS, rootless on Linux) a first-class, detected
container engine for vip dev-env, in both the Node.js and Go runtimes, so
the external ~/dotfiles/podman-docker shim stack is no longer needed.
Docker users see no behavior change: every new function is a no-op /
identity function for engine === 'docker'.

Companion fix (separate repo, own draft PR): the two new lando config keys
this branch sets (proxyPublishAddress, proxySocket) require Lando's
lando-proxy plugin to honor them, which the previously pinned
Automattic/lando-cli fork commit did not yet do. That fix now lives at
Automattic/lando-cli#245 (branch
add/podman-proxy-config), and this branch's package.json#lando is pinned
to that commit.

Defects addressed

Running vip dev-env create/start under podman without the dotfiles shim
surfaces a chain of masking defects — fixing the first only reveals the
next. This PR addresses defects A through J:

# Defect Where it lived Fix
A Engine identity (docker info vs podman info JSON shape) was never distinguished; every downstream decision assumed Docker src/lib/dev-environment/engine.ts, internal/devenv/dockercli/engine.go detectEngine/DetectEngine: parse both JSON shapes into a shared EngineInfo{engine, serverVersion, socketPath, rootless, composePlugin}; falls back to docker/unknown on any unrecognized shape or exec failure, so unrecognized info output never regresses a working Docker setup
B Docker socket discovery never looked for a podman socket src/lib/dev-environment/docker-utils.ts, internal/devenv/dockercli getDockerSocket/its Go mirror add the podman machine socket path (macOS), the XDG rootless socket path (Linux), and a podman machine inspect fallback, appended after the existing Docker candidate paths — Docker's own resolution order is unchanged
C No compose-version gate; podman ships podman-compose, not Compose v2 engine.ts/Go mirror composeRequirement: no-op ({ok:true}) for Docker; for podman, requires a real standalone docker-compose binary satisfying >=2.0.0, otherwise refuses with a steered remedy message and a docs link. podman-compose translation is explicitly not supported
D Docker Desktop binds the proxy to 127.0.0.1; podman machine's user-mode networking needs 0.0.0.0 to be reachable from the host engine.ts/Go proxy/spec.go proxyPublishAddress(info) returns 0.0.0.0 under podman, 127.0.0.1 under Docker; the proxy's own port-availability scan still probes 127.0.0.1 regardless of engine
E The proxy container's docker.sock bind-mount assumed Docker's path and never disabled SELinux labeling for podman's rootless socket engine.ts/Go proxy/spec.go proxySocketMount(info) mounts the real discovered socket path at /var/run/docker.sock inside the proxy container, and sets security_opt: [label=disable] only when running podman on an SELinux-enforcing Linux host
F No preflight signal for the unprivileged-port-80 sysctl podman needs; vip-cli must never mutate the host itself engine.ts/Go proxy/preflight.go podmanPreflight is a pure function over EngineInfo + injected probe results; under podman, if net.ipv4.ip_unprivileged_port_start doesn't allow port 80, it returns a warning naming the exact remedy command (podman machine ssh -- sudo sysctl -w ... on macOS, sudo sysctl -w ... on Linux). It never runs a probe or mutates state itself
G assets/dev-env.lando.template.yml.ejs: an initOnly service's dependents used service_started, which podman does not treat the same way Docker Desktop does, causing dependent services to race the init container template Every initOnly dependency condition now uses service_completed_successfully. Real for Docker too — this is a template correctness bug, not podman-specific
H The wordpress init container's rsync --delete /wp/ /shared/ deleted sibling bind-mount targets (config, log, vip-config, wp-content/uploads, app-code dirs) that podman does not recreate at container start, unlike Docker Desktop template rsync now excludes every sibling bind-mount target explicitly
I Lando addresses containers by Compose v2 project/service names; a v1-style name pattern makes every isRunning check false, so build steps start-then-kill each service and the VIP health check's retry logic rescans proxy ports and lands on the wrong port CI / detection requirement Compose v2 is now a hard requirement under podman (defect C), which keeps container naming Compose-v2-shaped; documented as a docker-compose version requirement rather than a separate code path
J Lando's own config resolves dockerBin via a stock env.getDockerExecutable() that only recognizes a binary literally named docker, so on a podman-only machine dockerBin is empty and every downstream check fails before any of A–I ever run src/lib/dev-environment/docker-utils.ts getDockerBin(): tries docker first (no-op / unchanged for Docker users), falls back to podman when only that's usable; wired into getLandoConfig() before Lando's own config merge

Testing

  • npm test (lint, check-types, jest): 92 suites, 980 passed / 1 todo, 0 lint errors.
  • go test ./...: all packages pass (with VIP_PROXY unset — an ambient SOCKS proxy in this environment otherwise breaks unrelated loopback-server tests in internal/gql, internal/sqlexport, internal/telemetry, internal/wpstream; unrelated to this change).
  • make require-node-vip-bin + make test-parity-unit: parity suite green, including new testdata/parity/devenv-podman-*.json fixtures; existing scenarios unchanged (22 pre-existing expected_drift entries, none newly added).
  • internal/parity Docker scenarios unmodified.

Real-hardware acceptance run (this Mac, podman machine, dotfiles shims unstowed)

With ~/dotfiles/podman-docker genuinely unstowed (stow -D podman-docker
confirmed no docker/docker-compose binary on PATH afterward) and a real
standalone docker-compose v2 binary installed via Homebrew:

First run (before the getDockerBin fix below), vip dev-env create --slug=podman-support … failed immediately:

Error:  docker binary could not be located! Please follow the following instructions to install it - https://docs.docker.com/engine/install/

Root cause: getLandoConfig() never set dockerBin, so Lando's own config
merge filled it via the stock (unforked) env.getDockerExecutable(), which
only searches for a binary literally named docker. Fixed by
getDockerBin() in src/lib/dev-environment/docker-utils.ts: tries a
docker-named binary first (Docker users see no behavior change — returns
null, leaving Lando's own default in place), falls back to podman when
only that's usable, wired into getLandoConfig() before Lando's own config
merge runs.

Second run, with that fix in place, docker/podman correctly resolves
and getDockerSocket() correctly discovers the podman socket
(/var/run/docker.sock, a symlink to the podman machine socket, installed
by podman-mac-helper) — but vip dev-env create still fails, with a
different error:

Error:  Failed to connect to Docker. Please verify that Docker engine (service) is running and follow the troubleshooting instructions for your platform.

Root cause (traced in source): this is Lando's own stock, unforked
daemon.js#getVersions(), invoked from lando.js during bootstrap and
assigned directly to lando.config.versions — it shells podman info --format json successfully, but reads only dockerData.ServerVersion
(Docker's JSON shape) and has no podman-shape awareness, so it silently
returns engine: ''. validateDockerInstalled() in this PR gates on that
exact lando.config.versions.engine value before it ever reaches this
PR's own podman-aware detectEngine() — so detectEngine,
composeRequirement, proxyPublishAddress, proxySocketMount, and
podmanPreflight (defects A, C, D, E, F — all merged and unit-tested) are
provably correct in isolation but are currently unreachable from the real
CLI entry point under podman, because a stock-Lando health check upstream
of them fails first.

This is reported rather than patched in this run, since it was found by the
real-hardware verification pass rather than planned work — it needs a
follow-up fix: either make validateDockerInstalled call this PR's own
detectEngine() before/instead of trusting lando.config.versions.engine
for the emptiness check, or teach a podman-aware version query into the
Lando bootstrap path itself.

No podman-support-slugged environment or containers were created by
either run (confirmed via podman ps -a and vip dev-env list — both
failures happen before any container/network state is touched). The
machine was restored afterward: stow podman-docker was re-run to restore
the dotfiles shims to their pre-run state.

Related

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Dependency Review

The following issues were found:
  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 1 package(s) with unknown licenses.
See the Details below.

License Issues

package.json

PackageVersionLicenseIssue Type
landogithub:automattic/lando-cli#fcd3119869c7bfa8175e287e37bde66690add9e0NullUnknown License

OpenSSF Scorecard

PackageVersionScoreDetails
npm/lando github:automattic/lando-cli#fcd3119869c7bfa8175e287e37bde66690add9e0 UnknownUnknown

Scanned Files

  • package.json

@sonarqubecloud

sonarqubecloud Bot commented Sep 7, 2026

Copy link
Copy Markdown

@abdullah-kasim
abdullah-kasim deleted the shadow-vipodman-08 branch September 7, 2026 14:20
@abdullah-kasim

Copy link
Copy Markdown
Contributor Author

Failed experiment on my end, I admit. Looks like adding podman support isn't so simple.

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