---
title: "Why Compose is generated"
summary: "Compose is an artifact Onebox generates, not an input it reads. What that buys, and how you get out."
description: "Why the runtime is derived from your declaration rather than read from a file you wrote."
status: shipped
read_when:
  - "Wondering why you cannot hand Onebox a docker-compose.yml as the contract"
---
Onebox does not read a Compose file you wrote. The contract is a declaration of
what the application *is*; the Compose file, the container names and the routing
labels are all derived from it.

## Why the runtime is not yours to hand over

Under the [ownership boundary](/explanation/ownership-boundary), Onebox owns the
runtime. Reading a runtime you wrote inverts that: you own it, and Onebox merely
operates something it did not design and cannot fully reason about.

Concretely, that costs three things:

**Two descriptions that can disagree.** The Compose file would say one thing,
the Onebox annotations another, and nothing would arbitrate.

**No derivation.** Onebox could not choose the container name, the volume name,
the router rule or the network, because you already would have. So it could not
guarantee they are consistent, collision-free, or stable across releases.

**No honest defaults.** `strategy: rolling` needs a health check to gate it. When
the health check lives in a file Onebox merely reads, "is this rollout gated?"
becomes a question rather than a fact.

## What generation buys

- **Derived, stable names.** Application containers use the uniform
  `<app>-<component>-<replica>` grammar, such as `shop-web-1`; persistent and
  provider resources include `ob_shop_postgres_data`, `ob_shop`, and
  `ob-ingress`. Once a volume exists its name cannot change without moving data,
  and a foreign resource already holding a derived name is refused rather than
  adopted.
- **Digest binding.** The rendered Compose is bound into the plan, so what was
  reviewed is what executes.
- **Refusals that mean something.** `strategy_ungated`, `route_collision`,
  `stateful_replicas` are all statements about the generated runtime, which is
  the only thing that will actually run.
- **Typed routing.** Two workloads claiming the same entrypoint, protocol, domain
  and path is `route_collision`, rather than a proxy that accepts both and routes
  to one with nothing saying which.

## You can still see it, and still leave

Generation is only acceptable because the output is inspectable and the exit is a
command:

```sh
ob preview    # print the generated runtime, change nothing
ob eject      # write it out and hand it over, permanently
```

`ob eject` strips the overlay, so what lands in your repository is ordinary
Compose — not Compose carrying Onebox labels you would then have to unpick — and
your workloads are repointed at it with your comments intact.

## And the escape hatch remains

A container Onebox cannot describe is adopted verbatim:

```yaml
workloads:
  legacy:
    role: daemon
    compose: docker-compose.yml#legacy
```

Refusals apply only where the referenced service contradicts something Onebox
owns — `extends`, a fixed `container_name`, `network_mode`, labels in the `ob.`
or `traefik.` namespaces. See [Adopt an existing Compose file](/guides/adopt-compose).

The result is a contract that generates by default and defers on request, rather
than one that can only do one of the two.