hetshield

Hetzner snapshots don't include volumes - and neither does any export

Cloud volumes are excluded from Hetzner snapshots and backups. How to check before you rely on one, and how to back a volume up separately.

updated 7 minute read by Dominik Geimer

The short answer

No. Hetzner's documentation says it in one sentence: "Backups and Snapshots do not include any Volumes which are attached to your server" (backups and snapshots FAQ). The volumes overview adds the other half: "we do not provide Backups or Snapshots for Volumes."

The same limit applies to every export method that starts from an image of the server, whether that is a snapshot, a backup, or a clone booted into the rescue system to read the disk out. What you get is the server's local disk, complete and bootable. The volume is a separate device and needs a separate copy. If your database, uploads or mail spool live on a volume, a restored server comes up with an empty directory where they used to be.

What Hetzner says, and why it is so

A snapshot is a copy of the server's local disk, the one it boots from. A volume is a network block device that Hetzner attaches to the server; the docs list its limits as up to 16 volumes per server, up to 10 TB in total, and attached to one server at a time. That last point is why the exclusion is structural rather than a missing feature. An image captures a disk, and a volume is not part of that disk. Because a volume can only be attached to one server, any server created from the image, including a temporary clone used to read the disk out, cannot see it either.

Hetzner has not hidden this. It is in the FAQ, in the overview, and in the API, which describes a server's volumes as a list of IDs next to the server rather than as part of its disk. It gets missed because the console shows the volume under the server, the file tree looks like one machine, and nothing in the snapshot dialog says which parts of that tree are about to be left out.

The surprise at restore time

Hetzner's automount script formats a new volume and mounts it at /mnt/HC_Volume_<id> (overview), and the device path the API reports for every volume is /dev/disk/by-id/scsi-0HC_Volume_<id>, the linux_device field in the volumes API. Either way there is a line in /etc/fstab naming a disk that will not exist wherever the image is restored.

What happens next depends on one mount option. The mount tutorial that Hetzner's volume docs point to uses nofail,defaults in its fstab example and explains the option this way: "If the system cannot mount one of the file systems, it will skip this particular device/partition once and the reboot will continue" (Hetzner Community). With nofail, the restored server boots normally, the mount point is an empty directory, and anything configured to write there writes into the root filesystem instead, without complaint. Without nofail, systemd waits for the device, times out, and drops to an emergency prompt on the console. The server never reaches the network, and the image looks broken when it is not.

Check the image for this before you need it:

grep HC_Volume /etc/fstab

If it prints a line, that server's export is incomplete by design. On a restored machine that stopped at the emergency prompt, comment the line out or add nofail, reboot, and restore the volume's data from wherever you back that up. /docs#restore-firstboot walks through it alongside the other first-boot fixes.

Which of your servers are affected

The console shows volumes per server, but for more than a handful the CLI or the API is quicker.

hcloud CLI

hcloud volume list

The default columns are id, name, size, server and location, so this is every volume in the project and the server it is attached to. The reverse view is a column on the server list:

hcloud server list -o columns=name,volumes

volumes prints the attached volume IDs; an empty cell means the server's disk is self-contained. The column is defined in the CLI's server list source, and it is not part of the default output, which is why it has to be asked for.

API

GET /servers returns a volumes array of IDs on every server object (API reference). A read-only token is enough:

curl -s -H "Authorization: Bearer $HCLOUD_TOKEN" \
  "https://api.hetzner.cloud/v1/servers?per_page=50" \
  | jq -r '.servers[] | select(.volumes | length > 0) | "\(.name)\t\(.volumes)"'

The API returns 25 servers per page by default and 50 at most, so on a larger project follow meta.pagination.next_page until it is null. A script that reads only the first page misses servers.

Backing a volume up separately

There is no volume snapshot. The volumes FAQ answers "Can I clone / duplicate Volumes?" with "This is not currently possible using Hetzner Console" and suggests a second volume plus dd or rsync instead (volumes FAQ); the API offers attach, detach, resize and protection actions on a volume and nothing else. So the idea of a second server mounting a copy of the volume does not exist as a feature. What does work:

File-level backup to storage outside the account

restic backs up a directory tree to S3-compatible storage or over SFTP; BorgBackup does the same over SSH, and Hetzner documents both restic and Borg against a Storage Box. Point either at the volume's mount point on a schedule. One caveat: a Storage Box is billed to the same Hetzner account as the server. It is a reasonable second copy for hardware failure and for rolling back a mistake; if the goal is a copy that survives losing the account, the repository has to sit with another provider or on hardware you own.

A database dump

If the volume holds a database, a file-level copy of a running database directory is not a backup. Dump it with pg_dump, mysqldump or mariadb-dump on a schedule and ship the dump with the same restic or Borg job. A dump is application-consistent, which neither a snapshot nor a block-level read of a running system can be.

A block-level copy from rescue mode

With the server powered off and booted into Hetzner's rescue system the volume is still attached and visible as a block device, so it can be read out with dd the same way the root disk can. This is the one way to get a byte-exact copy of a volume, and it costs downtime for the whole time the read takes, which for a large volume is long. For most workloads a file-level backup taken while the server runs is the better trade.

Design advice: keep the boot disk self-contained

If you want a server to be exportable as one bootable file, keep everything the server needs to come back on its local disk: the operating system, the application, its configuration, and the database. Use volumes for data that already has its own backup path and its own restore procedure, such as media libraries, build caches or blobs that are synchronised elsewhere anyway. Every volume adds a second backup stream and a second restore step, so attach one only when the data justifies that.

If the local disk is too small, the first move is a larger server type, which grows the local disk with it, rather than a volume for the database. Moving a database from a volume back onto the root disk is a copy and a configuration change; discovering at restore time that it was never in the image is a different kind of afternoon.

Before you rely on an export

Whichever export path you use, run hcloud volume list first, and treat a server with a volume as two backup jobs rather than one. hetshield does this check for you: the server list shows which of your servers carry a volume before you pay, and /docs#included spells out what the image contains and what it cannot. The warning is there so that the empty directory never turns up at the moment you are restoring.

Keep reading