Skip to content

Persistent volumes

Attach a writable disk to a service that survives redeploys and container restarts — for uploads, SQLite files, a CMS's content directory, anything that has to outlive one container.

What a volume is

A volume is a disk mounted into a service's container at a path you choose. A service can have several, each with its own name and mount path. Everything else in the container is thrown away on each deploy.

There are two kinds, and which one you have decides how its data is protected:

KindWhere it livesProtection
Block-backedA Hetzner Cloud block volume attached to the machine that runs the service.Triple-replicated by Hetzner. Off-site backups are refused — they would add nothing.
Host-localA Docker volume on the machine's own disk. Volumes on your own machine, volumes declared in hoststack.yaml, and volumes created before block storage.One disk. Turn on nightly off-site backups — they are the only second copy.

Attach a volume

  1. Add the volume

    In the dashboard, open the service, then Settings → Storage and click Add Volume. Give it a name, a mount path and a size. Or do the same from the terminal:

  2. Deploy the service

    Nothing is mounted until the next deploy. A block-backed volume starts as pending; the deploy creates the disk, attaches it, and the volume turns active. After every deploy, HostStack checks inside the container that each configured volume is really mounted — a deploy fails rather than starting your app without its disk.

hoststack volumes create svc_... data /var/data --size 10
# It will mount on the next deploy.
hoststack deploy trigger svc_...

Creating a volume needs a payment method on the team, because volumes are billed by size — see Billing.

Volume settings

name
stringrequired

Lowercase letters, digits and hyphens, up to 64 characters. It becomes part of the Docker volume's name on the host, so renaming means a different disk — pick it once.

mountPath
stringrequired

The absolute path inside the container, up to 500 characters. Any path your app owns works — /var/lib/mysql, /var/www/html, /app/data. It may not contain .. or //.

Refused: /tmp and /var/tmp, which are tmpfs and would hide the volume (writes would go to RAM and vanish on restart), and /, /proc, /sys, /dev and /etc, which belong to the container runtime.

sizeGb
number

10 to 10,240 GB (10 TB). 10 GB is Hetzner's minimum block volume size. The MCP create_volume tool accepts at most 100 GB; use the dashboard, CLI or SDK for more.

Default: 10

Declaring a disk in hoststack.yaml

A disk: block (one per service, using Render's field names, including the capital-GB sizeGB) is applied on every deploy and shows up in Settings → Storage afterwards. It behaves differently from a volume added through the dashboard or API:

  • It is always a host-local Docker volume, never a block volume, so turn its backups on.
  • sizeGB defaults to 1. It is recorded on the volume and billed; a host-local volume has no fixed size.
  • A yaml disk replaces any other volume at the same mountPath.
  • Renaming it mounts a new, empty disk. The old one is kept until you delete it.

See Infrastructure as code for the rest of the file.

Grow, change or delete a volume

hoststack volumes list svc_...
hoststack volumes resize svc_... vol_... 20   # grow to 20 GB
hoststack volumes delete svc_... vol_...
Grow

Volumes only grow — a smaller size is rejected, because a filesystem cannot shrink safely under live data. A block volume is enlarged at Hetzner and its filesystem extended on the machine straight away.

Move the mount

Change mountPath in the dashboard's edit dialog, the SDK's update or MCP's update_volume. It takes effect on the next deploy.

Delete

The volume is marked deleting and the machine is told to remove the disk. If the machine is offline, HostStack retries every 5 minutes until it confirms. Deleting destroys the data and the volume's restore points with it.

Backups and restore

Off-site backups are for host-local volumes. They are off by default — except on a volume on your own machine, where they are on from the moment it is created: that disk is the only copy there is. On a block-backed volume the toggle is refused.

hoststack volumes backup svc_... vol_... on     # nightly backups
hoststack volumes backups svc_... vol_...       # list restore points
hoststack volumes restore svc_... vol_... 41 --yes
Nightly backup

At 02:00 UTC each opted-in volume is archived as a .tar.gz and uploaded to off-site object storage. A machine that was off at 02:00 catches up when it reconnects.

Restore points

The 7 most recent uploaded archives, newest first. Turning backups on schedules them — it does not take one — so check that a restore point exists before you rely on it. Turning backups off keeps the archives you already have.

Restore

Unpacks the archive over the volume's current contents: files in the archive overwrite what is there, and files that are not in it stay. Nothing is snapshotted first, so there is no undo. The service keeps running during a restore — suspend it first if something writes to the volume.

Import

In Settings → Storage, the restore dialog also takes a .tar.gz you upload, with paths relative to the mount point. The browser uploads straight to object storage, so the limit is S3's single-upload ceiling: 5 GB per archive.

A restore or import is refused while the volume is not active, while another restore is still running (two archives unpacking into one directory produce a tree that is neither), or while the volume is being cloned or moved. Restoring requires the admin role.

Move a volume to block storage

A host-local volume on a HostStack machine can be moved onto a block volume in place: in Settings → Storage, choose Migrate to durable storage. The service must have been deployed first, so HostStack knows which machine holds the data. The block volume is created, the data copied, and the volume only switches to block-backed once the copy is confirmed. Volumes on your own machine stay host-local — a Hetzner block volume can only attach to a Hetzner server.

Billing

Volumes cost €0.10 per GB-month of their configured size. Usage is measured once a day in GB-days, so a 10 GB volume kept for a 30-day month costs €1.00, and resizing or deleting shows up on the next day's measurement.

  • A volume marked deleting is no longer counted, even before the disk has been removed.
  • A block-backed volume is not billed until its disk has actually been created — a volume you added but never deployed costs nothing.

See Billing for how usage appears on the invoice.

Troubleshooting

Files vanish after a restart

The app is writing to /tmp or another path outside the volume. Only the volume's mountPath survives a restart or deploy; check where the app actually writes.

Volume stays pending

A block-backed volume becomes active during the next deploy. Deploy the service; if it is still pending afterwards, the volume shows the provisioning error in Settings → Storage.

Deploy fails on volume mount verification

A configured volume was not mounted in the new container, so the deploy stopped rather than run the app without its data. The deploy log names the missing path.

Permission denied or read-only file system

Containers run with a read-only root filesystem and without the capability that lets root ignore file ownership. See Container runtime for what that means for your image.

Check what is attached
hoststack volumes list svc_...

Next steps

Was this page helpful?

Essential cookies only — for login sessions. No tracking. Details