The app concept
In Flui, “app” is a first-class concept. It is the unit you
deploy, observe, scale and retire — the object every CLI command
under flui app * and every Apps page in the dashboard operates
on. An app is not the same as a running process or a single
piece of cluster infrastructure: it is the bundle of name and
slug, source, runtime configuration, endpoints, certificates,
builds, releases, logs and metrics that together describe one
deployable thing and everything that surrounds it during its
life on the cluster.
This chapter is the conceptual definition. The manifest that describes an app for Flui has its own chapter (The flui.yaml manifest), and the distinction between Application and CatalogApp has its own chapter too (Catalog vs application).
What an app carries
An app, regardless of where it comes from, carries the same set of things:
- A name and a slug. A stable slug unique within the installation and a human-friendly name. The slug is what every other surface refers to.
- A cluster. Every app lives on one cluster — the cluster whose dashboard you opened, or the one you pointed the CLI at. Moving an app between clusters is not a single in-place operation; it is a redeploy on the other cluster.
- A source. Where the workload comes from. Today there are
three: a container image pulled from a registry, a source
repository that Flui builds for you against a
flui.yamlmanifest, or an entry picked from the Flui catalog of ready-made building blocks. The source decides what happens before the workload runs; the rest of the app shape is the same regardless of which one it is. - A runtime configuration. Replica count and scaling limits, healthcheck, ports, volumes — the operational shape of the workload once it is up.
- An endpoint, or none at all. An app can declare that it should be reachable from outside the cluster, in which case the deploy flow gives it a hostname and a TLS certificate to match. An app that does not need to be reached — a background worker, a job — runs without one. The endpoint itself is its own concept, covered in DNS as part of the deploy contract.
- A release history. Every deploy and every rollback is recorded as a new release entry, append-only. The current release is a pointer into that history.
The sections below take the points that need more than a line — resources, configuration and secrets, observability, automatic behaviours — and give each one a paragraph of its own.
Resources: CPU and memory
Every app declares two numbers per instance: the request — what the cluster reserves and guarantees — and the limit — the ceiling the instance cannot go past. The scheduler places an instance only if the node can honour the request; the runtime enforces the limit at peak.
The dashboard shows both numbers and the current usage side by side, and flags the app visually when the request is much higher than what the workload uses (over-allocated, paying for capacity that sits idle) or when the usage runs close to the limit (under-allocated, one bad minute away from being killed). The operator sees these signals without having to go looking for them.
There is also an automatic safety net for the very first launch of an app: if the workload runs out of memory in its early minutes, before its steady-state behaviour is even known, Flui can nudge the allocation upward on its own so the app gets a chance to start. The behaviour is meant for the bootstrap window, not for routine operation.
Volumes and storage
An app that needs to keep data across restarts declares one or more volumes — a name, a mount path inside the workload, a size. Where the data physically lives — the cluster’s shared storage, where any node can reach it and the app can be moved freely, or a node’s local disk, where access is faster but the app is pinned to that node — is decided by the cluster’s storage configuration. That choice carries implications for placement and for what happens during cluster operations like a node resize. The full story is in Storage classes and dedicated placement.
Configuration and secrets
Plain configuration reaches the workload as environment variables and lives with the app — the dashboard shows both names and values. Secrets follow the same name-value shape but are stored encrypted and write-only in the user interface: the dashboard shows the names so the operator knows what is set, but the values are masked. A secret can be overwritten with a new value at any time; it cannot be read back once stored.
Both are editable from the dashboard and the CLI without a fresh build or a re-deploy — the platform applies the change through a rolling restart, and the new value is in effect once the restart completes.
How an app is classified
The dashboard groups apps along two orthogonal axes:
- Category. Two values: system and user. System apps are the components of the platform itself — the API, the dashboard, the identity provider, the database, the observability stack — installed and reconciled by Flui as part of the control cluster. User apps are everything you deploy. Both kinds live in the same table and are operated through the same surfaces; the difference is that system apps are protected from accidental deletion (a deletion command refuses unless forced).
- Kind. A coarser grouping by purpose: database, tool, application, system. Used by the dashboard to organise the Apps page in a way that matches how operators think about what is running. It does not change the lifecycle.
These two are distinct from the manifest’s own kind: field
(Application vs CatalogApp), which describes where the app
comes from, not what it does. See
Catalog vs application for
that side.
The lifecycle
An app sits in one of a handful of steady states:
- Awaiting build — for apps built from source, while the build is in flight and the platform is waiting for an image.
- Running — the app is up and serving its work.
- Degraded — the app is up but not at full health (some replicas down, a healthcheck failing intermittently).
- Stopped — scaled to zero on purpose; the configuration is still there, no instances are running.
- Failed — a terminal failure (the last build failed, the last deploy failed, an unrecoverable condition was hit).
- Deleted — the app has been torn down; the row remains for audit.
During an operation — a deploy, a rollback, a tear-down — the app briefly passes through transient states that the dashboard surfaces as progress indicators rather than steady values.
Operations
Every operation an operator can run on an app is available on both surfaces, with the same semantics:
- Deploy / redeploy. Apply (or re-apply) the app’s configuration. For image-based apps a redeploy can also pin a different image version; for source-built apps a redeploy reuses an existing build instead of building again. Every deploy appends a new release entry.
- Rollback. Move the current release pointer back to a previous entry. Importantly, this adds a new release entry (marked as a rollback) rather than rewinding history — the trail of what happened stays intact.
- Start / stop. Bring the app’s replicas back from zero, or scale them to zero while keeping the configuration in place. Stopping is reversible without re-deploying.
- Restart. Replace the running instances without changing the release. See the rolling-restart section below for the shape of this operation.
- Scale. Set the replica count explicitly within the limits the manifest declares.
- Delete. Remove the app’s resources from the cluster, free its endpoint and certificate, and soft-delete the row. System apps require an explicit force flag for this operation.
Rolling restart
When the running instances of an app have to be replaced — to pick up new configuration or new secrets, or just to clear a wedged state — Flui rolls them: new instances come up first, and only when they are healthy do the old ones go away. The replica count never drops below the declared number, so the app keeps serving throughout. The platform also triggers a rolling restart on its own whenever a change it can apply in place needs to reach the running instances.
Logs and metrics
Every app has the same observability surfaces from day one, on both the dashboard and the CLI:
- Logs. Streamed from every instance, queryable by level and free-text search.
- Metrics. CPU, memory, network and replica counts over time — the baseline Flui collects for every app it manages, with no setup on the app side.
- Live status. Replicas ready, current release, health at a glance.
- Crashes. A summarised view of recent crashes: exit codes, out-of-memory events, restart counts, last log lines before the crash.
All four read from the same central backend on the control cluster — the position observability has in the platform is covered in Observability is not an afterthought.
Going deeper when something is wrong
For the cases where the everyday views are not enough — a first release that won’t start, a sudden burst of crashes, a configuration that subtly breaks the workload — the dashboard groups two deeper tools under an Advanced menu on every app:
- Diagnoses. An automated read of recent failures that goes beyond the raw crash list: it correlates exit codes, out-of-memory events, healthcheck failures and the log lines around them, and surfaces a probable cause and a suggested remediation (a missing environment variable to add, a memory limit to raise). Unresolved diagnoses are also shown as a banner on the app’s overview, so they cannot be missed.
- Debug pods. An on-demand snapshot of every running instance of the app, with the cluster-level details an operator needs when the higher-level views fall short. Refreshed on request rather than continuously, so the cluster is not paying for a constant detailed read of every app.
These surfaces are designed for the moments when the everyday ones — logs, metrics, status, crashes — have stopped being enough. They are not part of the routine flow.
Autoscaling, on the roadmap
Automatic scaling on load — adjusting the replica count within the manifest’s min/max range from the app’s own metrics — is the next step. The pieces are in place (every app already declares the range and produces the metrics), the loop that closes them is not yet in production. Today the replica count is operator-driven.
System apps are apps
A specific point worth being explicit about: the platform’s own components — the API, the dashboard, the identity provider, the database, the observability stack — are themselves apps in this model. They appear in the app list, they have releases, they expose logs and metrics, they go through the same lifecycle as user apps. What sets them apart is the system category and the protection it carries: their delete operation refuses unless the operator explicitly forces it. They are not special-cased in any other way.
This homogeneity matters. Operators do not learn one set of commands for “Flui’s own bits” and another for “the apps I deployed”. The way you read the dashboard’s logs is the way you read your app’s logs.
Where this chapter goes from here
- The flui.yaml manifest — the declarative shape that produces an app.
- Catalog vs application — the two kinds of manifest that produce apps, and what distinguishes them.
- Observability is not an afterthought — the surfaces that read from the cluster’s metrics and logs for every app you run.