appnz.yaml: the runtime contract
Why app.nz separates static and server runtimes with a small config file instead of guessing forever from package.json, Cargo.toml, dist folders, or Dockerfiles.
Listen to this article
On-device voiceUses the voice built into your browser; no article text leaves this page.
Audio narration is not supported by this browser.
appnz.yaml is the small file that keeps app.nz deploys from becoming guesswork. It tells the platform what kind of artifact this project produces and how the platform should run it.
The important field is runtime.
Static versus server
The current runtime split is intentionally blunt:
| Runtime | Meaning |
|---|---|
| static | build output is files |
| server | build output is a container image |
Static deploys go through hosted sites. Server deploys go through the build and container path. That distinction prevents the platform from treating every project like a web server.
Most frontends are static after build. React, Vite, SvelteKit static exports, docs sites, WASM frontends, and many agent-generated apps all fit this path. They should deploy as files.
APIs, long-running workers, websocket servers, and services with custom runtimes fit the server path. They need an image and a process.
Why a contract matters
Without appnz.yaml, a platform has to infer intent from files:
- package.json means Node, maybe.
- Cargo.toml means Rust, maybe static WASM or maybe an API.
- Dockerfile means server, unless it is only a build container.
- dist means static, unless it is stale.
Inference is convenient for demos, but painful in production because ambiguous guesses become outages. The config file makes the user's intent explicit.
Defaults are allowed, but not sacred
app.nz can infer a simple Dockerfile for common Node projects because that covers a large number of generated apps. But the default Dockerfile is a starting point, not a platform law.
Rust server apps should usually provide their own Dockerfile. GPU images must provide their own image and dependency story. Python apps with system packages should not rely on a generic inferred container.
Good defaults get a demo live. Explicit config gets a production app repeatable.
Build and release separation
The config file also creates a clean split:
source + appnz.yaml -> build artifact build artifact -> release release -> routing
Static artifacts are directories. Server artifacts are image refs. The control plane can reason about both without reading the repository again.
That separation is what makes fast rollback possible. If the platform stores old static file indexes and old image refs, rollback is not "run the old build again." It is "make the old artifact current."
The future fields
The next useful fields are not complicated:
- build command,
- output directory,
- Dockerfile path,
- health check path,
- port,
- runtime environment,
- cache policy,
- idle timeout,
- CPU/GPU machine hint.
The danger is turning appnz.yaml into Kubernetes. The file should describe the app's contract with app.nz, not force users to learn the platform's internal scheduler.
The principle
The config is small because the product should be opinionated. If app.nz knows the runtime class, output, and health check, it can choose the boring path. If it cannot, the user should say so explicitly rather than hoping a heuristic guessed right.