Skip to content

Insulate container operations from Docker Desktop under WSL - #66

Open
keithharvey wants to merge 3 commits into
beyond-all-reason:masterfrom
keithharvey:fix-docker-desktop-wsl
Open

Insulate container operations from Docker Desktop under WSL#66
keithharvey wants to merge 3 commits into
beyond-all-reason:masterfrom
keithharvey:fix-docker-desktop-wsl

Conversation

@keithharvey

@keithharvey keithharvey commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Two separate setup::init failures reported on Ubuntu 24 under WSL2 with Docker Desktop installed on the Windows side. Same root cause: Docker Desktop leaks client-side state into the distro's home and PATH, and we only ever hardened the daemon side.

1. compose build teiserver fails before pulling anything

error listing credentials - err: exec: "docker-credential-desktop.exe":
executable file not found in $PATH, out: ``
Error: executing /usr/local/bin/docker-compose ... build teiserver: exit status 1

The daemon is podman, but the client isn't. podman compose delegates to the Go docker-compose binary we install at /usr/local/bin/docker-compose (setup.sh:250-261), and Docker clients read ~/.docker/config.json for registry auth no matter which daemon they're pointed at. Docker Desktop writes "credsStore": "desktop.exe" there. That helper is a Windows binary, reachable only through WSL PATH interop with Docker Desktop integration enabled for the distro.

Fixed by pointing DOCKER_CONFIG at .devtools/docker so we carry our own client config. Every image the stack uses is public, so no credential helper is needed.

2. engine::build silently leaves podman

docker-build-v2/build.sh resolves its own runtime and prefers docker whenever one is on PATH:

elif command -v docker &> /dev/null &&
     ! docker version | grep -qi podman; then
    RUNTIME=docker

Nothing in this repo set CONTAINER_RUNTIME, so this is the one operation that jumps out of podman into the Docker Desktop VM. The source tree, ccache, and build output are then all bind-mounted across a distro boundary, which turns an engine build into an all-day affair.

Fixed by pinning CONTAINER_RUNTIME=podman to match every other container operation here.

3. just lua::check was still on docker compose

just/lua.just:8 defined COMPOSE := "docker compose -f ..." while every other module uses podman compose. On a podman-only host that recipe fails with command-not-found; under WSL with Docker Desktop it silently routes into the Docker Desktop VM. Switched to podman compose.

4. Doctor check

Both pins only cover our own recipes. Anyone invoking docker-compose or docker-build-v2/build.sh directly still hits these, so doctor now names the condition. Detection reads the docker symlink target rather than running docker version, so a wedged Docker Desktop can't hang a read-only diagnostic.

Verification

The credsStore failure is purely client-side, so it reproduces on Linux with a synthetic HOME:

$ HOME=$fake docker-compose ... pull --dry-run postgres
 Image postgres:16-alpine Error error pulling image: postgres:16-alpine
error getting credentials - err: exec: "docker-credential-desktop.exe": executable file not found in $PATH, out: ``

$ HOME=$fake DOCKER_CONFIG=.devtools/docker docker-compose ... pull --dry-run postgres
 Image postgres:16-alpine Pulled

just doctor passes clean, and the new check is correctly silent off-WSL.

The engine-build half is reasoned from the resolver source rather than reproduced — I don't have a Docker Desktop box. Worth a second pair of eyes from someone who does.

Not fixed here

That resolver has a real bug: docker version's stdout is piped into grep, so when the daemon is unreachable the pipeline still exits non-zero, ! flips it true, and docker gets selected anyway — podman is never tried. Our pin sidesteps it, but it'll keep biting anyone building Recoil directly. Belongs upstream in RecoilEngine.

LLM Disclosure

Opus 5

Docker Desktop leaks client-side state into a WSL distro's home and PATH,
and BAR-Devtools only ever hardened the daemon side. Two failures follow.

