chore(devcontainer): make rootless podman work here - #716
Merged
Conversation
…ne is reproducible locally podman was installed ad hoc in a running container and would vanish on the next rebuild. Nothing in the config asked for it, and nothing made it work: three things were false by default, each measured rather than assumed. SUBORDINATE IDs THAT FIT THE AMBIENT USERNS. This devcontainer already runs inside a user namespace — /proc/self/uid_map reads `0 100000 65536`, so only ids 0..65535 exist for us — while the image ships `vscode:100000:65536`, naming ids this namespace cannot represent. newuidmap fails with `write to uid_map failed: Operation not permitted`. Podman also refuses any single range containing the user's own uid, so the allocation has to straddle it as two ranges. Size matters past merely starting a container: a range stopping short of 65534 cannot map `nobody`, and Feature installs then die inside apt with `setgroups 65534 failed`. The script computes 1:999 + 1001:64535 from uid_map rather than hardcoding it, so it also does the right thing on a host with no ambient remap. A STORAGE DRIVER THE KERNEL WILL MOUNT. Rootless overlay goes through fuse-overlayfs, which needs /dev/fuse. The module is loaded but the node is not exposed to a container unless requested, and mknod is refused from inside. The script picks overlay when the node exists and vfs otherwise. vfs needs no added container privileges, which is why /dev/fuse is documented as an opt-in rather than set in runArgs: it grants a host device for a speed-up, and correctness does not depend on it. AN API SOCKET, for compose only. `podman compose` execs an external provider and points it at podman by exporting DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/… (measured by pointing compose_providers at a script that dumps its environment; it also sets DOCKER_BUILDKIT=0 and blanks DOCKER_CONFIG). With nothing listening, every compose call fails with `Cannot connect to the Docker daemon`. No systemd here to socket-activate it, so postStartCommand runs it directly. Verified end to end: rootless `podman run` works, `podman compose build` puts its image in podman's own store where `podman tag` finds it, and `DEACON_CONTAINER_RUNTIME=podman` now runs the suite for real. Both scripts are idempotent and were re-run to prove it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016SFzA2sTh2EpX8MZU3TWNS
…fails Both hooks ran unguarded, so a host with no apt mirror, no passwordless sudo, or a kernel that will not give podman a usable id mapping would fail the whole devcontainer build — disproportionate for an optional convenience on a repo whose default runtime is docker. The scripts keep `set -euo pipefail` so the failure stays loud and debuggable; only the container's fate is decoupled from it. `podman --version` also comes back out of postCreateCommand for the same reason. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016SFzA2sTh2EpX8MZU3TWNS
Contributor
Author
|
Pushed As first written they were unguarded, so a host with no apt mirror, no passwordless sudo, or a kernel that won't give podman a usable id mapping would have failed the entire devcontainer build. That's disproportionate for an optional convenience on a repo whose default runtime is docker — and it would have hit people who never asked for podman. The scripts keep |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
podman was installed ad hoc in a running container and would vanish on the next rebuild. Nothing in the config asked for it, and nothing made it work. This makes it declarative, so the Podman CI lane can be reproduced locally instead of only in CI.
Three things were false by default. Each was measured, not assumed.
1. Subordinate IDs that fit the ambient user namespace
This devcontainer already runs inside a userns —
/proc/self/uid_mapreads0 100000 65536, so only ids 0..65535 exist for us. The image shipsvscode:100000:65536, naming ids this namespace cannot represent:Podman additionally refuses any single range containing the user's own uid, so the allocation has to straddle it as two ranges. Size matters past merely starting a container — a range stopping short of 65534 cannot map
nobody, and Feature installs then die inside apt:The script derives
1:999+1001:64535fromuid_maprather than hardcoding, so it also does the right thing on a host with no ambient remap.2. A storage driver the kernel will actually mount
Rootless
overlaygoes through fuse-overlayfs, which needs/dev/fuse. The module is loaded (fuseis in/proc/filesystems) but the node isn't exposed to a container unless requested, andmknodis refused from inside. The script picks overlay when the node exists,vfsotherwise./dev/fuseis documented as an opt-in, not set inrunArgs. It grants the container a host device purely for a speed-up, and correctness doesn't depend on it — everything below was verified onvfs, with no added container privileges.3. An API socket — for compose only
podman composeexecs an external provider and points it at podman by exportingDOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock(measured by pointingcompose_providersat a script that dumps its environment; it also setsDOCKER_BUILDKIT=0and blanksDOCKER_CONFIG). With nothing listening, every compose call fails withCannot connect to the Docker daemon. There's no systemd here to socket-activate it, sopostStartCommandruns it directly.Verified
podman runworkspodman compose buildputs its image in podman's own store, wherepodman tagfinds itDEACON_CONTAINER_RUNTIME=podman cargo nextest runruns the suite for realThis is what let me measure #715 against a genuine podman substrate rather than reasoning about it — including finding that the Podman CI lane needs the socket started, which it currently doesn't do.
🤖 Generated with Claude Code
https://claude.ai/code/session_016SFzA2sTh2EpX8MZU3TWNS