hetshield

Can you download a Hetzner Cloud snapshot? No. Here is what works instead.

Hetzner offers no snapshot export or download. The three real options, what each costs in downtime and effort, and how to get a bootable image out.

updated 10 minute read by Dominik Geimer

No. Hetzner Cloud has no way to download or export a snapshot, not in the console, not in the API, not in the CLI. What you can do is read the disk that the snapshot describes and copy that out yourself, and there are three ways to do it, one of which needs no downtime at all.

This page explains why the export does not exist, walks through the three options with their trade-offs, and shows the exact commands for the one that leaves your server running.

What a snapshot is, and where it lives

A snapshot is a copy of a server's disk, stored as an image in the same Hetzner project as the server. You create it by hand, it stays until you delete it, and you can create new servers from it. Backups are the automatic sibling: created daily, seven slots per server, the oldest overwritten as new ones arrive. Hetzner's backups and snapshots FAQ has the comparison table.

Three facts about snapshots matter for the rest of this article:

  • The limit is 30 snapshots across all projects of an account, not per server (FAQ).
  • Snapshots are billed per GB of image size per month. At the time of writing that is roughly €0.0143 per GB; the current figure is on the Hetzner Cloud pricing page.
  • "Backups and Snapshots do not include any Volumes which are attached to your server" (FAQ). More on that at the end.

For rolling back a bad upgrade or cloning a configured server, snapshots are hard to beat, and nothing below is an argument against them. The question is only what to do when the copy has to exist outside your Hetzner project.

Why there is no export

It is worth checking rather than assuming. Hetzner publishes its API as an OpenAPI document at docs.hetzner.cloud/cloud.spec.json. The image resource has exactly these operations:

GET    /images
GET    /images/{id}
PUT    /images/{id}
DELETE /images/{id}
POST   /images/{id}/actions/change_protection
POST   /servers/{id}/actions/create_image

List, get, update the description and labels, delete, toggle protection, and create one from a server. There is no operation that returns image data, no signed download URL, no export job. Nothing in the spec matches "export" or "download" at all.

The question has been asked directly. In January 2024 someone opened hetznercloud/cli#677 asking, among other things, why snapshots cannot be downloaded or uploaded. A maintainer answered two days later that the public API "currently does not offer the ability to move resources between objects or to download snapshots", pointed to a 2023 blog post with a manual workaround, and closed the issue as not planned. Nothing has changed since; the current spec has the same six operations.

So the snapshot is a black box from the outside. What you can read is the disk it was made from, or a disk created from it. That is what all three options below do.

Option 1: dd from the running server

The simplest thing that can possibly work: SSH into the server and read its disk device from beginning to end.

ssh root@server 'dd if=/dev/sda bs=4M status=progress | zstd -1 -T0' > server.img.zst

It needs no API calls and costs nothing beyond outbound traffic. It has one problem that is not fixable: the filesystem is mounted and being written to while you read it. Blocks read early and blocks read late describe different moments, and anything a database or the journal wrote in between is caught halfway.

Hetzner's own API documentation says the same thing about creating an image from a running server: "If the Server is currently running you must make sure that its disk content is consistent. Otherwise, the created Image may not be readable" (cloud.spec.json, create_image). A dd from inside the running system is strictly worse than that, because it is not even a point-in-time copy.

A mostly idle web server usually survives this and boots after an fsck; a busy database server may not. If you go this route, stop heavy writers first and run a filesystem check on every restore before trusting it.

Option 2: rescue mode, with downtime

The clean version of option 1 is to read the disk while nothing is mounted. Hetzner's rescue system is a small Linux that boots over the network instead of your installed system; once it is enabled, "after the next restart, your server will automatically boot over the network of the rescue system" (rescue system docs).

hcloud server enable-rescue web-01     # prints a one-time root password
hcloud server reboot web-01
ssh root@<server-ip> 'set -o pipefail; dd if=/dev/sda bs=4M status=progress | zstd -1 -T0' > server.img.zst   # pipefail: a dd that fails must not exit 0 through zstd
hcloud server reboot web-01            # back to the normal system

The image is consistent, because nothing is writing to the disk. The price is that your server is offline for the whole read. How long depends on disk size and the read rate of the rescue system; on a 40 GB cx23 in Falkenstein a full read through zstd -1 took 1:52 at 365.7 MB/s, producing a 3.49 GB file. For a 40 GB disk that is a short maintenance window. For 320 GB it is a long one, and the download to your own machine comes on top if you pipe it out over SSH like this.

This is the method most forum answers and the 2023 blog post linked from the CLI issue describe (hannes.enjoys.it). It is fine if you can afford the outage; most people running something for other people cannot.

Option 3: snapshot, throwaway clone, rescue mode, no downtime

The third option combines the other two and removes the downtime. The idea: take a snapshot of the running server (which Hetzner does without stopping it), create a temporary server from that snapshot without letting it boot, put that temporary server into rescue mode, and read its disk instead of the original's. The original server is only ever touched by the snapshot call, and it keeps serving traffic throughout.

It works because two things Hetzner's API allows happen to combine well: a server can be created from a snapshot switched off (start_after_create is a flag in the create-server call), and the rescue system can be armed on a server that is off, so the first and only thing the clone ever boots is the rescue image - never your operating system, your cron jobs or your mail queue.

That is the whole trick, and it is not complicated. What makes it a job rather than a command is everything around it:

  • Choosing the clone. It must match the source's CPU architecture and have a disk at least as large - not the same server type, which is routinely sold out. Types sell out and come back within minutes, so a resource_unavailable on creation is the normal case and needs a retry with the next candidate, not a wait.
  • The snapshot limit is 30 for the whole account. The run spends one slot for a few minutes; at 30 it cannot start at all.
  • Rescue access. enable_rescue hands back a one-time root password, so no SSH key is added to the project and nothing is left behind. Hetzner also emails the account owner about that password every time; if somebody else owns the account, they should hear about it from you first.
  • Honest exit codes. The read is a pipeline, and without set -o pipefail a dd that fails halfway leaves the compressor exiting 0 and you with a truncated file that looks like a success.
  • Where the bytes go. A single PUT of a stream of unknown length is not something S3 accepts, so the upload is a multipart one, and if the machine doing it is one your customer is root on, it had better not hold your storage credentials.
  • Cleanup, every time. The clone bills by the hour and the snapshot by the GB until they are deleted - including when the run fails halfway, including when the process doing the work is killed. That needs a finally, and a sweeper for what the finally misses.
  • Firmware. Whether the disk boots through UEFI or legacy BIOS decides how the restored machine has to be created, and getting it wrong looks exactly like a corrupt download. It can be read off the partition table while the rescue system has the disk attached.
  • Volumes, which are in no snapshot and therefore in no export - more below.

The snapshot is crash-consistent, the same as any snapshot of a running system: the state a disk is in after a power cut. Journaling filesystems handle that; databases usually recover on start but may replay or discard the last transactions. If you need application consistency, dump the database separately or pause writes for the seconds the snapshot call takes.

What it costs at Hetzner: one snapshot slot for a few minutes, the snapshot's per-GB price prorated for those minutes, and the clone's hourly rate for the duration of the read - a cx23 is €0.0088 per hour at the time of writing, from the pricing page. A few cents in total.

This is what hetshield does: the whole sequence above, with the type selection, the retries, the exit codes, the cleanup and the firmware check, for $29 per server. If you would rather build it yourself, the pieces are all in Hetzner's API and the list above is the specification.

What you get

A raw block image of the whole disk: partition table, bootloader, every partition, exactly as they were. Decompress it:

zstd -d server.img.zst -o server.raw

Raw images are the common denominator. Proxmox and libvirt read them as they are; VMware, Hyper-V and VirtualBox take them after one qemu-img convert to VMDK, VHDX or VDI. Step-by-step instructions per hypervisor are in the restore documentation, and there is a dedicated guide for moving the image into Proxmox.

One thing to check before you build a virtual machine around the file: the firmware. Hetzner's changelog notes that newer server types "will use UEFI by default" and that a snapshot used with them needs an EFI system partition (changelog, 23 August 2023). A stock x86 cx23 we exported had one. A UEFI disk will not boot in a virtual machine set to legacy BIOS, and the failure looks like a corrupt download. Look before you guess:

fdisk -l server.raw        # or: sgdisk -p server.raw

A partition of type EFI System means UEFI; none means legacy BIOS. Set the virtual machine accordingly; the firmware section of the docs lists the setting for each hypervisor.

What is not in it

Volumes. Hetzner leaves attached volumes out of snapshots and backups, and a volume "can only be attached to one server at a time" (volumes FAQ), so it cannot be mounted on the clone either. No agentless method, including all three above, can include a volume. If your data lives on one, that data needs its own backup path: a file-level tool such as restic or Borg from inside the server, to a destination outside the account.

Check before you start:

hcloud volume list

Every server that appears in the SERVER column has data the image will not contain. On restore the machine still boots, because Hetzner's own fstab entries for volumes carry nofail, but the mount point will be empty.

Summary

Option Downtime Consistency Effort
dd from the running system none poor; fsck on restore, databases at risk one command
Rescue mode full read duration good four commands, a maintenance window
Snapshot, clone, rescue none crash-consistent, like any snapshot seven commands, cleanup discipline

Hetzner's snapshots stay the right tool for rollbacks and cloning within your project. When the copy has to exist outside the project, option 3 gets it there without anyone noticing. If you would rather not run the seven steps yourself, hetshield does exactly that for $29 per export, any disk size, and the docs list every API call it makes in your project.

Keep reading