The tabbify.toml manifest
tabbify.toml declares how an application builds and runs. It sits at the root
of your app directory next to your Dockerfile. The wire struct is vendored identically across
tcli, the supervisor, and the
node, so the fields mean the same thing everywhere they are read.
There is one public runtime: an OCI image built from your Dockerfile and booted
as a CPU-only Firecracker microVM. Runtime configuration
sets lifecycle, port, CPU, and memory; it does not select a different execution
engine.
tabbify.toml is the unified manifest read by every component. A legacy
manifest.toml continues to parse for backward compatibility — see
Gotchas for precedence.
A minimal manifest
The smallest manifest declares a name and a build kind:
[app]
name = "hello"
[build]
kind = "docker" # always "docker" — builds your Dockerfile into an OCI image
Identity: [app]
[app] carries the app's identity. Write name and optionally description.
You may pin an ID; otherwise the deploy client derives or mints one for the
application.
[app]
id = "0191e7c2-1111-7222-8333-444455556666" # your app's permanent address
name = "booking-site"
description = "Apartami apartment search API"
Use a valid UUID accepted by the current API, or let tcli manage it. The UUID
deterministically derives the app's private mesh address and public URL - see
Private Mesh and Routing.
Build: [build]
[build].kind is docker: the build produces an OCI image from your
Dockerfile.
[build]
kind = "docker" # always "docker" — builds an OCI image
context = "."
dockerfile = "Dockerfile"
builder = "thinkpad" # optional: which supervisor compiles the image
builder optionally names the supervisor that compiles the image; leave it unset
and Tabbify picks a capable builder for you.
Runtime, ports, and lifecycle: [runtime]
[runtime] carries only lifecycle and resource limits — not a runtime choice.
Every app runs as a Firecracker microVM; there is nothing to select.
[runtime]
lifecycle = "on_request" # always_on | on_request
idle_timeout_sec = 300
memory_mb = 512
vcpus = 1
-
lifecycle —
always_onstarts at deployment and avoids idle shutdown;on_requestlazy-spawns on first request and idle-stops afteridle_timeout_sec. An app started explicitly via the API is pinned and is never idle-reaped. -
port — the guest port your app listens on inside the microVM. A runner binds the app on
[app_ula]:8730over the mesh and the node proxies/app/<uuid>to it; the runner forwards that mesh side to your app's port. It defaults to8080, so an image that listens on 8080 needs nothing. If your app listens elsewhere, declare it — otherwise the readiness probe hits the wrong port and the app crash-loops:[runtime] port = 3000
Workload target: [[deploy]]
The current public deployment model selects one workload target. Omit [[deploy]]
to let Tabbify choose the default eligible supervisor, or include one block to
request a specific supervisor and add target-local environment values:
[[deploy]]
supervisor = "thinkpad"
[deploy.env]
LOG_LEVEL = "debug"
Use at most one [[deploy]] entry for a public deployment. Repeated entries do
not create replicas, a highly available group, or a placement policy. They are
outside the supported public contract and must not be used to model multiple
copies of one application UUID.
Environment and secrets
tabbify.toml does not carry plaintext secrets. A credential lives in the
encrypted per-network store (tcli secret set NAME, value via stdin) and the
manifest references it by name — the node rewrites each secret:NAME into the
real value at deploy time, so the plaintext never touches the manifest, the event
log, or the prompt:
[env]
STRIPE_KEY = "secret:STRIPE_KEY" # resolved from the store at deploy
Non-secret values can be placed in [env]. For credential commands and scoping,
see the secret CLI.
Gotchas
tabbify.tomltakes precedence over a legacymanifest.tomlif both exist.- Unknown fields may be silently ignored (
#[serde(default)], nodeny_unknown_fields). That's how old manifests stay compatible — but it also means a typo likelifcyclefalls back to the default rather than erroring. Watch your spelling. - Runtime-selection keys found in older manifests are inert. New manifests should omit them; every public workload runs in Firecracker.
Once your manifest is ready, deploy it. The simplest path is a git push — put
the repo on GitHub with the deploy workflow and Tabbify clones, builds, and runs
it for you; see the quickstart. To deploy from your own
terminal instead, run tcli deploy --remote, which hands your repo and commit to
the node to build and run remotely; see the CLI reference.