Sign inSign up

n8500x/qwen-code

By n8500x

Updated 5 months ago

Qwen Code CLI agent container, with an Ollama backend via docker-compose

Image
0

2.7K

n8500x/qwen-code repository overview

qwen-code

Qwen Code CLI coding agent, containerized and hardened for air-gapped / corporate-firewall use against an internal LLM endpoint.

Overview

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.

Quick start

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.

With docker-compose (bundled Ollama backend)

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

What's inside

  • Base image: node:20-slim (Node.js 20).
  • Agent: @qwen-code/qwen-code installed globally via npm (QWEN_CODE_VERSION build arg, default latest), exposed as the qwen binary.
  • System tools: curl, git, ca-certificates, openssh-client, openssl, dnsutils (nslookup), jq.
  • Working dir: /workspace.
  • Baked config in /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.
  • Entrypoint: /usr/local/bin/entrypoint.sh (corporate-firewall hardening + connectivity checks) which execs qwen --approval-mode yolo.

Configuration

Environment variables
VariableDefaultPurpose
LLM_AUTH_TYPEopenaiProvider protocol: openai or anthropic (for vLLM Anthropic-compatible endpoints).
LLM_API_BASEhttp://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_KEYnot-needed (image) / ollama (compose)API key; also exported to the provider-specific and DASHSCOPE_API_KEY vars.
LLM_MODELqwen3-coder (image) / qwen3:1.7b (entrypoint)Model id sent to the backend and written into settings.json.
HTTP_PROXY / HTTPS_PROXY / NO_PROXYempty / empty / localhost,127.0.0.1Proxy config; propagated to git and lowercase aliases.
NODE_EXTRA_CA_CERTS/etc/ssl/certs/ca-certificates.crtExtra CA trust for Node.
LLM_CONNECT_RETRIES3Endpoint connectivity retry attempts at startup.
LLM_CONNECT_RETRY_DELAY5Seconds between retries.
NODE_OPTIONS--max-old-space-size=4096 --no-warningsNode 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.

Mounts
PathPurpose
/workspaceYour 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.
Ports

The image itself exposes no ports. The bundled compose backend (Ollama) publishes 11434:11434.

Build

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.

Notes

  • TLS verification is disabled inside the container for internal-network use: the entrypoint sets 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.
  • SSH host-key checking is disabled (StrictHostKeyChecking no, UserKnownHostsFile /dev/null) — again, internal-network assumption.
  • Auto-approve (yolo) mode: the agent runs all tools without prompting for permission. Only point it at workspaces you trust.
  • Telemetry/update checks are blocked: disabled via env vars and settings.json, and several telemetry/update hostnames (npmjs registry, electron update, MS telemetry) are null-routed in /etc/hosts at startup.
  • Startup connectivity gate: if the LLM endpoint isn't reachable after the configured retries, the container prints troubleshooting steps and exits 1. If host networking is the issue, try --network host or point LLM_API_BASE at http://host.docker.internal:<port>/v1.
  • Anthropic mode: set LLM_AUTH_TYPE=anthropic for vLLM Anthropic-compatible endpoints; the trailing /v1 is stripped so the SDK's /v1/messages path isn't duplicated.

Tag summary

Content type

Image

Digest

sha256:cac502683

Size

136.6 MB

Last updated

5 months ago

docker pull n8500x/qwen-code