Skip to content

fix: dir-mount a dedicated Claude config dir for the CLI backend#90

Merged
max-tet merged 1 commit into
mainfrom
fix/claude-creds-dir-mount
Jun 3, 2026
Merged

fix: dir-mount a dedicated Claude config dir for the CLI backend#90
max-tet merged 1 commit into
mainfrom
fix/claude-creds-dir-mount

Conversation

@ClaydeCode
Copy link
Copy Markdown
Owner

Problem

With CLAYDE_CLAUDE_BACKEND=cli, the container fails intermittently with "claude authentication expired". Restarting the Docker Compose stack temporarily fixes it.

Root cause: docker-compose.yml bind-mounted the host's ~/.claude/.credentials.json as a single file. The Claude CLI refreshes its short-lived OAuth token by writing a temp file and atomically rename()-ing it into place — which changes the file's inode. A single-file bind mount is pinned to the inode resolved at container start, so the container keeps reading the stale, expired token forever. A restart re-resolves the mount → works again, until the next refresh.

The README also documented this as working — "Token refreshes... are immediately reflected" — which is the opposite of the actual behavior.

Fix

  • Mount a dedicated config directory (~/clayde-claude/home/clayde/.claude) instead of the single credentials file. A directory mount resolves the path live, so refresh atomic-renames are picked up — no restart needed.
  • Use a dedicated login (CLAUDE_CONFIG_DIR=~/clayde-claude claude login) rather than the host's personal ~/.claude. This isolates the container from the host's sessions/projects/history, and gives it its own OAuth refresh-token lineage so its refreshes can't invalidate the host login (refresh tokens are single-use).
  • Correct the README + CLAUDE.md docs accordingly.

Changed

  • docker-compose.yml — file mount → dedicated dir mount (with rationale comment)
  • README.md — CLI backend setup steps + corrected mount explanation
  • CLAUDE.md — CLI backend requirements line

Verified

Applied the same change to a live deployment, recreated the stack, confirmed the container reads the token via the directory mount, and the Pebble webhook works. Host login remained intact (separate lineage).

🤖 Generated with Claude Code

The CLI backend mounted the host's `~/.claude/.credentials.json` as a
single file. The Claude CLI refreshes its short-lived OAuth token via
atomic rename (new inode), but a single-file bind mount is pinned to the
inode resolved at container start — so the container keeps reading the
stale, expired token and fails with "authentication expired" until the
stack is restarted.

Switch to mounting a dedicated config *directory* (`~/clayde-claude`),
which resolves the path live and picks up refreshes with no restart. The
README previously claimed host refreshes were "immediately reflected" —
they were not; this corrects the docs and the compose mount.

Using a dedicated login (separate `CLAUDE_CONFIG_DIR`) also isolates the
container from the host's personal `~/.claude` state and gives it its own
OAuth refresh-token lineage, so container refreshes can't invalidate the
host login (refresh tokens are single-use).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@max-tet max-tet merged commit 71ed2f7 into main Jun 3, 2026
3 checks passed
@max-tet max-tet deleted the fix/claude-creds-dir-mount branch June 3, 2026 18:57
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