# Qwen3.8-27B on one RTX 3090, containerized. See docs/docker.md. # # docker compose --profile single up -d # low latency: MTP speculative decoding # docker compose --profile batch up -d # throughput # docker compose run --rm prepare # just download + requantize the model # docker compose run --rm single verify # verify.sh inside the container # # Knobs go in a .env file next to this file (never committed): VLLM_API_KEY=..., # and any of the start-script variables (CTX, KV, MAX_LEN, MAX_SEQS, GPU_UTIL, VISION, # SPEC_ATTN, DRAFT_TOKENS, EXTRA_ARGS, ...) — .env is passed straight into the # container. Fastest single-user decode, if the card is yours alone (README, # "If you are the only user"; the drafter it needs is already downloaded by # prepare): # # printf 'SPEC=dflash2\nDFLASH_TOKENS=15\nPREFIX_CACHE=1\n' >> .env # # On WSL2 add VLLM_WSL2_ENABLE_PIN_MEMORY=1 to that .env: SPEC=dflash2 forces vLLM's # V2 model runner, whose UVA buffers are refused there by default, and the container # aborts at "RuntimeError: UVA is not available" before loading weights (docs/docker.md). # PORT and MODELS_DIR are read here: models live in ./models by default # (the same layout as the venv install, so both can share one download). # One GPU: run either "single" or "batch", not both. # # More than one GPU (issues #68, #104) — no hand-edit of this file needed: # # GPU_COUNT=2 # cards handed to the container # EXTRA_ARGS="--tensor-parallel-size 2" # cards the engine actually uses # # GPU_COUNT defaults to 1, which means "the first GPU the runtime enumerates", # i.e. GPU 0 — not the card you meant if the box has mixed hardware. To choose # *which* cards, expose them all and pin inside the container: # # GPU_COUNT=all # CUDA_VISIBLE_DEVICES=1,2 # # The device reservation below decides which cards are visible, so setting # NVIDIA_VISIBLE_DEVICES in .env alone was not enough (#68); CUDA_VISIBLE_DEVICES # is read inside the container and is what docs/gotchas.md 53 recommends. # Pin the KV pool before you benchmark or trim context on a multi-GPU box: the # launcher skips the single-card KV_MEM pin under --tensor-parallel-size > 1, # so the pool is sized from GPU_UTIL and moves with compile-cache state # (README, "More than one GPU"; docs/gotchas.md 48). x-qwen: &qwen # Prebuilt on every main push by .github/workflows/docker-image.yml; the # first `up` pulls it (~9.5 GB, pull_policy below) instead of the 20-minute # local build. `docker compose build` (or `up --build`) still builds the # identical image under the same tag name. Immutable per-commit tags: # ghcr.io/syv-ai/hyperqwen:sha-<7>. image: ghcr.io/syv-ai/hyperqwen:latest build: . pull_policy: missing ipc: host env_file: - path: .env required: false environment: HOME: /cache volumes: - ${MODELS_DIR:-./models}:/app/models - qwen-cache:/cache x-serve: &serve <<: *qwen ports: - "${PORT:-18020}:${PORT:-18020}" depends_on: prepare: condition: service_completed_successfully deploy: resources: reservations: devices: - driver: nvidia # 1 by default (= GPU 0). GPU_COUNT=2 / GPU_COUNT=all for TP; see # the multi-GPU note in the header for picking specific cards. count: ${GPU_COUNT:-1} capabilities: [gpu] restart: unless-stopped stop_grace_period: 30s healthcheck: test: ["CMD", "curl", "-sf", "http://127.0.0.1:${PORT:-18020}/health"] interval: 30s timeout: 5s retries: 3 start_period: 900s # first start: torch.compile + CUDA graphs + FlashInfer JIT services: prepare: <<: *qwen command: prepare single: <<: *serve profiles: [single] command: single batch: <<: *serve profiles: [batch] command: batch volumes: qwen-cache: