zenith-compose.yml is a Docker Compose file with one Zenith extension: x-zenith.
File requirements
Zenith fetches the exact default-branch commit selected during submission. Later changes to
zenith-compose.yml are not automatically pulled and will not change an existing submission until you submit a new revision.
Unknown fields inside x-zenith fail parsing. Standard Compose fields are parsed separately; only the fields listed under Compose compatibility have defined Zenith runtime behavior.
Document shape
zenith-compose.yml
catalog
Set catalog.name in the repository. It is the only catalogue field required before submission.
expose
expose maps one service port to the app’s public HTTP hostname. The entry produces a route from / to the selected service and port.
The endpoint uses the deployment’s normal hostname:
Endpoint validation
servicemust name an existing Compose service.portmust be between1and65535.webmust be present, even when its value isfalse.exposemust contain one entry.
The exposed endpoint uses HTTP routing with TLS terminated by Zenith.
web: false does not create a raw TCP or UDP endpoint.storage
storage is a map keyed by a stable storage ID. Each value describes a top-level Compose named volume.
Zenith combines the app’s persistent volumes into one deployment allocation. Each named, anonymous, or persistent directory mount contributes to its total size. A named volume uses the matching
default_size; an unsized volume contributes 100Mi.
The stored directory for a named volume is derived from its Compose volume name with _ changed to -. Two volume names that normalize to the same directory, such as app_data and app-data, fail validation.
Public storage validation
Whenpublic is true:
volumemust be set.- The named volume must exist in the top-level Compose
volumesmap. - The volume cannot be external.
- No other public storage entry may reference the same volume.
configs
configs accepts a list of configuration-file descriptors.
This block is separate from the standard Compose
configs key. Developer submissions cannot use configs.file, and the current Zenith renderer does not mount Compose config references.
env
env is a map from a container environment variable name to an environment declaration.
PATH, every LD_* name, and environment variables that can make a loader or interpreter execute user-selected code: NODE_OPTIONS, PYTHONPATH, PYTHONSTARTUP, PYTHONHOME, BASH_ENV, ENV, JAVA_TOOL_OPTIONS, _JAVA_OPTIONS, RUBYOPT, PERL5LIB, and PERL5OPT. Set a fixed value under the service’s Compose environment block when the app requires one of these names.
Environment declaration
Every declaration must contain
input or exactly one computed source: generate, template, or alias. The computed source fields are mutually exclusive.
A bare string is shorthand for alias:
input
default and default_template are mutually exclusive. Neither may be used when the same declaration has generate, template, or alias.
A static default that uses an address at zenith.hosting, example.com, example.org, or example.net fails submission validation. To seed an account with the deployment owner’s address, use default_template: '{ZENITH_OWNER_EMAIL}'.
An input attached to a computed source adds display metadata and validation. It also makes the field available in the deployment editor. A generated input accepts an owner replacement because its stored raw value is the source. An alias or template remains source-driven and resolves again on every render. An input-only declaration uses the stored owner value or its default.
Environment values are limited to 4096 bytes when set through the deployment editor.
Clearing an input-only value resets it to its declared default on the next render. A computed input cannot be cleared to an empty value; it must be replaced with another value. A declaration without input is not owner-editable.
Resolution sources
Generated and defaulted values are stable for the lifetime of a deployment. Changing a generator pattern or input default does not rotate a value already stored by an existing deployment.
generate
generate accepts an RE2 regular expression. Zenith expands it into a deterministic value and stores the result.
template
template substitutes {NAME} references with declarations or built-ins on every render.
{{ when you need a literal { before text that could otherwise be parsed as a reference.
An empty template fails validation.
alias
alias copies another declaration or built-in on every render.
services
Without services, Zenith injects a declared value into every Compose service. With services, every listed name must exist and the value is injected only into those services.
x-zenith.env value replaces a Compose environment value with the same name in every targeted service.
Owners may also add undeclared environment variables to a deployment. Zenith injects those variables only into services marked by an expose entry with web: true. If no exposed endpoint has web: true, Zenith injects them into every service.
transform
Transforms run after the raw source is resolved and validated. The raw value remains stored and visible according to the input rules. The transformed value is injected and is the value referenced by downstream aliases and templates.
Use
urlencode on one URL component, such as an SMTP username or password. Do not apply it to a complete URL.
An unknown transform fails validation. argon2id on alias or template also fails validation.
legacy
legacy migrates a stored value from Zenith’s pre-v1 {{ZENITH_*}} token system into a generated declaration.
{{ZENITH_ fails submission validation.
Built-ins
Built-ins are resolved for each deployment. Zenith does not inject them automatically. Declare an alias or template when the app needs one.ZENITH_OWNER_EMAIL can be empty. Use it with default_template when creating an owner account. The value is stored only after it resolves non-empty.
Compose compatibility
Zenith uses the Compose model for service definitions, then maps a defined subset to its runtime. Every service becomes one independently managed workload.Service fields
Services resolve each other by service name inside the deployment.
ports does not limit which ports sibling services can reach.
Health checks
Compose health checks are exec-based in Zenith:CMDruns the argument list directly.CMD-SHELLruns through/bin/sh -c.interval,timeout, andretriesmap to probe timing.start_periodcreates a startup probe budget.- Readiness always uses a failure threshold of
3so unhealthy services stop receiving public traffic promptly.
condition: service_healthy waits for the same readiness signal.
Persistent volumes
Top-level, non-external named volumes share one persistent allocation. Each volume receives its own stable subdirectory. Anonymous volume mounts are also persistent and receive stable subdirectories derived from the service name and target path. External volumes do not create Zenith storage. Do not rely on an external volume being present in the deployment.Secrets and configs
Developer submissions cannot read repository or host files. Therefore:- Top-level
secrets.<name>.fileis rejected. - Top-level
configs.<name>.fileis rejected. - Service
env_fileandlabel_fileare rejected. - Service
extendsis rejected. - Top-level
includeis rejected.
environment values, x-zenith.env, or configuration baked into the image.
Unsupported build and host features
Submission validation
Zenith performs these checks before accepting the app for review:- Fetch the root
zenith-compose.ymlfrom the selected default-branch commit. - Enforce the non-empty
128 KiBfile limit. - Reject external file references and bind mounts.
- Load the Compose project with an empty host environment and discard environment files.
- Parse
x-zenithstrictly. - Validate required catalogue and endpoint fields.
- Resolve every
x-zenith.envdeclaration against deterministic test deployment data. - Render the complete app and reject invalid or duplicate runtime object names.
- Reject unresolved pre-v1
{{ZENITH_*}}tokens.
- An empty Compose service set.
- A service without
image. - Endpoint references to missing services.
- Invalid or colliding service names.
- Colliding persistent-volume directory names.
- Invalid public storage references.
- Invalid environment declarations.
Passing validation makes the app eligible for review. It does not publish the app automatically.
Defaults summary
Build the file
Return to the step-by-step guide.