Requires
Declares required resource dependencies. The app will not start without them. Value is an array; each entry is a string (shorthand) or an object.
A string shorthand (requires: [postgres]) expands to [{ type: "postgres" }].
An object entry is one of two kinds, distinguished by its marker field: a backing service (has type:) that the provider provisions and wires, or a host capability (has host:) that the provider grants or refuses — see Host capabilities.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
no | Resource name for expression references (defaults to type) |
type |
string |
yes | Resource type (see Resource Property Vocabulary) |
endpoint |
string |
conditional | The provides entry this resource fronts, by its name. Required on a type: https-origin entry; meaningless on any other type — see Public HTTPS origins |
version |
string |
no | Version constraint using semver ranges (e.g. >=15, ^7.0, 20.x) |
config |
map<string, any> |
no | Resource provisioning hints (platform-interpreted) |
uses |
array<string | object> |
no | Which features of the resource the app uses (e.g. [db, pubsub] for redis); a repeatable use may be named more than once (- db: cache) — see Resource uses |
set_env |
map<string, string> |
no | Maps resource properties to app env vars using $ expressions |
requires:
- type: postgres
version: ">=15"
set_env:
DATABASE_URL: $url
DB_HOST: $host
DB_PASSWORD: $passwordResource naming
By default, a resource's name is its type. Expression references like $postgres.host use this name. When an app requires multiple instances of the same type, use the name field to distinguish them:
requires:
- type: postgres
name: primary-db
set_env:
PRIMARY_DB_URL: $url
- type: postgres
name: analytics-db
set_env:
ANALYTICS_DB_URL: $urlReferences use the name: $primary-db.host, $analytics-db.host. Without name, two resources of the same type would be ambiguous.
Version constraints
The version field uses semver range syntax (as defined by node-semver):
| Syntax | Meaning |
|---|---|
>=15 |
Version 15 or higher |
^7.0 |
Compatible with 7.x (>=7.0.0, <8.0.0) |
~2.1.0 |
Approximately 2.1.x (>=2.1.0, <2.2.0) |
20.x |
Any 20.x version |
15.2.0 |
Exact version |
Resource configuration
The config map passes provisioning hints to the platform. Keys and semantics are resource-type-specific:
requires:
- type: postgres
version: ">=15"
config:
extensions: [pgvector, postgis]
shared_buffers: 256MB
- type: redis
config:
maxmemory: 256mb
maxmemory-policy: allkeys-lruThe platform interprets these hints when provisioning the resource. Unknown keys are ignored by platforms that don't support them.
Resource uses
requires: redis says the app needs Redis. It does not say what the app needs of it — one keyspace, pub/sub channels, or the whole server. uses states that fact. It is a list of tokens from a small per-type vocabulary (Resource Use Vocabulary):
requires:
- type: redis
uses: [db, pubsub]
set_env:
CACHE_URL: $redis.db.url # redis://host:6379/<index>
CACHE_DB: $redis.db.index # the integer
REDIS_URL: $url # the instance, as beforeUndeclared means what the property vocabulary already promises. A requires: redis entry with no uses is satisfied by any Redis the platform supplies that speaks the property vocabulary — pooled or dedicated — exactly as before; it does not mean a dedicated server. A requires: postgres entry with no uses is one database (name), also as before. An app that needs the whole server declares uses: [server].
Declared uses narrow the need and select the fields. A platform may satisfy the entry from a shared unit only when every declared use fits in that unit; otherwise it provisions, or refuses. Each use registers its own properties, addressed as $<resource>.<use>.<property> — $redis.db.url is the standard Redis URL with the database selected by path (redis://[user:password@]host:port/<index>), $redis.db.index that integer. The entry-level $url, $host, $port and $password keep their meaning: the instance address. A use that registers no property of its own (pubsub, server) is addressed through the instance properties. Inside an entry that declares uses, a $<resource>.<use>.<property> path either resolves from the use's registered properties or is an error — never an empty string, never a fallback to the instance value — so a mistyped use fails at wiring time rather than connecting the app to the wrong database. That error stops generation for the whole app: the provider emits nothing, for any component. It does not skip the single component, which is what a provider does when it cannot cover a declared use (D-64) — a reference to a use the entry never declared is wrong in the file, so no partly-wired deployment is produced.
A provider covers every declared use or refuses the component. A use the provider cannot cover — including a token it does not recognise, since no provider can claim to cover a use it does not know — takes the same refuse branch as a resource type it cannot provision (PROVIDERS.md §10 item 5, D-64): the component is refused before launch, naming the entry and the uncovered use. The vocabulary is open, so a token outside the standard set is a validate warning rather than a validation error — but at deploy time a typo fails the deployment rather than being silently ignored. On a supports: entry the same shortfall leaves the entry unfulfilled with a warning, never refused.
A repeatable use may be named more than once. A use the vocabulary marks repeatable (db on redis, database on postgres and mysql) may appear more than once on one entry, each occurrence written as a single-key map naming it. The name is an expression path segment and takes the name grammar (^[a-z][a-z0-9-]*$):
requires:
- type: redis
uses:
- db: cache
- db: sessions
- pubsub
set_env:
CACHE_URL: $redis.db.cache.url # redis://host:6379/<index of cache>
SESSION_URL: $redis.db.sessions.url # a different index
SESSION_DB: $redis.db.sessions.index
REDIS_URL: $url # the instance, as beforeEach named use registers the token's properties under its name, addressed as $<resource>.<use>.<name>.<property> — the same nesting $app.endpoints.<name>.<property> uses: $redis.db.cache.url is redis://[user:password@]host:port/<index> with the index allocated to cache, $redis.db.cache.index that integer; $postgres.database.<name>.url is the standard URL with /<database> as its path and $postgres.database.<name>.name that database. A platform hands each named occurrence its own unit — one Redis database per named db, one database per named database — so two names never share one. The entry-level properties keep the instance meaning, and an unnamed single db keeps the three-segment $redis.db.url form: naming is for the entry that needs more than one.
Within one entry a token is declared bare or named, never both — uses: [db, {db: cache}] is invalid, because $redis.db.url and $redis.db.cache.url would then name two different databases under one token; name every occurrence, or declare the token once unnamed. The same name twice on one token is invalid, as a bare token twice is. A name on a use the vocabulary marks non-repeatable (pubsub, server) is a validation error, not a lint warning: the name would promise a second set of channels or a second server the type cannot hand over. A token outside the standard vocabulary may take a name — the vocabulary is open, and whether a provider-defined use repeats is that provider's to say.
The strict resolution rule above extends to the named form. On an entry that names its db uses, $redis.db.url — the bare form — is an error, not the instance URL and not any one of the named databases; $redis.db.nosuch.url names an occurrence the entry does not declare and is an error too. A provider covers each named occurrence or refuses the component, naming the entry, the token and the name; a supplied resource satisfies the entry only when its map carries every registered <use>.<name>.<property> key.
Expression wiring
Values in set_env use the expression syntax. Inside a requires or supports block, bare $prop references resolve against the enclosing resource's property vocabulary.
Real-world examples: See how Ghost, Metabase, and Miniflux declare their database requirements. Browse all apps →
Public HTTPS origins
An app that only works over HTTPS says so with the backing-service type https-origin: browsers reach this app at a public origin whose scheme is https. Like any backing service, the provider provisions and wires it, or refuses — it is not a hint and not a probe.
provides:
- name: web
protocol: http
port: 80
exposed: true
requires:
- type: https-origin
endpoint: web
set_env:
DOMAIN: $urlThe app's own listener is untouched: protocol: http still describes what the component speaks on its own port (D-59). Where TLS terminates, and which certificate it uses, stay outside the file (D-5, D-15).
Six rules bind the entry:
- The type names an interface, never a product.
https-originis an origin whose scheme ishttps— nevercaddy,traefik, oracme. endpoint:names aprovidesentry by itsname(named endpoints). It is required on anhttps-originentry and meaningless on any other type. The entry MUST sit on the component that owns the endpoint; at the top level of a file that declarescomponents:it is a validation error, because top-levelrequiresdefaults into every component that declares none and one entry would face severalprovideslists. The name must match exactly one entry on that component; that entry must beexposed: trueand declare an HTTP-family listener —http,https,ws, orgrpc. Naming atcporudpentry is a validation error: those listeners have no origin.- One per app, and it defines "primary". At most one
https-originentry across all components; a second is a validation error. The named endpoint is the app's primary endpoint for$app.*derivation and for orchestrator-supplied publication context (D-58 rule 4). Declaring the entry fixes the primary — whether or not a provider fulfills it — so$app.urlnever changes value with a provider's capability. It reaches$app.*only:$components.<name>.urlis unaffected. With no entry declared, providers keep their own positional choice. - One property:
url— the same value as$app.url, from one derivation. Nohost,port,scheme,authority, ortlsproperty is registered;$app.*already standardises those. - Fulfillment, no probe. The provider provisions the origin, or accepts one supplied through its orchestrator-facing publication channel, or refuses with a clear message. A supplied URL whose scheme is not
httpsis refused. The provider does not verify the origin exists or is reachable. See PROVIDERS.md §7 and §10 item 5. supports:is the optional mood. Unfulfilled, the entry'sset_envis absent and the provider notes the un-granted dependency — the app deploys and degrades.
# mailpit — the endpoint reference takes whatever the app called it
provides:
- name: web-ui
protocol: http
port: 8025
exposed: true
- name: smtp
protocol: tcp
port: 1025
exposed: true
supports:
- type: https-origin
endpoint: web-ui # `endpoint: smtp` would be a validation error (rule 2)A ws endpoint behind an https-origin resolves url to https://…, not wss://…: url is fixed as the https origin for every admitted listener protocol. A wss spelling, if it ever arrives, arrives as a new property, never as a change to what url resolves to.
Host capabilities
A requires/supports entry can request a host capability — a privileged grant from the machine the app runs on — instead of a backing service. A capability entry is marked with host:. The provider grants it (mounts or forwards the underlying coordinate and populates the capability's properties) or refuses the deployment with a clear message; it never provisions anything. See PROVIDERS.md for the provider-side contract.
requires:
- postgres # backing service → provision + wire
- host: { container_runtime: docker } # capability → grant or refuse
set_env:
DOCKER_HOST: $url
supports:
- host: { container_runtime: any } # optional — deploy, probe, degrade| Field | Type | Required | Description |
|---|---|---|---|
host |
map<string, string | bool> |
yes | Capability name → interface value (see Capability vocabulary) |
set_env |
map<string, string> |
no | Maps capability properties to app env vars using $ expressions |
Rules:
- The
host:marker is required on every privileged entry. An entry's kind is machine-extractable from the file itself: a bare string ortype:entry is a backing service; ahost:entry is a capability. Anyone — tooling or a human reviewer — can list an app's full privilege surface with zero extra tooling.launchfile validateprints it ashost capabilities requested: […]. - The value names an interface, never a product.
container_runtime: dockermeans "the Docker Engine API" — a Podman-compatible socket satisfies it — exactly asrequires: postgresnames a wire protocol, not a vendor.container_runtime: anyis runtime-agnostic. The vocabulary is open: unknown capability names and values are tolerated by parsers; a provider that cannot grant a required capability it does not understand refuses. - Required vs optional is
requiresvssupports. A capability inrequiresmust be granted or the provider refuses to deploy the component. A capability insupportsis optional: the app deploys without it, probes its env vars at startup, and degrades gracefully. - Wiring uses
set_env, exactly as for backing services. A granted capability exposes provider-supplied properties (below); bare$propreferences inside the entry resolve against the enclosing capability's properties.
Capability properties
Like resource properties, a granted capability exposes a standard set of properties the provider computes from how it granted the capability:
| Capability | Property | Meaning |
|---|---|---|
container_runtime |
$socket |
Filesystem path of the runtime socket (e.g. /var/run/docker.sock) |
container_runtime |
$url |
DOCKER_HOST-style connection string (e.g. unix:///var/run/docker.sock, tcp://10.0.0.5:2376) |
container_runtime |
$api |
HTTP(S) API endpoint URL, when the runtime is reachable over the network |
Properties a provider cannot supply resolve to the empty string, matching unknown resource properties.
Capability vocabulary
| Capability | Values | Meaning |
|---|---|---|
container_runtime |
docker, any |
Access to a container-runtime control API. docker = the Docker Engine API (any compatible socket satisfies it, including Podman's); any = runtime-agnostic. |
network |
host |
Must share the host network stack |
filesystem |
read-write, read-only |
Host filesystem access |
privileged |
true |
Elevated privileges (e.g. device access) |
network, filesystem, and privileged are the entry-form spelling of the legacy host block keys — they are grant/refuse capabilities like any other. That block is deprecated in launch/v1 and removed in launch/v2 (D-54); entries are the form to write. Existing files using the block stay valid and keep their meaning for the whole of launch/v1 — see Migrating off the host: block.