Restore a raw disk image to a new Hetzner Cloud server: dd from rescue mode
Hetzner Cloud cannot import disk images. Write a raw .img onto a fresh server from rescue mode, fix the network, and turn it into a reusable snapshot.
11 minute read by Dominik Geimer
Hetzner Cloud cannot import a disk image. There is no upload endpoint, no "restore from file" button, and the rebuild action only accepts images that already live in Hetzner. If you have a raw image of a server on your own disk and want it running on Hetzner again, the supported route is to boot a new server into the rescue system and write the image onto its disk yourself.
This guide shows that route end to end: which server to create, the exact commands, what to check before the first boot, the network problem that makes a restored server look dead, and how to turn the result into a snapshot you can create more servers from. It works for any raw whole-disk image, such as one taken from an old Hetzner server or a disk converted with qemu-img convert -O raw.
Why Hetzner has no import
The official OpenAPI spec at docs.hetzner.cloud/cloud.spec.json lists what you can do with images: list, get, update, delete, change protection, and create one from a server. Nothing accepts image data. The only ISO endpoint is GET /isos, a list of ISOs Hetzner provides. The rebuild action "overwrit[es] its disk with the content of an Image", and that Image "can either be one you have created earlier (backup or snapshot Image) or it can be a completely fresh system Image provided by us."
So the only images a Hetzner server can be built from are ones Hetzner already holds. To get yours in, you make one: write the disk content into a server through the rescue system, then, if you want to reuse it, snapshot that server. The author of the community tool hcloud-upload-image describes the same limitation: "The only option for custom disk images is to take a snapshot of an existing server's root disk."
Hetzner's community tutorial Deploy a .img file to a cloud server stages the image on an attached volume first. The method below streams instead, so no volume is needed.
What you need before you start
- A raw image of the whole disk, not of one partition. It must contain the partition table and bootloader. If you have a
.vmdk,.vhdxor.qcow2, convert it first:qemu-img convert -f qcow2 -O raw server.qcow2 server.raw. - A matching architecture. An x86 image needs an x86 server type (cx, cpx), an Arm image needs an Arm one (cax). Hetzner will not run one on the other.
- A server type whose disk is at least as large as the image.
ddstops with "No space left on device" when the target is smaller, and the partition table at the end of a too-short disk is missing. A larger disk works; the extra space is unused until you grow the last partition (see below). - The
hcloudCLI and a project token with read and write access, or the same actions in the console. - About 10 minutes and a few cents. The new server bills by the hour from creation. At the time of writing a cx23 is €0.0088 per hour (Hetzner Cloud pricing).
Check the image's firmware before you begin, because it decides whether the server boots:
zstd -d server.img.zst -o server.raw # skip if you already have a raw file
sgdisk -p server.raw # or: fdisk -l server.raw
A partition of type EFI System means the image boots through UEFI; a small bios_grub partition or a plain MBR means legacy BIOS (a hybrid image has both). Hetzner's changelog of 23 August 2023 notes that newer server types use UEFI by default and that images used with them need an EFI system partition. If your image is BIOS-only and the new server will not boot, this is the first thing to suspect. A stock x86 cx23 we exported earlier had an EFI system partition.
Also test the file itself, so a truncated download does not cost you the server:
zstd -t server.img.zst
Step 1: create a throwaway server
Create a server of the right architecture and size. The system image you pick here does not matter, because it is about to be overwritten.
hcloud server create --name restore-01 --type cx23 --image ubuntu-24.04 --location fsn1
Server types sell out and come back within minutes, so if creation fails with resource_unavailable, try another type with a large enough disk or another location. Hetzner emails the account owner when a server is created; that is normal.
If the original server had a fixed IPv4 address you want to keep, attach the same Primary IP when you create the server. A restored server will otherwise get a new address.
Step 2: boot into the rescue system
hcloud server enable-rescue restore-01 # prints a one-time root password
hcloud server reset restore-01
Rescue mode is documented in the rescue system docs. In the API reference: "The next time a Server with enabled rescue mode boots it will start a special minimal Linux distribution designed for repair and reinstall," enabling it "will not reboot your Server," and rescue mode "is automatically disabled when you first boot into it or if you do not use it for 60 minutes." So there is a one-hour window. Hetzner also emails the account owner the rescue password each time.
Wait a few seconds, then connect with the printed password. Rescue systems get a new host key on every boot, so ignore the key mismatch warning for this one connection:
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@<server-ip>
lsblk
lsblk should show one disk, normally /dev/sda, with the size of the server type. Confirm that before writing anything. dd does not ask.
Step 3: write the image
The rescue system runs from RAM and a 40 GB raw file will not fit, so never copy the file there first. Stream it from your machine and decompress on the way:
zstd -dc server.img.zst | ssh root@<server-ip> 'dd of=/dev/sda bs=4M conv=fsync status=progress'
Or, if the compressed file sits on a web server you control, let the rescue system pull it. The rescue system includes zstd (we have used it there; xz and bzip2 are the usual alternatives for other formats):
ssh root@<server-ip>
set -o pipefail
curl -fsSL https://example.com/server.img.zst | zstd -dc | dd of=/dev/sda bs=4M conv=fsync status=progress
The pipefail matters. Without it a failed download followed by a successful dd of zero bytes still exits 0, and you reboot into a blank disk. Only the compressed size crosses the network; our measured 40 GB disk compressed to 3.49 GB, though the ratio depends on your data.
When dd finishes, flush and check the result before rebooting:
sync
partprobe /dev/sda
lsblk -f /dev/sda
fsck -fn /dev/sda3 # use the root partition lsblk shows; -n makes it read only
The filesystem should be recognised and the read-only fsck should be clean. If the image came from a running machine copied with plain dd, fsck may report errors; repairing them with fsck -fy before the first boot is the safe order.
Step 4: use a larger disk if you chose one
If the new server's disk is bigger than the image, Linux sees the old partition table end halfway through the disk, and the backup GPT header is in the wrong place. Move it to the end of the disk and grow the root partition and filesystem:
sgdisk -e /dev/sda # move the backup GPT header to the end
growpart /dev/sda 3 # partition number from lsblk; from cloud-guest-utils
resize2fs /dev/sda3 # ext4; use xfs_growfs on a mounted XFS
growpart is not always in the rescue system; apt install cloud-guest-utils or parted /dev/sda resizepart does the same. Most Ubuntu and Debian cloud images also grow the root filesystem on first boot through cloud-init, so you may not need to do this at all. Look at lsblk after the first boot and act only if the space is missing.
Step 5: fix the network before the first boot
This is the step that makes people think the restore failed. The server boots, the console shows a login prompt, and it does not answer on its IP.
The usual causes:
- A static address from the old server. The image we examined from a Hetzner server carried a static address and a gateway of
172.31.1.1in its netplan configuration. A new server has a different IP, so that file points at an address that no longer exists. - A MAC address pinned to the old interface. Some netplan files match on
macaddress; the new server has another one. - cloud-init hunting for the old environment. With a Hetzner data source it will reconfigure the network on its own; without one it waits for a metadata service and can sit for a minute or two before giving up.
Mount the root partition in rescue mode and look before you boot:
mount /dev/sda3 /mnt
cat /mnt/etc/netplan/*.yaml
ls /mnt/etc/cloud/cloud.cfg.d/
A server created from Hetzner's own system image is the reference. Before you overwrite the disk, or on a second throwaway server in the same location, read its /etc/netplan/ and /etc/cloud/ and copy what it does. If you keep cloud-init enabled on the restored image, it can rewrite the network configuration itself on a Hetzner server, provided it reaches the metadata service. If you would rather turn it off, do that deliberately: touch /mnt/etc/cloud/cloud-init.disabled.
Unmount and reboot:
umount /mnt
reboot
Rescue mode disabled itself when you booted into it, so the server now starts from its own disk. If it does not answer, open the console in the Hetzner Cloud console and read what the machine is saying: a stuck boot, a missing network, and a wrong-firmware failure each look different there.
Step 6: turn it into a reusable image
A restored server is just a server. If you want to create more from the same state, or keep it as a restore point, shut it down and take a snapshot. Hetzner's own documentation for create_image recommends shutting the server down first so the disk content is consistent.
hcloud server shutdown restore-01
hcloud server create-image restore-01 --type snapshot --description "restored from server.img.zst"
hcloud server create --name web-02 --type cx23 --image <snapshot-id>
Remember that snapshots count against the account limit of 30 and are billed per GB per month (backups and snapshots FAQ). The community tool hcloud-upload-image automates steps 1 to 6: it takes an image URL, a CPU architecture and optionally a compression format, and leaves you with a snapshot. It is MIT licensed and, as its README says, not an official Hetzner product. The image must be reachable by URL, and the README's example uses bz2, so check hcloud-upload-image upload --help for other formats.
What does not come back
Volumes. An image made from a snapshot, a backup, or a disk read in rescue mode contains the server's local disk and nothing else. A volume is a separate network device that can be attached to one server at a time, so it is in none of them, and on the restored server its mount point is empty. Check /etc/fstab for volume mounts, since an entry without nofail can stop the boot, and any data that lived on the volume needs its own copy; see Hetzner snapshots do not include volumes.
Two copies at once. The restored server has the same SSH host keys, hostname and often the same service configuration as the original. If the original is still alive, do not let both talk to the same mail relay, queue or database.
Where hetshield fits
The restore above is only as good as the file you have. If the image has to exist outside your Hetzner account, hetshield produces it from a running server without downtime: a snapshot, a throwaway clone that never boots your system, and a compressed raw image you download, $29 flat plus VAT, refunded if it does not finish. A 40 GB disk was read in 1:52 and a whole export took about nine minutes end to end. Volumes are not included, as for every agentless method.
To be precise about what has been checked: hetshield's restore to Hyper-V has been tested on real hardware. This guide's route onto a new Hetzner server uses ordinary rescue-mode tooling and is not one of our measured restores, so test it once on a cheap server before you need it. The docs list the restore steps we have verified, including the firmware check that decides whether a hypervisor boots the image. For moving the same file onto your own hardware, see Migrate a Hetzner Cloud VM to Proxmox without downtime and the qemu-img conversion guide. To get the image out in the first place by hand, start with Can you download a Hetzner Cloud snapshot?