Declarative Stacks
Managing servers imperatively (e.g., typing super start commands manually) works for small setups, but it leads to Configuration Drift at scale. You forget which arguments you used last week, or someone else changes a setting without documenting it.
Super supports a Declarative approach, similar to kubectl apply or docker-compose up. You define the desired state of the entire system, and Super converges to that state.
The Concept: “Apply”
Instead of managing individual processes, you define a Stack (a list of all services) in a file. Stack files are TOML by default (.toml or no extension); legacy JSON stacks (.json) keep working and can be mixed in the same [include] glob.
When you run super apply stack.toml, Super will:
- Create new services that are in the file but not in the system.
- Update existing services if their configuration (args, env, etc.) has changed.
- Prune (Remove) services that are running in the system but missing from the file — only if
prune = true.
Caution
prune defaults to false. Omit it or set prune = false for normal day-to-day stacks. Pruning runs only when the stack file (or API body) explicitly sets prune = true. With prune enabled, any managed program not listed in that file is stopped and removed from Super — a typo or empty services list can wipe your entire fleet in one apply. Removals are irreversible from Super’s perspective. Both super apply and the Dashboard Stack Editor print a full apply diff (keep/update · create · every REMOVE) and require typing confirmed before removals run (y/--yes/a normal Confirm click are not enough; CLI automation uses --force-prune after --dry-run). On daemon start / super reload via [include], the same prune runs without a prompt — keep prune = false in include files. super check warns when an include stack sets prune = true.
One TOML note: structured values such as
health_checkorartifactcan be written either as an inline table —health_check = { type = "tcp", port = 8080 }— or as a nested table ([services.health_check]/[services.artifact]). Only[[array.of.tables]]syntax is rejected for tagged values. Thetypefield selects the probe kind (tcp/http/exec). Per-program OTA fields (source,checksum,destination,extract,restart_policy,download_timeout,verify_timeout): Config reference —artifactand Atomic OTA Updates.
Workflow Example
1. Define the Stack
Create a file named prod-stack.toml. This can be generated by your CI/CD system or managed in Git.
# prod-stack.toml
prune = false
[[services]]
name = "redis-cache"
command = "/usr/bin/redis-server"
autostart = true
[[services]]
name = "backend-api"
command = "/usr/local/bin/api"
env = { PORT = "8080" }
depends_on = ["redis-cache"]
health_check = { type = "tcp", port = 8080 }2. Apply the Stack
Run the apply command via the CLI.
$ super apply prod-stack.toml
Applying stack from "prod-stack.toml"...
- Creating service: redis-cache
- Updating service: backend-api
- Pruning service: old-worker (not in stack)
Stack applied successfully.JSON Stacks (Legacy)
JSON stack files remain fully supported — useful when your stack is generated by tooling rather than edited by hand. The API accepts JSON bodies (POST /api/v1/stack), and super export still emits JSON. Example file prod-stack.json:
{
"prune": false,
"services": [
{
"name": "redis-cache",
"command": "/usr/bin/redis-server",
"autostart": true
}
]
}Format is chosen by file extension: .json → JSON, everything else → TOML.
CI/CD Integration
This feature makes Super ideal for GitOps.
Commit your stack to a Git repository — either
stack.toml(default) orstack.json(legacy), both are accepted by the API.Configure a GitHub Action / GitLab Runner.
On push to
main, the runner executes:TOML (use
--data-binary— plain-dstrips newlines and breaks multi-line TOML):curl -X PUT http://prod-server:9002/api/v1/stack \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/toml" \ --data-binary @stack.tomlJSON (no special header needed — JSON is the default body type;
-dworks fine):curl -X PUT http://prod-server:9002/api/v1/stack \ -H "Authorization: Bearer $TOKEN" \ -d @stack.jsonOr use the CLI against the daemon (same files, no curl headers):
super apply stack.toml # or super apply stack.json
Exporting Current State
If you have manually configured a server and want to capture its state into a file, you can use the export command. TOML is the default output — the same format super apply / [include] expect:
$ super export > current-stack.tomlThis generates a valid TOML stack file representing the currently running system, which you can then check into version control.
To emit the legacy JSON shape instead (for tooling that expects it), pass --format json:
$ super export --format json > current-stack.jsonEither output round-trips: super apply current-stack.toml (or dropping it into conf/conf.d/) re-creates the same programs.
Snapshot vs. Stack
Besides stacks, Super also persists every program’s runtime state to data/snapshot.json (with automatic .bak rotation). Stack files are the desired state; the snapshot is the runtime truth — so for same-host disaster rollback, restoring snapshot.json directly is byte-exact, while cross-host migration usually goes through export/apply. For the full comparison and how [include] stacks interact with both, see Snapshot Persistence & Restore.
Was this page helpful? Thanks for your feedback!
Still have questions? Open an issue or browse the source.