Skip to content

Storage classes and dedicated placement

Most workloads need nothing special from the disk under them. They write the occasional file, read it back later, and can move from one node to another whenever the cluster needs them to.

Other workloads depend on the disk behaving in a precise way: writes that land in order, locks that are honoured, and a save that is really on disk when the kernel says it is. Databases are the usual example, but any workload that relies on real filesystem semantics belongs in this group. Shared storage built on a network filesystem cannot always guarantee those semantics, and the result ranges from surprising performance to quietly losing the last seconds of writes.

Flui ships two storage classes, one for each group, and lets the application pick.

Shared the default: one volume, every node reaches it shared volume on the master master sees it ✓ worker app sees it ✓ worker sees it ✓ Dedicated a node's own disk, for databases master worker database its disk pinned here worker
Shared storage is reached from every node, so an application can start again anywhere; a dedicated disk pins its application to one node.

Two storage classes

Every persistent volume on a Flui cluster ends up in one of two classes:

  • Shared. The default class. A single volume on the master is exposed to every node as a network filesystem, with a local cache on each node. An application on any node reads and writes through the same address, so if its node is lost it starts again elsewhere with its data. Use it for stateless apps, log directories and any workload that occasionally writes a file and reads it later.
  • Dedicated. The node-local class, backed by a node’s own disk. Writes go straight to that disk with no network hop, and the application that uses it is pinned to the node hosting the disk. Once placed, it does not move. Use it for databases and anything that depends on the disk behaving like a real disk.

The application makes the choice: a catalog manifest declares it with persistence.scope: dedicated, and Flui does the rest. An application that declares nothing gets shared placement.

A new shared volume becomes usable from every node only after Flui has proven that every node sees the shared storage. Every ten minutes it writes a fresh token on the master and reads it back on each other node. While any node fails that check, new volumes stay on the node that created them, and a volume keeps the node it was created on. flui app status names any volume of an application that is tied to one node.

Where dedicated workloads live

When an application asks for dedicated placement, Flui decides which node hosts its disk. By default this is a worker node, not the master. At deploy time Flui looks at every worker’s free capacity and pins the app to the one with the most room. It records that choice as the app’s dedicated node name, so later operations know where its disk lives. Keeping dedicated workloads off the master keeps stateful, node-bound workloads away from the control plane and lets their hosts grow independently.

This default needs a worker to exist. If a cluster has no worker, the result depends on the cluster. A workload cluster places the app on its master, since there is no Flui control plane there to protect. The control cluster, which runs Flui itself, does not fall back: it fails with the code NO_WORKER_FOR_DEDICATED_APP and tells you to add a worker (or to use the escape hatch below).

The master escape hatch

For small cases, such as a single-node cluster or a choice to keep one dedicated app on the control plane, an app carries an allowMasterPlacement flag (a boolean, default false). It exists on the application itself and on the catalog-install input, so you can set it when installing or later. From the CLI it is --allow-master. When it is set, a dedicated app may run on the master even though no worker is available. When it is not set, a control cluster with no worker fails as described above. By default, dedicated work goes to a worker unless you say otherwise.

A locked node

A node hosting at least one dedicated workload is locked: the node-removal flow refuses to delete it while those workloads live there, and lists which applications would lose their disk if it went away.

To unlock the node, move the dedicated workloads off it (to another worker, or to the master through allowMasterPlacement), and the node-removal flow then accepts. The platform does not move dedicated data between nodes automatically. That step is left to the operator, who knows whether a downtime window is acceptable and whether a backup and restore is the right way to move the data.

A pre-flight check before deploy

Before a dedicated workload is deployed (or redeployed after a configuration change), Flui checks the target node: it asks the cluster how much CPU and memory the node has free and compares that with what the workload needs. If the node cannot fit the workload, the deploy fails immediately with an error that shows the gap and the ways to close it: resize the node, or move the workload to one with room.

The check runs at every deploy, not only at install. Raising the resource request of a dedicated workload either succeeds because the room is there, or fails with a useful diagnosis instead of leaving the workload stuck in a degraded state.

Vertical scaling: giving the host more