`compose build teiserver` dies before it pulls anything: Docker Desktop
writes `"credsStore": "desktop.exe"` into ~/.docker/config.json, and the Go
docker-compose provider podman delegates to reads that file regardless of
which daemon it's talking to. The helper is a Windows binary that isn't on
PATH. Point DOCKER_CONFIG at our own runtime dir instead.

`engine::build` silently leaves podman: docker-build-v2/build.sh resolves
its own runtime and prefers docker whenever one is on PATH, so the build
lands in the docker-desktop distro's VM with the source tree, ccache, and
build output all bind-mounted across a distro boundary. Pin
CONTAINER_RUNTIME=podman to match every other container operation here.

Doctor grows a check for both signals, since anyone invoking docker-compose
or build.sh directly still hits them.
`just lua::check` was the one recipe still shelling out to `docker compose`.
On a podman-only host it fails with command-not-found; under WSL with Docker
Desktop it silently routes into the Docker Desktop VM.
Match the DEVTOOLS_DISTROBOX pattern -- default in code, .env or the
environment overrides. The hard assignment blocked anyone whose compose
pulls from a private registry from keeping their own docker login.

The default stays in code rather than moving to .env: `set dotenv-load`
reads .env when just starts, so a key written at step 0 isn't in the
environment for step 4 of that same run. It would be unset for the first
setup::init and correct on every run after.
@keithharvey
keithharvey force-pushed the fix-docker-desktop-wsl branch from 094a352 to a1d0559 Compare August 14, 2026 20:23
Comment thread scripts/common.sh
: "${DEVTOOLS_SYNC_DISTROBOX:=bar-sync}"
export DEVTOOLS_SYNC_DISTROBOX

# Our own Docker client config; Docker Desktop's ~/.docker/config.json names a

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debated putting this in .env, but that file holds user selections and this is derived rather than chosen, so it doesn't really fit.

There's also a timing problem with .env: set dotenv-load reads it when just starts, so a key written at step 0 isn't in the environment for step 4 of that same run. It'd be unset for the first setup::init and correct on every run after — a rough way to fail, since it'd only ever bite a new contributor.

That said, the : below does allow users to override it in their own .env file and it will be honored.

This does add a minor risk if someone has DOCKER_CONFIG on their shell, but that's kind of expected/I'm fine with it — Docker Desktop itself doesn't set that var, it writes ~/.docker/config.json.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems fine given the use case, I have never heard of someone setting $DOCKER_CONFIG on their environment. Possibly it would be good to emit an info message of $DOCKER_CONFIG envvar set so someone with eagle-eyes can spot it in the log.

@NortySpock

Copy link
Copy Markdown
Contributor

The LLM-ese in the PR description around how the proposed fix solves the problem is a bit abstract and I'm struggling to grok it. But I'm getting there slowly...

@NortySpock NortySpock left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a question about fixing build.sh directly.

I guess I have a larger set of questions around 'Are we supporting podman-first, or docker-first? Which of the two is expected first, and which is the fallback? Do we need to make "podman vs docker" some sort of environment variable configured by the user and flex between the two?'

Comment thread just/engine.just
;;
esac
bash "$build_script" {{args}}
# build.sh picks docker over podman when both are on PATH -- under Docker

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From this comment.... should you just fix build.sh to pick podman first and fallback to docker if podman is unavailable?

(I am aware from other discussion in this PR that podman still uses docker runtime in some cases)

Comment thread scripts/common.sh
: "${DEVTOOLS_SYNC_DISTROBOX:=bar-sync}"
export DEVTOOLS_SYNC_DISTROBOX

# Our own Docker client config; Docker Desktop's ~/.docker/config.json names a

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems fine given the use case, I have never heard of someone setting $DOCKER_CONFIG on their environment. Possibly it would be good to emit an info message of $DOCKER_CONFIG envvar set so someone with eagle-eyes can spot it in the log.

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.

2 participants