Skip to content

docs/docker: ARCHON_HOME and ARCHON_DATA leak into container env with confusing semantics; no persistence path for ~/.claude/ #1517

@leex279

Description

@leex279

Summary

  • What's confusing: In Docker, ARCHON_HOME and ARCHON_DATA both leak into the container env via env_file: .env, but neither is read by Archon code in the container — ARCHON_HOME is silently overridden by isDocker(), and ARCHON_DATA is a Compose host-side substitution token that no TS source ever reads. Operators see ARCHON_DATA=/Users/x/data inside the container, try cd $ARCHON_DATA, and get "no such file or directory". .env.example documents both as configurable without the Docker caveats.
  • What's missing: The Docker image runs Claude Code as appuser with $HOME=/home/appuser, but no compose file mounts /home/appuser/.claude/ — so user-installed Claude Code skills/prompts are lost on every docker compose up --build / image pull. There's no documented persistence story for "shared .claude/skills across all repos in the container."
  • Severity: minor (UX / docs — no functional break; the bind mount itself works correctly)
  • Reported by: Community member Zolto on Dynamous forum (cross-posted by maintainer)

Steps to Reproduce

  1. cp .env.example .env
  2. Add to .env: ARCHON_DATA=/Users/myuser/develop-local/archon-data-volume (a host path)
  3. mkdir -p /Users/myuser/develop-local/archon-data-volume
  4. docker compose up -d
  5. docker compose exec app bash
  6. Inside the container:
    echo $ARCHON_DATA
    # /Users/myuser/develop-local/archon-data-volume   <-- host path, leaked
    cd $ARCHON_DATA
    # /bin/sh: cd: can't cd to /Users/myuser/develop-local/archon-data-volume
  7. Try setting ARCHON_HOME=/anywhere/else in .env, restart:
    echo $ARCHON_HOME
    # /anywhere/else      <-- leaked into env
    ls /.archon
    # ... contents of the bind mount, NOT /anywhere/else — ARCHON_HOME ignored
  8. Try mkdir -p ~/.claude/skills/myskill and re-create the container — the directory is gone (not on the bind mount).

Expected vs Actual

  • Expected (operator's mental model):
    • ARCHON_DATA and ARCHON_HOME either work consistently inside and outside Docker, OR .env.example warns clearly that they don't.
    • There's a documented place to drop .claude/skills that survives container recreation.
  • Actual:
    • ARCHON_HOME is silently ignored in Docker (packages/paths/src/archon-paths.ts:56-74getArchonHome() returns hardcoded /.archon when isDocker() is true).
    • ARCHON_DATA is never read by any TS source — only by Compose substitution at docker-compose.yml:41 (${ARCHON_DATA:-archon_data}:/.archon).
    • Both leak into the container via env_file: .env (docker-compose.yml:35, deploy/docker-compose.yml:17) and sit there as dead weight.
    • No mount exists for /home/appuser/.claude/, so any Claude Code config the user adds inside the container is ephemeral.

User Flow

Operator's expectation:                           Reality:
─────────────────────────                         ────────

.env: ARCHON_HOME=/foo                            .env: ARCHON_HOME=/foo
  |                                                 |
  v                                                 v
Compose env_file: .env  ---> container            Compose env_file: .env  ---> container
                                                                                process.env.ARCHON_HOME=/foo
                                                                                            |
Archon reads ARCHON_HOME = /foo                   Archon: isDocker() -> true
                                                                       |
                                                                       v
                                                  getArchonHome() returns '/.archon' [X] silently ignores /foo
                                                                       |
                                                                       v
                                                  Operator: "echo $ARCHON_HOME shows /foo, but
                                                             workspaces went to /.archon — wat?"


.claude/skills:                                   .claude/skills:
mkdir -p ~/.claude/skills/foo                     mkdir -p ~/.claude/skills/foo  (= /home/appuser/.claude/skills/foo)
docker compose up -d --build                      docker compose up -d --build
ls ~/.claude/skills/foo  -> persists              ls ~/.claude/skills/foo  -> [X] gone (not on a volume)

Environment

  • Platform: Docker (any host)
  • Database: any
  • OS: any (reported on macOS bind mount, but the env-leak applies everywhere)
  • Versions: dev branch @ 4631b8e (also present on main)

Logs

Inside container:

$ env | grep ARCHON
ARCHON_DOCKER=true
ARCHON_DATA=/Users/myuser/develop-local/archon-data-volume   # leaked, dead
ARCHON_HOME=~/.archon                                         # leaked, ignored

Source confirming the silent override:

// packages/paths/src/archon-paths.ts:56-74
export function getArchonHome(): string {
  if (isDocker()) {
    return '/.archon';            // ARCHON_HOME never consulted in Docker
  }
  const envHome = process.env.ARCHON_HOME;
  ...
}

grep -r ARCHON_DATA packages/ returns only doc files — no source reads it.

Impact

  • Affected workflows: any user setting up Docker for the first time who reads .env.example and tries to customize paths.
  • Reproduction rate: Always (deterministic).
  • Workaround: ignore the env vars; add a manual bind mount for /home/appuser/.claude/ via docker-compose.override.yml.
  • Data loss risk: No (just operator confusion + ephemeral skills).

Scope

  • Package(s): paths, infra, docs
  • Module: packages/paths/src/archon-paths.ts (clarify intent), .env.example, docker-compose.yml, deploy/docker-compose.yml, packages/docs-web/src/content/docs/deployment/docker.md

Proposed Cleanup

Three small, independent changes — pick any subset:

  1. Annotate .env.example (lowest cost, highest UX win):

    # ARCHON_HOME — local-only. IGNORED in Docker (the container always uses /.archon).
    # ARCHON_HOME=~/.archon
    
    # ARCHON_DATA — host-only. Used by docker-compose to choose the bind-mount source for /.archon.
    # NOT read by Archon source code. The container always sees data at /.archon regardless.
    # ARCHON_DATA=/opt/archon-data
  2. Document a ~/.claude/ persistence recipe in docs/deployment/docker.md and docker-compose.override.example.yml:

    # Persist Claude Code skills/prompts across container recreations
    services:
      app:
        volumes:
          - claude_home:/home/appuser/.claude
    volumes:
      claude_home:

    Or via bind mount: ${CLAUDE_HOME:-claude_home}:/home/appuser/.claude mirroring the ARCHON_DATA pattern. (Needs the entrypoint to chown this on bind mounts the same way it does /.archon — see docker-entrypoint.sh:7-15.)

  3. (Optional) Entrypoint warning: log a one-line stderr notice if ARCHON_HOME or ARCHON_DATA is set inside the container, e.g. [archon] ARCHON_HOME=… ignored in Docker (container home is fixed at /.archon). Deters silent confusion. Keeps existing behavior.

Definition of Done

  • .env.example notes the Docker semantics for ARCHON_HOME and ARCHON_DATA
  • docs/deployment/docker.md has a "Persisting Claude Code config" recipe (or equivalent under "Data Directory")
  • docker-compose.override.example.yml shows the mount pattern as a comment
  • (optional) entrypoint emits a warning when these vars are present in the container env

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: docsDocumentationarea: infraDocker, deployment, CI/CDdocumentationImprovements or additions to documentationdxDeveloper experience improvementseffort/lowSingle file or function, one responsibility, isolated change

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions