Skip to main content

Docker Compose's models: Key Has Been in the Official Spec Since June 2025 — What That Means for a Compose-to-Cluster-API Build Step

9 min readDora NodaDora Noda
Share
On this page

Docker Compose can now bring up a full agent stack — a local model, an MCP tool server, and the agent runtime that ties them together — with the same docker compose up a Compose file has always used to bring up a web app and its database. That capability first shipped in mid-2025, and the detail most people missed: the models: key it's built on isn't a Docker-proprietary extension anymore. It lives in the vendor-neutral Compose Specification itself, has required no breaking changes in over a year, and now has a 13-framework example catalog behind it. For a git-push PaaS whose build pipeline already parses docker-compose.yml, the honest answer to "extend the Compose-to-Cluster-API translation layer to recognize this now, or wait for it to stabilize" is: it already stabilized. There's nothing left to wait for.

What's Actually New in the YAML

Three pieces landed together, and each is a small, composable addition rather than a new file format to learn.

A top-level models: key declares an AI model the same way Compose has always declared a service, a volume, or a network — as a named, versioned resource other services can reference:

yaml
models:
  qwen:
    model: ai/qwen3:8B-Q4_0
    context_size: 32000
    runtime_flags:
      - "--a-flag"

model is the only required field — an OCI artifact identifier that a model runner pulls and serves over an OpenAI-compatible API. context_size and runtime_flags are optional tuning knobs passed straight to the inference engine.

A service-level models: reference wires that model into whichever service needs it, injecting the endpoint and model name as environment variables instead of forcing the service to hardcode a URL:

yaml
services:
  agent:
    build: .
    models:
      qwen:
        endpoint_var: MODEL_RUNNER_URL
        model_var: MODEL_RUNNER_MODEL

An mcp-gateway service is just an ordinary Compose service running Docker's official gateway image, brokering tool access for whatever MCP servers it's told to expose:

yaml
services:
  mcp-gateway:
    image: docker/mcp-gateway:latest
    use_api_socket: true
    command:
      - --transport=sse
      - --servers=duckduckgo,github
  agent:
    build: .
    depends_on: [mcp-gateway]
    environment:
      - MCPGATEWAY_ENDPOINT=http://mcp-gateway:8811/sse
    models:
      qwen:
        endpoint_var: MODEL_RUNNER_URL
        model_var: MODEL_RUNNER_MODEL

Put all three together and one compose.yaml describes the entire agent stack — model, tool access, and the agent's own code — with nothing beyond keys Compose already knows how to parse. That's the concrete change: not a new agent-hosting product, but three small extensions to a spec every tenant repo at a git-push PaaS's door already speaks.

This Isn't a Docker Extension Anymore

The part worth double-checking before building against it: is models: a Docker Desktop feature that could be pulled at any point, or something durable enough to parse in a production build pipeline?

It's the latter. The models.md file defining this schema landed in the compose-spec/compose-spec GitHub repository — the same vendor-neutral specification that Kubernetes' own Kompose tool and every other Compose-compatible platform already targets — on June 30, 2025. It requires Compose v2.38 or later, which has been shipping in Docker Desktop and Docker Engine for over a year as of this writing. The timeline runs: MCP Gateway reached general availability with Docker Desktop 4.43 on July 3, 2025; Docker announced the full agent-building-blocks story at WeAreDevelopers a week later; a comprehensive write-up followed on November 21, 2025; and Docker Offload — the separate cloud-burst product for compute-heavy model and container workloads — reached general availability on April 2, 2026, as a Docker Business add-on.

None of that reads like a feature still finding its shape. The docker/compose-for-agents repository now ships 13+ working examples spanning LangGraph, CrewAI, Google's ADK, Agno, Vercel's AI SDK, Spring AI, and multi-agent A2A patterns — evidence of an ecosystem that's already built on top of the schema, not one still waiting to see if it holds. A platform deciding whether to parse models: today isn't betting on a Docker roadmap item; it's adopting a stable line in a spec that's already a year old and multi-vendor by design.

Where Docker's Compose-for-Agents Stops

Adopting the syntax doesn't mean adopting Docker's runtime, and it's worth being precise about which half of the problem Docker actually solved:

