Skip to content

Docker: auth-dir default doesn't match the volume mount, causing silent token loss on restart #23

Description

@eumemic

Summary

docker-compose.yml mounts a persistent named volume at /data (auth-data:/data), but the default config.example.yaml keeps auth-dir: "~/.auth2api". Inside the container, ~ expands against process.env.HOME || "/root" (per src/config.ts:resolveAuthDir), and HOME is not set by the upstream Dockerfile (no ENV HOME=..., no USER, default WORKDIR is /app). Net effect: auth-dir resolves to /root/.auth2api, not /data. OAuth tokens and stats.jsonl land outside the mounted volume and are lost on container restart.

Repro

git clone https://github.com/AmazingAng/auth2api && cd auth2api
cp config.example.yaml config.yaml
# Leave auth-dir at the default "~/.auth2api". Set api-keys to anything.
docker compose up -d
docker compose exec auth2api npm run login -- --provider=codex
# (complete OAuth flow; token file lands at /root/.auth2api/codex-*.json)
docker compose exec auth2api ls -la /data /root/.auth2api 2>&1
#   /data is empty
#   /root/.auth2api contains the token file
docker compose down && docker compose up -d
# Account pool is empty on the new container — token is gone.

Fix options

Three reasonable fixes; #1 is least invasive:

  1. Update config.example.yaml: change the default to auth-dir: "/data" (since that's the path your docker-compose.yml mounts). One-line change. Users running natively (npm run dev) would need to override, which is a reasonable trade since the comment can call it out.

  2. Set ENV HOME=/data in the Dockerfile: keeps the ~/.auth2api default semantically meaningful and pointing at the right place. Slightly more magical.

  3. Update docker-compose.yml to override via env-var (would require src/config.ts to read an env-var fallback, which it currently doesn't).

I'd suggest #1 plus a brief note in the README's Docker section explaining auth-dir must point at the mounted volume.

Why this matters

Silent data loss is the worst failure mode for an OAuth proxy — the operator doesn't notice until their pool is empty and they're confused why --login worked but the running server has no accounts. For the codex provider specifically, the foot-gun is amplified by the 24h cooldown on refresh_token_reused: if the operator re-logs in to fix the "empty pool" and the running server has a now-stale refresh token cached in memory, attempting to use both copies of the token risks a 24h account ban.

Context

Caught while writing a Coolify deployment for a personal pool of ChatGPT Plus accounts (https://github.com/eumemic/oai-proxy). Worked around it in our config (auth-dir: "/data" explicitly), but figured the upstream fix would help future operators avoid the trap.

Happy to send a PR if helpful — just want to flag the issue first in case there's reason to prefer a different fix shape.

Thanks for auth2api! The refresh-token discipline (the explicit lock + the code-cited cross-reference against the codex-rs source) is the cleanest implementation I've seen across the half-dozen Codex-OAuth proxies I surveyed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions