Persisting Claude CLI Login Between Container Builds A developer solved the problem of Claude Code CLI login state not persisting across devcontainer rebuilds by mounting a named Docker volume onto a directory and symlinking the dotfile into it, avoiding the broken file-to-file mount on Docker Desktop. The workaround ensures `~/.claude.json` survives image rebuilds without shadowing the entire home directory. Keep Claude Code's account/session login ~/.claude.json alive across devcontainer rebuilds, instead of having to re-authenticate every time the image is rebuilt. Claude Code keeps two things on disk: ~/.claude/ — a directory, already persisted via a named Docker volume claude-playwright-setup . ~/.claude.json — a single Normally you'd just mount a named volume onto the whole folder the state lives in, the same way .claude/ , .copilot/ , and .continue/ are already handled. That's not an option here: .claude.json isn't inside its own subfolder, it sits directly in $HOME alongside everything else .bashrc , .ssh/ , .profile , ... . Mounting a volume onto $HOME itself to catch one file would shadow all of that, so the file has to be persisted on its own. Mounting a named volume straight onto the file path claude-json-...:/home/container-user/.claude.json seems like the next-simplest option, but it breaks on this Docker Desktop setup: mount ... not a directory: Are you trying to mount a directory onto a file A named volume's backing store is always a directory. Docker is supposed to detect that the mount target is a single file and copy the image's file into the volume so it ends up binding file-to-file. On this Docker Desktop that detection fails — the volume comes up as an empty directory, and runc then tries to bind that directory onto the file path and crashes at container start. This was confirmed by deleting the volume and rebuilding the image from scratch, so it isn't a stale-cache artifact. Never mount a volume directly onto a single file. Instead, mount it onto a directory — the same shape already used for .claude / .copilot / .continue — and symlink the dotfile into that directory from the Dockerfile. Dockerfile.debian : USER container-user .... RUN mkdir -p /home/container-user/.claude-json && \ touch /home/container-user/.claude-json/claude.json && \ ln -s /home/container-user/.claude-json/claude.json /home/container-user/.claude.json docker-compose.yml : services: devcontainer: volumes: - claude-json-playwright-setup:/home/container-user/.claude-json volumes: claude-json-playwright-setup: name: setup-playwright-playground-debian claude-json-playwright-setup post-container-install.sh — fix ownership on the new mount, same as the sudo chown -R container-user:container-user /home/container-user/.claude \ /home/container-user/.claude-json \ /home/container-user/.copilot \ ... .claude/settings.json and .claude/settings.local.json also gained a few permission entries docker volume , docker run , docker compose , docker history , and a find ... -iname .copilot needed to diagnose and rebuild the container while working through this. If an app ever persists the file via write-temp-then-rename an atomic write instead of writing in place, the rename replaces the symlink with a plain file inside the container's ephemeral layer. No crash — but persistence silently stops working until the next image build recreates the symlink. Worth checking if login state ever mysteriously stops surviving a rebuild again.