ConcernDocker's Compose-for-AgentsA self-hosted fleet (bex)
Parse models:/mcp-gateway into a topology✅ Compose spec + CLINeeded if Compose is a supported build input
Run a model locally via Docker Model Runner✅ Core featureNot applicable — a fleet needs a fleet-wide model runner, not a per-laptop one
Sandbox an MCP tool server (CPU/memory/network caps)✅ MCP Gateway defaults (1 CPU/2GB, default-deny egress)Needed regardless of source — same defaults apply to a deployed tenant tool server
Decide which node in a multi-tenant fleet a model or agent lands on❌ Not attempted — Compose has no schedulerCluster API MachineDeployment / bin-packing
Burst a compute-heavy model workload to cloud GPU capacity✅ Docker Offload (Docker's own cloud, Business add-on)Not available to a fleet running on owned Hetzner hardware — the answer is a bex-owned GPU node pool, not a vendor's cloud
Enforce per-tenant isolation across agent + model + tool services sharing a node❌ Not attempted — Compose assumes one trust boundaryNamespace/cgroup/network-policy layer
Route a custom domain + TLS to the running agent service❌ Not attemptedcert-manager + Gateway/ingress layer

Read down that table and the shape is familiar: Compose describes the stack, Docker's own products run it on Docker's own machines, and everything about placing that stack safely on a multi-tenant fleet the operator owns is still the platform's job. Docker Offload in particular is worth calling out by name, because it's the piece of the TODO-list framing easiest to misread — it's a genuinely useful feature for a developer without a GPU laptop, but it's Docker's cloud, sold as a Docker Business add-on. It doesn't extend to a platform that has deliberately chosen to run its own Hetzner fleet instead of renting capacity from a hyperscaler-adjacent vendor; the GPU-scheduling problem for a self-hosted PaaS is solved on its own node pool via Cluster API and Dynamic Resource Allocation, not by a burst button pointed at someone else's cloud.

What This Means for bex's Compose-to-Cluster-API Translation Layer

Given the syntax is spec-level and stable, and the boundary is this clean, the recommendation is concrete rather than "watch and see":

Parse models: now. It's three optional fields on a schema Compose has offered for over a year — low surface area, low churn risk, and skipping it just means tenants who already wrote a models: block for local development have to rewrite it by hand for production.

Translate mcp-gateway like any other service — with the resource caps already decided. An mcp-gateway entry in a tenant's Compose file doesn't need new infrastructure; it needs the same CPU/memory/filesystem/network defaults Docker's own gateway already applies (1 CPU, 2GB, no host mounts, default-deny egress) carried over into whatever the Compose importer emits for every other service. The security model doesn't change because the service happens to broker MCP tool calls instead of serving HTTP.

Map the service-level models: block onto the same env-var contract, both locally and on the fleet. A tenant's agent service already expects MODEL_RUNNER_URL and MODEL_RUNNER_MODEL (or whatever variable names its endpoint_var/model_var fields name) to point at a running model endpoint. Translating that into a Deployment plus a Service — with the fleet's own model-runner backing it instead of a laptop's local one — means the tenant's code doesn't change between docker compose up and a production deploy, which is the entire promise of the compose-first onboarding path in the first place.

Don't build a Docker Offload equivalent. That's a distinct, larger problem — cloud GPU capacity, not YAML parsing — and it's already the subject of a fleet's own GPU node pool strategy, not something a Compose importer should try to shim.

Laid out as an importer would actually implement it, the three new keys map onto objects a Compose-to-Cluster-API translator already knows how to emit — nothing about the target side is novel, only the source parsing is:

Compose primitiveFleet-side objectWhat changes from a normal service
Top-level models: entryDeployment + Service running the fleet's model runner, keyed by the model OCI referencecontext_size/runtime_flags become container args instead of being dropped
Service-level models: referenceEnv vars injected into the consuming Deployment (endpoint_var → the model Service's DNS name, model_var → the model id)None — this is the same env-injection path already used for database connection strings
mcp-gateway serviceDeployment + Service, same as any Compose serviceResource requests/limits pinned to the 1 CPU/2GB/no-host-mount/default-deny defaults regardless of what the tenant's Compose file requested

None of the three rows requires a new reconciler, a new CRD, or a new admission controller — they're the existing service-import path with one extra field read (model) and one extra default policy applied (the gateway's resource caps). That's exactly the kind of low-risk addition that justifies shipping it now instead of waiting for a v2 of the spec that, a year in, shows no sign of arriving.

The net effect: a tenant who already wrote a compose.yaml with a model, an MCP tool server, and an agent service for local development gets that same file recognized as a first-class deploy target — no separate agent-hosting product, no bespoke YAML dialect to learn, just the build-target detection a git-push PaaS already runs, extended by three keys that turned out to already be stable.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, whether that repo ships a Dockerfile, a docker-compose.yml with a models: block, or neither. Star the repo on GitHub or deploy your first app today.

Sources

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex