Skip to content

fix(docker): make the Docker deployment actually work offline - #65

Open
MA1503 wants to merge 2 commits into
nikmcfly:mainfrom
MA1503:fix/docker-offline-hygiene
Open

fix(docker): make the Docker deployment actually work offline#65
MA1503 wants to merge 2 commits into
nikmcfly:mainfrom
MA1503:fix/docker-offline-hygiene

Conversation

@MA1503

@MA1503 MA1503 commented Jul 24, 2026

Copy link
Copy Markdown

Why

MiroFish-Offline sells itself as a fully local, offline-first stack — but two
runtime cloud/config dependencies leak through the Docker path and break a clean
docker compose up. Neither shows up in native dev (the host has internet, a
warm cache, and localhost-mapped services), so they stay hidden until you run
the thing the way the README tells people to run it: in containers.

This PR fixes both. They are independent one-liners, one commit each.

1. OS env must win over .env (config injection)

backend/app/config.py loaded the .env file with load_dotenv(override=True),
which makes the .env file take precedence over real OS environment variables.
That inverts the usual convention and, more importantly, contradicts this
project's own Docker deployment model: docker-compose.yml feeds configuration
into the container through the environment (env_file: - .env), and the key
value there is NEO4J_URI — inside the container it has to resolve to the
compose service (bolt://neo4j:7687), not the localhost:7687 value that is
only correct for native dev with a port-mapped Neo4j.

With override=True, any .env that reaches the container — baked into the
image, bind-mounted, or shipped by a downstream fork — silently clobbers the
injected value, and the backend dials localhost:7687 inside the container,
where nothing is listening. Graph build then crashes with Connection refused.

Fix: both load_dotenv() calls use override=False. OS env wins. Native dev is
unchanged, because those variables aren't in the OS environment there, so the
.env values still load as before.

2. Pre-seed the tiktoken BPE cache at build time (offline runtime)

CAMEL/OASIS counts tokens with tiktoken, which downloads its BPE encodings
(o200k_base, cl100k_base) from openaipublic.blob.core.windows.net on first
use — a hidden runtime cloud dependency. In the container the simulation
subprocess crashes with NameResolutionError the moment DNS is unavailable,
which is exactly the environment an "offline" stack is supposed to survive.
Native runs never hit this because the host has internet and a warm cache.

Fix: set TIKTOKEN_CACHE_DIR and download both encodings during the image
build, so simulation runtime needs zero external hosts.

What this PR does not change

  • .dockerignore already exists in main and is comprehensive — it already
    excludes .env, backend/.venv, node_modules, .git, and
    backend/uploads, so the image doesn't drag in the host venv (which would make
    uv re-sync from PyPI on every container start) and doesn't bake secrets. No
    change needed.
  • .env into the container is already handled: docker-compose.yml passes
    it via env_file: - .env, so config still reaches the container even though
    .env is (correctly) kept out of the image.

Testing notes

  • backend/app/config.py still parses (ast.parse), and the change is a pure
    default flip on load_dotenv.
  • The Dockerfile change is a build-time ENV + RUN inserted after uv sync
    (so tiktoken is already installed) and before COPY . . (so the cache
    survives the source copy); no application code is touched.

🤖 Generated with Claude Code

MA1503 and others added 2 commits July 24, 2026 23:22
config.py called load_dotenv(override=True), which makes a .env file take
precedence over real OS environment variables. That inverts the standard
convention and breaks the Docker deployment model, where docker-compose feeds
configuration into the container through the environment (env_file). The value
that matters most here is NEO4J_URI: inside the container it must resolve to the
compose service (bolt://neo4j:7687), not the localhost value that is only correct
for native dev with a port-mapped Neo4j. Any .env that reaches the container
(baked in, bind-mounted, or shipped by a fork) would silently override the
injected value with override=True, and the backend would dial localhost:7687
inside the container — nothing there — and crash graph build with
Connection refused.

Fix: both load_dotenv() calls use override=False, so OS env wins. Native dev is
unchanged — those vars aren't in the OS env there, so the .env values still load.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ntime

CAMEL/OASIS token counting imports tiktoken, which downloads
o200k_base/cl100k_base from openaipublic.blob.core.windows.net on first use — a
hidden runtime cloud dependency. In the container the simulation subprocess
crashes with NameResolutionError whenever DNS is unavailable, which defeats the
point of an offline-first stack. Native runs never hit this because the host has
internet and a warm cache.

Bake both encodings into the image via TIKTOKEN_CACHE_DIR and a build-time
download, so simulation runtime needs zero external hosts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant