Skip to main content

Outcome

Keep application data across restarts and redeployments by declaring a persistent volume at the exact data directory the service uses. The deployed release is healthy, and Stackdome reports the expected volume name, size, access mode, and provisioning phase. Object stores solve a different outcome: they hold PostgreSQL addon backups. Registering an object store is currently dashboard/API-only; there is no public stackdome object-store command and object stores are not Stackfile mounts.

Prerequisites

  • A repository with a stackfile.yaml and evidence of the directory the application writes to.
  • The URL of Stackdome Cloud or a self-hosted installation, plus an API token. See authentication and API tokens.
  • A size chosen from the application’s current data and expected growth. Volume size and access mode cannot be changed after creation in the current platform workflow.
  • For an object store, an existing Generic secret containing provider credentials and access to the Stackdome dashboard or API.

Agent prompt

Agent actions

1

Find the persistent data directory

Inspect image documentation, Dockerfile declarations, Compose volumes, application configuration, and existing deployment files. Distinguish durable application data from caches and temporary files.
2

Declare the volume and mount

Add a named entry under top-level volumes, then mount that name under the resource’s volumes array at an absolute container path. Choose the size and access mode before deployment.
3

Validate and deploy

Run stackdome validate, confirm the active scope with stackdome whoami -o json, run stackdome deploy --wait -o json, and retain its non-empty release.id as the deployed release ID.
4

Verify persistence resources

Poll stackdome volume list --stack <stack-name> -o json for at most five minutes. Select the volume by name and require status.phase to equal Ready; Pending is not success. Then run stackdome status -o json and require the converged release ID to equal the deployed release ID, its state to be Released, and its health to be ok.
5

Test the application behavior

Use an application-specific, reversible write/read check when available. Restart only the affected resource with stackdome restart <resource> if the command is appropriate for the service, then confirm the data remains.

Success criteria

  • stackdome validate exits successfully with the top-level volume and resource mount present.
  • stackdome deploy --wait -o json exits successfully, returns a non-empty release.id that is retained as the deployed release ID, and returns release.state as Released.
  • stackdome status -o json returns converged_release.id equal to the deployed release ID, converged_release.state as Released, and converged_release.health as ok.
  • stackdome volume list --stack <stack-name> -o json contains the expected name, spec.size, and spec.access_mode, and that selected volume has status.phase equal to Ready.
  • An application-specific read after restart returns the data written before restart, when a safe persistence check is available.

CLI and Stackfile workflow

Declare storage and its mount together in the Stackfile:
path must be an absolute path inside the container. size is required, and access_mode defaults to ReadWriteOnce when omitted; set it explicitly for reviewable agent changes. The name in the resource mount must match a top-level volume name.
Wait for the selected volume, not merely the first array entry. Use this agent-readable bounded procedure; it needs no additional JSON command-line tool:
  1. Make at most 30 attempts over five minutes, waiting ten seconds only after a confirmed Pending result.
  2. On each attempt, run stackdome volume list --stack <stack-name> -o json.
  3. If the command exits nonzero, stop immediately. Preserve its stderr as the diagnostic; do not classify an authentication, authorization, server, or network error as storage provisioning.
  4. Parse stdout as a JSON array. If it is not valid JSON or is not an array, stop immediately and report an invalid structured-response error.
  5. Select the single element whose name exactly equals the declared volume name. If it is missing, stop immediately and report the stack name and missing volume; do not keep polling the wrong target.
  6. Require that selected object’s declared spec.size and spec.access_mode. If either differs, stop immediately and report the mismatch.
  7. Read that selected object’s status.phase:
    • Ready passes the volume gate.
    • Pending permits the next attempt after ten seconds, unless this was attempt 30.
    • A missing or any other phase is an immediate diagnostic error; do not treat it as still provisioning.
