You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A root dotfiles step poisons the shared pixi cache: #240's second clause, by a route it did not consider #661
#240 closed with the mount moved out of $HOME, which fixed the root-owned ~/.cache. Its "Done when" carried a second clause that was not done:
Decide and record what happens when the container uid cannot write the cache — at minimum it should not be a hard provisioning failure.
On 19 Sep 2026 that clause came due. Three workspaces came up with no nvim, no git-delta, no jq, no forgit, no claude-statusline and no xclip. Every launch exited 0 and printed Dotfiles setup completed successfully!. The symptom the user saw was nvim: command not found.
It arrived by a route #240 did not consider. #240 reasoned about an image whose user is not uid 1000, and concluded the common case is safe because every mainstream base declares uid 1000 and this host is uid 1000. That holds. But the container uid is not the only uid that writes this cache — the dotfiles step picks its own user, and it falls back to root.
What is in the cache now
One file, written by uid 0 into the directory every other container reads:
#240 guessed the residual hazard as package subdirectories at 0755, and expected 0777 on the host directory to rescue the cross-uid case. Neither holds for repodata. Every shard in repodata/shards-v1 is 0600 — all 1156 of them, including the shards the uid-1000 containers wrote themselves. That is the mode pixi writes, not a umask artifact, so no mode on the parent and no umask in the container changes it. A shard written by another uid is unreadable, full stop.
Every pixi global sync afterwards died on that one file:
WARN Couldn't sync environment neovim
× failed to read from file `/var/tmp/devlaunch-pixi/repodata/shards-
│ v1/125d7bea…msgpack`: Permission denied (os error 13)
Error: × Couldn't sync the following environments: git-delta, forgit, jq,
│ claude-statusline, neovim, xclip
Repodata rather than a package, so it took out every environment whose solve needed that shard instead of one of them. The env directories were all created, which is why nothing looked absent — they were never populated and never exposed.
Where the root came from
devpod's dotfiles step picks its user by reading the ssh alias back out of the config, and falls through to root when it finds none — pkg/dotfiles/dotfiles.go:139, devpod 0.26.1:
Both WorkspaceConfig() fields are always empty. up writes neither, and a workspace.json here carries only id, uid, provider, machine, ide, source, creationTimestamp, lastUsed and context. So GetUser always reads ~/.ssh/config, and the rule reduces to one sentence: the dotfiles step runs as root whenever ~/.ssh/config holds no alias for the workspace at that moment.
In this instance the alias was missing because I had moved the DevPod blocks to ~/.ssh/config.d/devpod while testing devpod's SSH_CONFIG_INCLUDE_PATH option. That is reverted, and it is not the trigger that matters. The trigger that matters is that a missing alias is an open, unreproduced bug in its own right — blooop/dotfiles#40, where a machine lost its host alias and spent 15 hours attaching as root. Every path to a missing alias is now also a path to a poisoned cache for every workspace launched afterwards.
Why this is expensive to find
It outlives the launch that caused it. The poisoned file survives the container that wrote it, so the workspace that breaks the cache is not the workspace that reports it. Mine was written 30 minutes before the first affected launch.
09:46:03 — the root dotfiles step. /root/.pixi in that container, and the root:root shard in the shared cache.
10:15:52, 10:15:56, 10:36:44 — three unrelated workspaces run their dotfiles step. All three fail the same way. All three report success.
Repro
Take a workspace whose alias is not in ~/.ssh/config. Delete the block, or set the SSH_CONFIG_PATH context option and let up write elsewhere — that reaches the same place through the never-written WorkspaceConfig().SSHConfigPath.
devpod up <ws>. Confirm the dotfiles step went root: /root/.pixi exists in the container, and the log says Added /root/.pixi/bin to PATH.
find ~/.cache/devlaunch/pixi ! -user $USER — expect a fresh 0600 shard under repodata/shards-v1.
Create any other workspace. Its pixi global sync fails on that shard, and its shell arrives with no nvim.
Candidate fixes
Key the cache by uid — PIXI_CACHE_TARGET plus id -u, or a per-uid host source. This closes the class rather than this instance, and it does not wait on devpod fixing its root fallback. A root dotfiles step then costs its own container a download and nothing else.
Withhold the cache from a non-matching uid. Cheaper, same containment, and it makes a root step slow instead of contagious.
I would take the uid keying and the reporting together: the first stops it spreading, the second stops it hiding.
Meanwhile
Check: find ~/.cache/devlaunch/pixi ! -readable -type f. One hit breaks every workspace.
Repair: delete the offending file, then re-run pixi global sync in each workspace. repodata/shards-v1 and pkgs belong to the host user, so removing a root-owned file inside them needs no sudo. Removing a root-owned directory does.
Measured on dl 0.53.0 / devpod 0.26.1. PIXI_CACHE_TARGET is unchanged on dl 0.57.1.
#240 closed with the mount moved out of
$HOME, which fixed the root-owned~/.cache. Its "Done when" carried a second clause that was not done:On 19 Sep 2026 that clause came due. Three workspaces came up with no
nvim, nogit-delta, nojq, noforgit, noclaude-statuslineand noxclip. Every launch exited 0 and printedDotfiles setup completed successfully!. The symptom the user saw wasnvim: command not found.It arrived by a route #240 did not consider. #240 reasoned about an image whose user is not uid 1000, and concluded the common case is safe because every mainstream base declares uid 1000 and this host is uid 1000. That holds. But the container uid is not the only uid that writes this cache — the dotfiles step picks its own user, and it falls back to root.
What is in the cache now
One file, written by uid 0 into the directory every other container reads:
#240 guessed the residual hazard as package subdirectories at
0755, and expected0777on the host directory to rescue the cross-uid case. Neither holds for repodata. Every shard inrepodata/shards-v1is0600— all 1156 of them, including the shards the uid-1000 containers wrote themselves. That is the mode pixi writes, not a umask artifact, so no mode on the parent and no umask in the container changes it. A shard written by another uid is unreadable, full stop.Every
pixi global syncafterwards died on that one file:Repodata rather than a package, so it took out every environment whose solve needed that shard instead of one of them. The env directories were all created, which is why nothing looked absent — they were never populated and never exposed.
Where the root came from
devpod's dotfiles step picks its user by reading the ssh alias back out of the config, and falls through to root when it finds none —
pkg/dotfiles/dotfiles.go:139, devpod 0.26.1:Both
WorkspaceConfig()fields are always empty.upwrites neither, and a workspace.json here carries onlyid,uid,provider,machine,ide,source,creationTimestamp,lastUsedandcontext. SoGetUseralways reads~/.ssh/config, and the rule reduces to one sentence: the dotfiles step runs as root whenever~/.ssh/configholds no alias for the workspace at that moment.In this instance the alias was missing because I had moved the DevPod blocks to
~/.ssh/config.d/devpodwhile testing devpod'sSSH_CONFIG_INCLUDE_PATHoption. That is reverted, and it is not the trigger that matters. The trigger that matters is that a missing alias is an open, unreproduced bug in its own right — blooop/dotfiles#40, where a machine lost its host alias and spent 15 hours attaching as root. Every path to a missing alias is now also a path to a poisoned cache for every workspace launched afterwards.Why this is expensive to find
devpod upexits 0 and reports the dotfiles setup as successful while the sync underneath reports six unsynced environments. This is Shared pixi cache mount makes ~/.cache root-owned in any image that lacks it, and hard-fails on a uid mismatch #240's unfinished clause exactly: a hard failure of provisioning, presented as a success.Timeline
All UTC, one host:
09:46:03— the root dotfiles step./root/.pixiin that container, and theroot:rootshard in the shared cache.10:15:52,10:15:56,10:36:44— three unrelated workspaces run their dotfiles step. All three fail the same way. All three report success.Repro
~/.ssh/config. Delete the block, or set theSSH_CONFIG_PATHcontext option and letupwrite elsewhere — that reaches the same place through the never-writtenWorkspaceConfig().SSHConfigPath.devpod up <ws>. Confirm the dotfiles step went root:/root/.pixiexists in the container, and the log saysAdded /root/.pixi/bin to PATH.find ~/.cache/devlaunch/pixi ! -user $USER— expect a fresh0600shard underrepodata/shards-v1.pixi global syncfails on that shard, and its shell arrives with nonvim.Candidate fixes
PIXI_CACHE_TARGETplusid -u, or a per-uid host source. This closes the class rather than this instance, and it does not wait on devpod fixing its root fallback. A root dotfiles step then costs its own container a download and nothing else.I would take the uid keying and the reporting together: the first stops it spreading, the second stops it hiding.
Meanwhile
Check:
find ~/.cache/devlaunch/pixi ! -readable -type f. One hit breaks every workspace.Repair: delete the offending file, then re-run
pixi global syncin each workspace.repodata/shards-v1andpkgsbelong to the host user, so removing a root-owned file inside them needs no sudo. Removing a root-owned directory does.Measured on dl 0.53.0 / devpod 0.26.1.
PIXI_CACHE_TARGETis unchanged on dl 0.57.1.