Deployment manifest
Minimal example
Section titled “Minimal example”apiVersion: odysseus/v1kind: Deploymentmetadata: name: workerspec: image: registry.delta-telematics.ca/acme/worker:2.1.0 replicas: 2 networks: [acme-network] environment: QUEUE: jobs resources: limits: {cpu: "1", memory: 512Mi} requests: {cpu: 250m, memory: 256Mi}Manifest · testdata/docs-examples/minimal-deployment.yamlFull example
Section titled “Full example”apiVersion: odysseus/v1kind: Deploymentmetadata: name: api labels: app: apispec: image: registry.delta-telematics.ca/acme/api:1.4.2 replicas: 3 environment: LOG_LEVEL: info networks: [acme-network, traefik-public] resources: limits: {cpu: "1", memory: 512Mi} requests: {cpu: 250m, memory: 256Mi} healthCheck: # required for rolling type: exec command: "wget -qO- http://localhost:8080/healthz" interval: 10s timeout: 15s retries: 3 startPeriod: 20s secrets: - name: DB vaultPath: deployments/api/db dependsOn: - job: api-migrate condition: complete placement: affinity: {label: role, value: app} onNodeFailure: reschedule ingress: host: api.example.com port: 8080 tls: {enabled: true, certResolver: letsencrypt} healthCheck: {path: /healthz, interval: 10s, timeout: 3s} # LB probe — the zero-downtime part compress: true headers: response: set: {X-Frame-Options: DENY} retry: {attempts: 3, initialInterval: 100ms} update: strategy: rolling allow_concurrent_versions: true # required attestation max_surge: 1 max_unavailable: 0 health_timeout: 90s min_healthy_time: 15s progress_deadline: 10m failure_action: rollbackManifest · testdata/docs-examples/routed-deployment.yamlDeployment spec
Section titled “Deployment spec”Generated from DeploymentSpec. Hand edits to this table are overwritten on the next build — change the Go doc comment, or the generator.
| Field | Type | Required | Default | Mutability | Since |
|---|---|---|---|---|---|
canary |
CanarySpec | no | — | in-place | 0.74.0 |
capAdd |
string[] | no | — | recreate | 0.74.0 |
capDrop |
string[] | no | — | recreate | 0.74.0 |
command |
string[] | no | — | recreate | 0.74.0 |
dependsOn |
DependencyRef[] | no | — | in-place | 0.7.6 |
dns |
string[] | no | — | recreate | 0.74.0 |
dnsSearch |
string[] | no | — | recreate | 0.74.0 |
entrypoint |
string[] | no | — | recreate | 0.74.0 |
environment |
object | no | — | recreate | 0.1.0 |
expose |
integer[] | no | — | recreate | 0.74.0 |
extraHosts |
string[] | no | — | recreate | 0.74.0 |
healthCheck |
HealthCheckSpec | no | — | recreate | 0.1.0 |
hostname |
string | no | — | recreate | 0.74.0 |
image |
string | no | — | recreate | 0.1.0 |
imagePullSecret |
string | no | — | recreate | 0.74.0 |
ingress |
IngressSpec | no | — | recreate | 0.9.4 |
init |
boolean | no | — | recreate | 0.21.1 |
logging |
LoggingConfig | no | — | recreate | 0.74.0 |
networkAliases |
string[] | no | — | recreate | 0.74.0 |
networks |
string[] | no | — | recreate | 0.1.0 |
onNodeFailure |
string | no | — | in-place | 0.9.4 |
placement |
PlacementSpec | no | — | recreate | 0.7.6 |
ports |
PortMapping[] | no | — | recreate | 0.74.0 |
privileged |
boolean | no | — | recreate | 0.74.0 |
probe |
ProbeSpec | no | — | recreate | 0.74.0 |
readOnly |
boolean | no | — | recreate | 0.74.0 |
replicas |
integer | no | — | in-place | 0.1.0 |
resources |
ResourceSpec | no | — | recreate | 0.1.0 |
restartPolicy |
string | no | — | recreate | 0.74.0 |
restartRetries |
integer | no | — | recreate | 0.7.6 |
scaling |
ScalingSpec | no | — | in-place | 0.1.0 |
secrets |
SecretSpec[] | no | — | recreate | 0.1.0 |
securityOpt |
string[] | no | — | recreate | 0.74.0 |
stack |
string | no | — | recreate | 0.74.0 |
stopGracePeriod |
integer | no | — | recreate | 0.74.0 |
ulimits |
Ulimit[] | no | — | recreate | 0.21.1 |
update |
UpdatePolicy | no | — | in-place | 0.11.0 |
user |
string | no | — | recreate | 0.74.0 |
volumes |
VolumeSpec[] | no | — | recreate | 0.1.0 |
workingDir |
string | no | — | recreate | 0.74.0 |
canary: Canary shifts traffic to the new version in weighted steps and promotes or
aborts on the thresholds it carries. See CanarySpec.
capAdd: CapAdd grants individual Linux capabilities, e.g. NET_ADMIN.
capDrop: CapDrop removes capabilities the runtime would otherwise grant. “ALL”
followed by a narrow capAdd is the safe shape.
command: Command overrides the image’s CMD, as argv. Empty leaves the image default.
dependsOn: SP-6, ruling JD4
dns: DNS are custom resolvers for the container, overriding the daemon’s.
dnsSearch: DNSSearch are the search domains appended to unqualified names.
entrypoint: Entrypoint overrides the image’s ENTRYPOINT, as argv.
environment: Environment are plain environment variables set on every container. Never
put a credential here — use secrets:, which resolves from Vault at
dispatch and never stores the value.
expose: Expose are ports reachable only from other containers on the same network.
extraHosts: ExtraHosts are additional /etc/hosts entries, each written “name:address”.
healthCheck: HealthCheck is the container probe. Only type: exec produces a real
healthcheck; an http or tcp declaration is refused rather than left inert.
hostname: Hostname is the container’s own hostname. Leave it unset to take the
container name.
image: Image is the fully-qualified container image reference, including an
explicit tag. Never “:latest” in production: an unpinned tag makes a
rollback impossible to describe.
imagePullSecret: ImagePullSecret names the registry credential used to pull the image.
ingress: Ingress is the WP3 typed ingress block (IG1/IG3/IG9/IG12). G1: the
manifest surface had no ingress support before this task — authors had
to hand-write traefik.* labels, which ValidateIngress now rejects
outright whenever ingress: is also set.
init: Init runs Docker’s tiny init as PID 1 so orphaned processes are reaped.
Omit it to take the platform default, which is on; set false only for an
image that already runs its own init.
logging: Logging selects the Docker logging driver and its options.
networkAliases: NetworkAliases are extra DNS names the container answers to on its
networks, for discovery by other containers.
networks: Networks are the Docker networks the container joins. Names are
tenant-prefixed for you; a backend should stay off traefik-public.
onNodeFailure — A node hosting several deployments takes the most conservative policy among
them — keep beats approve beats reschedule. The policy is resolved across
the node, not per deployment, so setting reschedule on one deployment does not
override a keep on another sharing the node.
onNodeFailure: OnNodeFailure is the WP6 (ND5) per-deployment node-failure policy:
reschedule | approve | keep (empty = cluster default). #71 NF-4: without
this field the manifest surface could not opt into keep/approve.
placement: SP-4; shared: Jobs place too
ports: Ports publishes container ports on the host. Container-to-container
traffic needs no entry here — use a shared network instead.
privileged: Privileged gives the container full access to the host’s devices and
capabilities. It is an escape from container isolation, not a permission
level — prefer capAdd with the specific capability.
probe: Probe declares an external blackbox target for monitoring. It is
observability ONLY and never gates a rollout — that is healthCheck’s job,
and confusing the two is how an inert probe comes to satisfy a readiness
gate it was meant to guard.
readOnly: ReadOnly mounts the container’s root filesystem read-only. Anything that
must be written needs a volume or a tmpfs mount.
replicas: Replicas is how many container instances to keep running. 0 is accepted
and means “declared but stopped”.
resources: Resources are the CPU and memory limits and requests, written as
limits/requests rather than as four flat keys.
restartPolicy: RestartPolicy is what Docker does when the container exits. The accepted
values differ by kind and are enforced by each kind’s own validator.
restartRetries: RestartRetries caps Docker’s on-failure restart attempts. It is only
meaningful for a workload whose restart policy is on-failure.
scaling: Scaling turns on autoscaling between a floor and a ceiling of replicas,
driven by the metrics it names.
secrets: Secrets are references to material in Vault, injected as environment
variables at dispatch. The value never appears in this document.
securityOpt: SecurityOpt are the runtime’s security options, e.g.
“no-new-privileges:true” or a seccomp profile.
stack: Stack is the logical grouping this deployment belongs to, emitted as the
com.docker.compose.project label so tools such as Portainer group its
containers together. Empty defaults to “odysseus”.
stopGracePeriod: StopGracePeriod is how many seconds the container gets to exit after
SIGTERM before it is killed.
ulimits: Ulimits are the container’s POSIX resource limits. A per-container ulimit
overrides the daemon’s defaults, which is the supported way to raise a
file-descriptor ceiling — never a hand-edited daemon.json.
update: Update embeds types.UpdatePolicy directly (WP4 RM-1, G3 ruling): the
types struct carries yaml tags, so the manifest inherits them 1:1 and
strict parsing covers the nested fields via registeredKinds.
user: User is the UID:GID or username the container process runs as.
volumes: Volumes are the named volumes and bind mounts attached to each container.
workingDir: WorkingDir is the directory the process starts in, overriding the image’s
WORKDIR.
resources
Section titled “resources”Generated from ResourceSpec. Hand edits to this table are overwritten on the next build — change the Go doc comment, or the generator.
| Field | Type | Required | Default | Mutability | Since |
|---|---|---|---|---|---|
limits |
ResourceValues | no | — | recreate | 0.1.0 |
requests |
ResourceValues | no | — | recreate | 0.1.0 |
ulimits
Section titled “ulimits”Generated from Ulimit. Hand edits to this table are overwritten on the next build — change the Go doc comment, or the generator.
| Field | Type | Required | Default | Mutability | Since |
|---|---|---|---|---|---|
hard |
integer | no | — | recreate | 0.21.1 |
name |
string | no | — | recreate | 0.21.1 |
soft |
integer | no | — | recreate | 0.21.1 |
healthCheck
Section titled “healthCheck”Generated from HealthCheckSpec. Hand edits to this table are overwritten on the next build — change the Go doc comment, or the generator.
| Field | Type | Required | Default | Mutability | Since |
|---|---|---|---|---|---|
command |
any | no | — | recreate | 0.1.0 |
interval |
string | no | — | recreate | 0.1.0 |
path |
string | no | — | recreate | 0.1.0 |
port |
integer | no | — | recreate | 0.1.0 |
retries |
integer | no | — | recreate | 0.1.0 |
startPeriod |
string | no | — | recreate | 0.1.0 |
timeout |
string | no | — | recreate | 0.1.0 |
type |
string | no | — | recreate | 0.1.0 |
command: Command is the probe command, in either the shell-string or the argv-list
form (see ShellOrArgv). Both land on the same stored []string.
interval — There is no floor. Clamping an interval upward would delay failure detection, so
unlike the timeout it is not safety-monotonic — a longer timeout can only reduce
spurious kills, a longer interval can only slow detection. odysseus spec-audit
reports the value; nothing rejects or changes it.
timeout — There is a 10s floor for exec probes: a timed-out exec is SIGKILLed and reparented to a
PID 1 that never reaps it, so a shorter value is refused outright on create (code
healthcheck_timeout_below_floor). On a DEPLOYMENT update that omits healthCheck
entirely, the stored value is restored first and THEN clamped up to the floor if it
predates this rule — silently, unless you are reading the response, where it is
disclosed as healthcheck_timeout_clamped. Omit timeout to take Docker’s 30s default,
which already clears the floor; state it explicitly only to set a tighter bound than
the default.
type: http, tcp, exec
volumes
Section titled “volumes”Generated from VolumeSpec. Hand edits to this table are overwritten on the next build — change the Go doc comment, or the generator.
| Field | Type | Required | Default | Mutability | Since |
|---|---|---|---|---|---|
distributedVolumeId |
string | no | — | recreate | 0.63.0 |
readOnly |
boolean | no | — | recreate | 0.1.0 |
source |
string | no | — | recreate | 0.1.0 |
target |
string | no | — | recreate | 0.1.0 |
distributedVolumeId: DistributedVolumeID mounts a DVM volume by its typed ID (#414). Its
PRESENCE is the discriminator — the manifest surface deliberately has
no type key (every plain manifest volume is a bind mount, see
convertVolumeSpecs), so exactly one of source/distributedVolumeId is
set, and the shape validator downstream rejects both-or-neither.
secrets
Section titled “secrets”Generated from SecretSpec. Hand edits to this table are overwritten on the next build — change the Go doc comment, or the generator.
| Field | Type | Required | Default | Mutability | Since |
|---|---|---|---|---|---|
anchor |
string | no | — | recreate | 0.37.0 |
mountPath |
string | no | — | recreate | 0.1.0 |
name |
string | no | — | recreate | 0.1.0 |
rotation |
string | no | — | recreate | 0.1.0 |
vaultPath |
string | no | — | recreate | 0.1.0 |
anchor: Anchor: volume(<id>) | process | external — WP18 S10.
placement
Section titled “placement”Generated from PlacementSpec. Hand edits to this table are overwritten on the next build — change the Go doc comment, or the generator.
| Field | Type | Required | Default | Mutability | Since |
|---|---|---|---|---|---|
affinity |
PlacementSelector | no | — | recreate | 0.7.6 |
antiAffinity |
PlacementSelector | no | — | recreate | 0.7.6 |
node |
string | no | — | recreate | 0.7.6 |
preferredZone |
string | no | — | recreate | 0.7.6 |
residency |
ResidencySpec | no | — | recreate | 0.74.0 |
affinity: Affinity steers placement TOWARD nodes carrying a label. It is a
preference the scorer weighs, not a filter that refuses — a workload with
an unsatisfiable affinity is still placed, just not where it asked.
antiAffinity: AntiAffinity steers placement AWAY from nodes carrying a label, with the
same weigh-do-not-refuse semantics as Affinity.
node: Node pins the workload to one node by name, landing on Deployment.NodeID.
Leave it unset to let the scheduler choose; an empty value never clears an
assignment the scheduler already made.
preferredZone: PreferredZone names an edge zone the scorer prefers.
NOTE, so nobody debugs this twice: no zone is declared in any shipped control-plane config today, so this currently scores against nothing. That is a real finding with its own issue, not a defect in this block.
residency: Residency is where the workload is legally allowed to run (S19, #557).
dependsOn
Section titled “dependsOn”Generated from DependencyRef. Hand edits to this table are overwritten on the next build — change the Go doc comment, or the generator.
| Field | Type | Required | Default | Mutability | Since |
|---|---|---|---|---|---|
condition |
string | no | — | in-place | 0.7.6 |
deployment |
string | no | — | in-place | 0.7.6 |
job |
string | no | — | in-place | 0.7.6 |
condition: deployment: started|healthy; job: complete
job: WP8 JB-6: depend on a job (condition: complete)
scaling
Section titled “scaling”Generated from ScalingSpec. Hand edits to this table are overwritten on the next build — change the Go doc comment, or the generator.
| Field | Type | Required | Default | Mutability | Since |
|---|---|---|---|---|---|
enabled |
boolean | no | — | in-place | 0.1.0 |
maxReplicas |
integer | no | — | in-place | 0.1.0 |
metrics |
MetricSpec[] | no | — | in-place | 0.1.0 |
minReplicas |
integer | no | — | in-place | 0.1.0 |
scaleDownCooldown |
string | no | — | in-place | 0.1.0 |
scaleUpCooldown |
string | no | — | in-place | 0.1.0 |
ingress
Section titled “ingress”Generated from IngressSpec. Hand edits to this table are overwritten on the next build — change the Go doc comment, or the generator.
| Field | Type | Required | Default | Mutability | Since |
|---|---|---|---|---|---|
basicAuth |
BasicAuthMiddleware | no | — | recreate | 0.9.4 |
compress |
boolean | no | — | recreate | 0.9.4 |
forwardAuth |
ForwardAuthMiddleware | no | — | recreate | 0.9.4 |
headers |
HeadersMiddleware | no | — | recreate | 0.9.4 |
healthCheck |
IngressHealthCheck | no | — | recreate | 0.11.0 |
host |
string | no | — | recreate | 0.9.4 |
ipAllowList |
IPAllowListMiddleware | no | — | recreate | 0.9.4 |
middlewares |
string[] | no | — | recreate | 0.9.4 |
network |
string | no | — | recreate | 0.9.4 |
pathPrefix |
string | no | — | recreate | 0.9.4 |
port |
integer | no | — | recreate | 0.9.4 |
priority |
integer | no | — | recreate | 0.9.4 |
rateLimit |
integer | no | — | recreate | 0.9.4 |
redirect |
RedirectMiddleware | no | — | recreate | 0.9.4 |
retry |
RetryMiddleware | no | — | recreate | 0.11.0 |
routerName |
string | no | — | recreate | 0.9.4 |
stickySessions |
boolean | no | — | recreate | 0.9.4 |
stripPrefix |
StripPrefixMiddleware | no | — | recreate | 0.9.4 |
tls |
IngressTLSSpec | no | — | recreate | 0.9.4 |
basicAuth: WP3.1 middleware definitions (IM1) — the types structs are reused
verbatim (they carry yaml tags), so the manifest surface mirrors
IngressConfig 1:1 by construction and cannot drift.
healthCheck: HealthCheck: Traefik LB probes (WP4 RU4) — types struct reused verbatim.
update
Section titled “update”Generated from UpdatePolicy. Hand edits to this table are overwritten on the next build — change the Go doc comment, or the generator.
| Field | Type | Required | Default | Mutability | Since |
|---|---|---|---|---|---|
allow_concurrent_versions |
boolean | no | — | in-place | 0.11.0 |
failure_action |
string | no | — | in-place | 0.11.0 |
health_timeout |
integer | no | — | in-place | 0.1.0 |
max_failure_ratio |
number | no | — | in-place | 0.11.0 |
max_surge |
integer | no | — | in-place | 0.1.0 |
max_unavailable |
integer | no | — | in-place | 0.1.0 |
min_healthy_time |
integer | no | — | in-place | 0.11.0 |
progress_deadline |
integer | no | — | in-place | 0.11.0 |
rollback_on_fail |
boolean | no | — | in-place | 0.1.0 |
strategy |
string | no | — | in-place | 0.1.0 |
allow_concurrent_versions: AllowConcurrentVersions is the criterion-3 operator attestation (RU3):
old and new versions may briefly coexist. REQUIRED for rolling/canary;
never inferred.
failure_action: FailureAction: “pause” (default) or “rollback” (to last stable, RU6).
health_timeout: HealthTimeout is the per-instance healthy DEADLINE (WP4 semantics).
max_failure_ratio: MaxFailureRatio is the tolerated failed-instance fraction [0,1].
min_healthy_time: MinHealthyTime: an instance must STAY healthy this long, unbroken, to
count (a health flip resets the clock — RU5/G4).
progress_deadline: ProgressDeadline bounds the whole rollout; resets per healthy instance.
rollback_on_fail: RollbackOnFail is honored as FailureAction “rollback” (retained field).