Skip to main content
A stackfile is a YAML document that describes a complete stack: its workloads, how they are built or pulled, how they wire to each other, and how they consume secrets, addons, and volumes. It is the declarative authoring surface for both humans and agents. A stackfile describes and connects. It never creates secrets or addons — those must already exist in the signup-created organization’s default project, and the stackfile references them by name. During alpha, Stackdome uses that default project automatically. There is no project-selection step in the application workflow. The machine-readable schema lives at pkg/stackfile/schema.json (JSON Schema draft-07). Unknown fields are rejected at parse time — a typo’d key is an error, never silently ignored.

Top-level structure

Resources

Each entry under resources: is one workload. Exactly one of image or build is required.

Workload types

Building from source

Rules:
  • branch and tag are mutually exclusive.
  • commit pins a SHA and requires branch or tag alongside it.
  • Neither branch nor tag: the server resolves the repository’s default branch.
  • Clone credentials come from the preview config or an organization git integration — never from the stackfile.

Ports

At most 20 ports per resource. Ports do double duty: they configure networking and they define the resource’s outputs (next section).

Outputs and how to consume them

Three kinds of things produce outputs: resources, addons, and secrets. Each has its own vocabulary and consumption syntax.

Resource outputs

A resource’s outputs are derived entirely from its ports: Naming rule: a resource with one port uses bare names (port, url, public_url). A resource with multiple ports suffixes with the port name (port.http, url.grpc, public_url.web). host is always bare. Referencing bare url on a multi-port resource is rejected as ambiguous.

Your own outputs — {{ self.X }}

The ref must be the entire value. No templates, no mixing with other refs.

Another resource’s outputs — {{ <resource>.X }}

Exact ref or template. All refs in one value must come from the same source resource.
Multi-port source:
Everything is validated at parse time: an unknown output name errors with the list of valid outputs for that resource.

Addon outputs

Declared under a resource’s addons: block. The addon must already exist in the default project; the block key is its name. Currently supported type: postgres. Postgres outputs: host, port, database, username, password, sslmode, ca_certificate, url. Inside the addon’s env:, outputs are referenced bare — no prefix, since the block already names the addon. Templates are fine and may mix any outputs.
{{ postgres.host }} (dotted) is invalid — bare names only, rejected at parse time.

Superuser credentials

With superuser: true, {{ username }} / {{ password }} resolve to superuser credentials instead of the app owner role. The addon itself must have been provisioned with enable_superuser_access — the stackfile only requests the connection, it cannot enable it. Typical pattern: the app connects plain (owner credentials); a dedicated Job resource connects with superuser: true for DDL, extensions, or migrations.

Secret outputs

Each key of a secret in the default project is an output. Consumption is a plain map — no {{ }}, no templating:

Cheat sheet

Volumes

Declare volumes at the top level, mount them per resource:
A mount referencing an undeclared volume is a parse error.

Complete example

Validation summary

Checked at parse time (before anything reaches the server):
  • name and at least one resource required; unknown fields anywhere are errors.
  • Exactly one of image / build per resource.
  • branch xor tag; commit requires one of them.
  • Port numbers 1–65535; every port named; max 20 ports, 50 resources, 50 volumes; file size max 1 MiB.
  • Volume mounts must reference declared volumes; depends_on must reference resources in the file; no self-dependency.
  • Every {{ ref }} checked against the source’s real outputs; self refs whole-value only; one source resource per env value; addon refs bare-name only.
  • Secrets and addons are validated for existence when the stack is resolved server-side.

Example stackfiles

These live in pkg/stackfile/testdata/ and double as test fixtures — every one is validated against the schema and round-tripped through the transpiler on each test run.

Shared database, owner + superuser (superuser_migration.yaml)

One postgres addon, two consumers: the app on owner credentials, a migration Job on superuser credentials.

Everything at once (kitchen_sink.yaml)

Secrets, addon templating, cross-resource refs, self refs, a stateful cache with a volume:
Other fixtures: basic_image.yaml (minimal), build_from_source.yaml (git build), with_secrets.yaml, with_addon.yaml, with_addon_superuser.yaml, infisical.yaml (real-world multi-resource app), simple_nginx.yaml.

JSON Schema

Source of truth: pkg/stackfile/schema.json, embedded in the server binary. Point your editor at it (# yaml-language-server: $schema=...) for autocomplete and inline validation.

Not expressible in a stackfile

These exist in the Stack API but have no stackfile syntax (the exporter fails loudly on them rather than dropping them):
  • Init containers, restart triggers, labels/annotations.
  • Private image pull credentials, git integration selection, push targets.
  • Building an image from a volume; volume sources (git repo / remote dir / build artifact), storage class, sub-path or read-only mounts.
  • Mounting secrets or outputs as files (everything is env).
  • Addon provisioning (version, storage, backups, instances) — stackfiles connect to addons, they never create them.