Qwen Code CLI agent container, with an Ollama backend via docker-compose
2.7K
Qwen Code CLI coding agent, containerized and hardened for air-gapped / corporate-firewall use against an internal LLM endpoint.
This image runs the @qwen-code/qwen-code CLI agent inside a container. It is built to talk to a local or on-prem LLM backend (Ollama by default, or any OpenAI- or Anthropic-compatible endpoint such as vLLM) — no public API access required.
On startup the entrypoint configures auth, relaxes TLS/SSH for internal networks, patches settings.json to match the selected provider, runs DNS + connectivity checks against the endpoint, blocks telemetry/update hosts, then hands off to the qwen CLI in auto-approve (--approval-mode yolo) mode. It works from a mounted /workspace and reads QWEN.md project-context files.
docker pull n8500x/qwen-code:latest
Interactive run against an OpenAI-compatible endpoint (e.g. Ollama on the host):
docker run -it --rm \
-e LLM_API_BASE=http://host.docker.internal:11434/v1 \
-e LLM_API_KEY=ollama \
-e LLM_MODEL=qwen3-coder \
-v "$PWD:/workspace" \
n8500x/qwen-code:latest
The container needs an interactive TTY (-it), a mounted project at /workspace, and a reachable LLM endpoint — the entrypoint aborts with troubleshooting hints if the endpoint can't be reached.
The included docker-compose.yml brings up Ollama, pulls a model, and starts the agent wired to it:
# GPU host
docker compose up
# CPU-only host
docker compose --profile cpu up
On Windows there is a helper run.ps1:
.\run.ps1 -Model qwen3:4b # start Ollama + pull model + launch agent
.\run.ps1 -Profile cpu # CPU-only
.\run.ps1 -Shell # drop into a shell instead of the agent
.\run.ps1 -Pull # just pull the model
.\run.ps1 -Stop # tear everything down
.\run.ps1 -Logs # follow logs
node:20-slim (Node.js 20).@qwen-code/qwen-code installed globally via npm (QWEN_CODE_VERSION build arg, default latest), exposed as the qwen binary.curl, git, ca-certificates, openssh-client, openssl, dnsutils (nslookup), jq./workspace./root/.qwen/:
settings.json — preconfigured modelProviders (OpenAI-style Ollama entries for qwen3:1.7b/4b/8b, plus an Anthropic-style on-prem vLLM entry for gpt-oss-120b), telemetry and update checks disabled, compact UI mode.QWEN.md — global agent instructions (yolo mode, read workspace QWEN.md/.qwen/ files, look for host inventory/SSH configs).ssh-dark.json — the "SSH Dark" UI theme referenced by settings.json./usr/local/bin/entrypoint.sh (corporate-firewall hardening + connectivity checks) which execs qwen --approval-mode yolo.| Variable | Default | Purpose |
|---|---|---|
LLM_AUTH_TYPE | openai | Provider protocol: openai or anthropic (for vLLM Anthropic-compatible endpoints). |
LLM_API_BASE | http://host.docker.internal:11434/v1 (image) / http://ollama:11434/v1 (compose/entrypoint) | LLM endpoint base URL. For anthropic, a trailing /v1 is stripped automatically. |
LLM_API_KEY | not-needed (image) / ollama (compose) | API key; also exported to the provider-specific and DASHSCOPE_API_KEY vars. |
LLM_MODEL | qwen3-coder (image) / qwen3:1.7b (entrypoint) | Model id sent to the backend and written into settings.json. |
HTTP_PROXY / HTTPS_PROXY / NO_PROXY | empty / empty / localhost,127.0.0.1 | Proxy config; propagated to git and lowercase aliases. |
NODE_EXTRA_CA_CERTS | /etc/ssl/certs/ca-certificates.crt | Extra CA trust for Node. |
LLM_CONNECT_RETRIES | 3 | Endpoint connectivity retry attempts at startup. |
LLM_CONNECT_RETRY_DELAY | 5 | Seconds between retries. |
NODE_OPTIONS | --max-old-space-size=4096 --no-warnings | Node heap tuning for large responses. |
Based on LLM_AUTH_TYPE, the entrypoint exports the matching SDK vars (OPENAI_*/OLLAMA_API_KEY or ANTHROPIC_*) and rewrites /root/.qwen/settings.json to match at runtime.
| Path | Purpose |
|---|---|
/workspace | Your project directory (working dir). Compose uses ${PROJECT_DIR:-.}. |
/root/.qwen/ | Baked config; mount over it to supply your own settings.json / QWEN.md. |
certs/ (build time) | Drop corporate CA .crt files here before build to add them to the trust store. |
The image itself exposes no ports. The bundled compose backend (Ollama) publishes 11434:11434.
With Docker directly:
docker build \
--build-arg QWEN_CODE_VERSION=latest \
--build-arg HTTP_PROXY= \
--build-arg HTTPS_PROXY= \
--build-arg NO_PROXY=localhost,127.0.0.1 \
-t n8500x/qwen-code:latest .
Via compose:
docker compose build
Via the Makefile (also runs an integration test suite):
make build # build the image
make run # build + run interactively
make test # build + run integration tests
make test-live # tests against a live LLM backend at LLM_API_BASE
make layers # show image layer sizes
To bake in corporate CA certificates, place .crt files in certs/ before building — they are copied to /usr/local/share/ca-certificates/custom/ and registered with update-ca-certificates.
NODE_TLS_REJECT_UNAUTHORIZED=0, clears CURL_CA_BUNDLE/REQUESTS_CA_BUNDLE, and runs git config --global http.sslVerify false. Intended for trusted air-gapped/corporate networks only.StrictHostKeyChecking no, UserKnownHostsFile /dev/null) — again, internal-network assumption.settings.json, and several telemetry/update hostnames (npmjs registry, electron update, MS telemetry) are null-routed in /etc/hosts at startup.1. If host networking is the issue, try --network host or point LLM_API_BASE at http://host.docker.internal:<port>/v1.LLM_AUTH_TYPE=anthropic for vLLM Anthropic-compatible endpoints; the trailing /v1 is stripped so the SDK's /v1/messages path isn't duplicated.Content type
Image
Digest
sha256:cac502683…
Size
136.6 MB
Last updated
5 months ago
docker pull n8500x/qwen-code