Once a workload is pinned, the only way to give it more CPU or memory is to give the host node more. A dedicated workload cannot add another replica, because there is only one disk it can use, so you grow the machine underneath it.

Resizing a node is planned maintenance. The cluster powers the node down, asks the provider to change the server type, powers it back up, and waits for the cluster to report it healthy before the operation completes. The workload is unavailable for a few minutes, which cannot be avoided on the providers Flui integrates with, and the operation’s plan shows that window before anything happens. The command reference for resizing is in CLI: Environments.

Growing the shared storage

The shared storage also fills up over time as the apps using it accumulate data. Flui has an operation to grow it: the volume at the provider is resized, the filesystem on it is extended, and the new space is immediately visible to every node that mounts it. The operation runs online, with no downtime and no application restarts, but it cannot be reversed: the providers Flui integrates with let volumes grow but not shrink. Plan the target size, because the only way back to a smaller volume is a fresh cluster.

When an application goes away

A volume that outlives its application keeps costing money, even though nothing is running and nothing looks broken.

So removing an application removes its volumes too. The same applies to a catalog install with several parts: uninstalling it removes the storage of every part, not only the one you named.

Because this cannot be undone, every surface tells you what will be lost before it asks. flui app delete lists each volume with its size, then asks you to type the application’s name to confirm. The dashboard’s delete dialog shows the same list. An agent can read the same removal preview before it proposes anything.

All three read the same answer from the API, so they always agree, and the removal deletes exactly what that answer named.

When Flui cannot reach the cluster to read the volumes, it reports the loss as unknown, never as “none”.

Storage nobody is using

A cluster can still hold volumes no application uses, left by a removal that could not finish or created outside Flui. Nothing else lists them, so nobody knows they are being paid for.

flui cluster volumes lists them. It reports each abandoned volume with its size, the application it belonged to when that is still known, and a sentence explaining why it is considered abandoned. The dashboard shows the same list on the cluster’s Storage tab.

To remove one, run flui cluster volumes --remove <ref> with the reference the listing prints next to it. Removal is permanent, so it asks you to type the volume’s name first. The server then runs the whole check again before it deletes anything, because the list you read may be minutes old and something may have started using that volume since.

An agent can read this list but cannot act on it. No agent permission deletes a volume; an agent can only tell you what it found and give you the command.

How the scan decides a volume is abandoned

The two possible mistakes are not equal. Missing a volume leaves you paying for a disk. Naming the wrong one offers to delete somebody’s data. So the scan is built to miss volumes rather than name the wrong one.

A volume is called abandoned only if all of these hold: no running workload uses it, no application that still exists owns it, it is not in a location the platform reserves for itself, and it has existed for at least fifteen minutes, so it cannot be something still being created.

It also needs a positive reason, and there are four: Flui’s own mark on it names an application that is gone; its name matches an application Flui removed; its name could only have been made for a workload that no longer exists; or every application that ever lived beside it has been deleted.

A volume with none of those (no mark from Flui, a name that matches nothing, next to applications that are still alive) is never reported. A volume created outside Flui, with no mark from it, can fall into that gap. So an empty list means nothing was found by these rules, not that nothing is left. The same goes for a cluster that cannot be read at all: the listing says so instead of showing a false zero.

There is a second, coarser kind of leftover, at the provider instead of inside a cluster: a block volume that survived the destruction of its cluster. flui env orphan-volumes lists those, and --cleanup removes them.

What Flui does not do here

Moving a dedicated workload from one host to another does not migrate its data automatically. Flui changes the placement, but the disk does not follow. You have to stop the workload, copy its data to the new host yourself (usually a backup followed by a restore on the target), and redeploy it with the new placement. This is the main caveat. It does not affect the common path: install a database, later resize its host, and optionally grow the shared volume.

Where this chapter goes from here

  • The app concept: the application surface that declares storage, exposure and placement, and the home of the install-time capacity gate.
  • Nodes and cluster topology: the nodes a workload can be pinned to.
  • The database console: the first-party tooling for the databases that most often want dedicated, node-local storage.
  • CLI: Environments: the command-by-command reference for resizing nodes and growing the shared volume.
  • CLI: Applications: removing an application, and finding the storage of applications that no longer exist.