After 30 successful responses that all selected the expected volume in Pending, the wait has timed out. Do not report success and do not delete the volume. Run only the verified volume and stack-status diagnostics:
Inspect the selected volume’s status.conditions for its reason and message, and inspect stack conditions for a workload waiting on storage. On self-hosted Stackdome, correct the default storage class or available capacity; on Stackdome Cloud, route the unresolved provisioning condition to support. If the user chooses to back out a new mount while storage is repaired, remove only that mount from the Stackfile, validate, and redeploy. Delete and recreate a newly created empty volume only after explicit approval and after the provisioning cause is corrected. The CLI also exposes stackdome volume create, list, and delete. volume create provisions a volume but does not declare the application mount; for a reproducible application change, keep the definition and mount in stackfile.yaml and deploy them together. Object-store registration and editing remain dashboard/API-only. Once an object store is configured on a PostgreSQL addon, the CLI can trigger and list addon backups as described in Postgres.

Dashboard workflow

Two kinds of storage share this page, and they have almost nothing to do with each other.A volume is a disk. It belongs to a stack, and one or more resources in that stack mount it at a mount path inside the container. What you write there survives a restart and a redeploy.An object store is a bucket somewhere else — S3, Azure, GCS. Nothing mounts one. Its only job today is to be the destination for Postgres addon backups.

Dashboard prerequisites

  • A stack with at least one resource. A volume is created already attached to a resource, so the Volume tile stays disabled until the stack has one.
  • On self-hosted Stackdome, a cluster whose default storage class can provision disks. Stackdome never asks you which storage class to use for a volume — there is no field in the dialog, and the API sends none — so your cluster’s default StorageClass is what provisions it. Check what that is before you start:
  • A Secret holding the bucket credentials, if you are registering an object store. The form only ever references a Secret; you never type a key into it. See Create the secret.

A volume belongs to the stack, not to a resource

This is the part that surprises people. A volume is a stack-level object with its own name, size and lifecycle. Resources reference it by name. That is why a stack card counts the two separately — 3 resources · 2 volumes — and why deleting a resource does not delete the disk it was using.Its spec is also fixed the moment it is created. Name, size, access mode and storage class are all write-once: there is no update endpoint for a volume, only create, read and delete. Plan the size before you create it.

Declare a volume

1

Open the Architecture tab

Open the stack and stay on the canvas.
2

Choose + Add resource → Volume

The panel lists it under the Storage heading as Volume“persistent storage”. If the tile is greyed out, the stack has no resource yet.
3

Fill in the dialog

Four fields, described below.
4

Deploy

The volume itself is provisioned as soon as the editor saves. The mount only reaches the cluster on your next deploy.
The Add volume dialog with a Name field prefilled volume, a Size field prefilled 1Gi and hinted e.g., 1Gi, 500Mi., a required select labelled Attach to service, and a required Mount path field placeholdered slash var slash lib slash dataThe dialog is titled Add volume:There is no storage-class field, no access-mode field, and no source field. Every volume the dashboard creates is an empty disk.
Creating the volume and mounting it are one gesture, but two things. The dialog writes a stack-level volume and a mount on the resource you picked. The volume is created on the server as soon as the editor autosaves, and provisioning starts immediately — you do not have to deploy for the disk to exist. The mount, like every other spec change, only reaches the cluster when you deploy. The pill above the canvas counts what has not been released yet — Apply 1 change, next to Deploy.

Where a volume shows up

A volume that some resource mounts appears as a chip along the bottom of that resource’s node, showing the volume’s name; hover it for the mount path. A volume nothing mounts appears instead as its own node, badged VOLUME, floating on the canvas.The Architecture canvas showing three resource nodes — web at the top, with postgres and redis below it — where the postgres and redis cards each carry a volume chip along the bottom edge reading pgdata and redis-dataClick a chip to open the drawer for that volume.The volume drawer for pgdata, headed 1Gi ReadWriteOnce, with a disabled Name field marked rename from volumes list, a disabled Size field marked fixed once provisioned, an Access mode row reading ReadWriteOnce parenthesis RWO with the note single resource read slash write, and a MOUNTS section listing postgres at slash var slash lib slash postgresql slash dataThe header repeats the size and access mode. Under Specification, Name and Size are both disabled — Size is annotated “fixed once provisioned” — and Access mode is a value, not a control. Mounts lists every resource that mounts this volume, and the path each one mounts it at.Right-clicking a chip offers Disconnect volume, Volume settings and Delete volume. Right-clicking a floating volume node offers an attach entry instead of the disconnect one, which asks for a resource and an absolute mount path.

Mounts, seen from the resource

Open a resource’s drawer and its Configuration tab has a Mounts section, annotated “managed on canvas”. It is deliberately read-only there: it shows the volume, the path, and a shortcut arrow to the volume’s own settings.The Mounts section of the postgres resource drawer, headed MOUNTS with the note 1 volume managed on canvas, listing pgdata mounted at slash var slash lib slash postgresql slash data and the hint drag a volume onto the node to attachWith nothing mounted it says so, and points you back at + Add resource → Volume. To attach an existing volume to a resource, drag the volume onto that resource’s node.

Access modes

Every volume Stackdome creates is ReadWriteOnce. The drawer shows it as ReadWriteOnce (RWO) and offers no alternative; the dialog has no access-mode field. The REST contract’s Volume schema does define three values — ReadWriteOnce, ReadWriteMany and ReadOnlyMany — but the dashboard only ever sends the first, and the field cannot be changed after creation.What that means in practice: one node can mount the volume, and only that node. Two resources both scheduled onto that node can share it. A resource whose replicas land on different nodes cannot — the replicas that land elsewhere will not start, because the disk is not available to them.
This bites when you add a second node, not when you add a second replica. On a single-node install every pod lands on the same node, so a shared volume and a scaled-up resource both appear to work. Add a node to the cluster and the same stack can start failing to schedule, with no change on your side. If a resource needs storage and more than one replica, give each replica its own volume, or keep the resource at one replica.

Changing a volume on a running stack

Deleting a volume a live release still mounts will fail that release. A release is pinned to the spec it was cut from, so it keeps referencing the volume by name. Stackdome checks that every volume a release references still exists before it applies anything, and fails the release outright with release references volume(s) that no longer exist: <name> rather than leaving the cluster half-applied. Deploy again once the stack no longer mounts the deleted volume.

Object stores

An object store is a registered backup destination. Nothing in a stack mounts it, and it is not a substitute for a volume — the only thing that reads one today is the Postgres addon, for scheduled backups, WAL archiving, and restoring a new instance from an existing instance’s backups.Find them under PLATFORM → Object Stores in the sidebar, at /object-stores. The page is headed Object Stores“Backup destinations for Postgres add-ons. Supports AWS S3, S3-compatible (e.g. MinIO), Azure, and GCS.” — and reads NO OBJECT STORES YET until you register one. Registered stores are listed under Organization Object Stores: they belong to the organization, not to a stack.Choose New Object Store.The New Object Store dialog, subtitled Configure a backup destination. Credentials reference an existing Secret. It has required Name, Destination path and Retention fields, provider tabs for S3 slash S3-compatible, Azure and GCS, and on the S3 tab a required Region, an optional Endpoint URL, and Access Key ID and Secret Access Key rows that each select a Generic secret and then a key inside it
Credentials are never typed into this form. Every credential field picks a Generic Secret and a key inside it, which is what the dialog means by “Credentials reference an existing Secret.” Create the Secret first — see Create the secret — then come back. If the select is empty, you have no Generic Secret yet.
An Azure or GCS store cannot be edited afterwards. The dialog refuses with “Editing Azure/GCS stores is not yet supported in the UI. Please delete and re-create the store.” S3 stores can be edited, except for the name.

Using one

Turn on backups on the Postgres addon and pick the store there — the addon’s Backups section has the Object Store select, and it refuses to enable scheduled backups or WAL archiving without one. Restoring a new instance from a store’s backups goes through the same list.Deleting a store that an addon still points at is refused. The toast shows the reason the API gives; when the API returns no message, the dashboard falls back to “This Object Store is in use by one or more Postgres add-ons.” Either way, detach it from the addon first.

Postgres

Enabling scheduled backups, WAL archiving, and restoring an instance from an object store.