Introduction

Quickly upload any raw disk images into your Hetzner Cloud projects!

Badge: Documentation Badge: Stable Release Badge: License MIT

About

The Hetzner Cloud API does not support uploading disk images directly, and it only provides a limited set of default images. The only option for custom disk images that users have is by taking a "snapshot" of an existing servers root disk. These can then be used to create new servers.

To create a completely custom disk image, users have to follow these steps:

  1. Create server with the correct server type
  2. Enable rescue system for the server
  3. Boot the server
  4. Download the disk image from within the rescue system
  5. Write disk image to servers root disk
  6. Shut down the server
  7. Take a snapshot of the servers root disk
  8. Delete the server

This is an annoyingly long process. Many users have automated this with Packer & packer-plugin-hcloud before, but Packer offers a lot of additional complexity to wrap your head around.

This repository provides a simple CLI tool & Go library to do the above.

Getting Started

CLI

Binary

We provide pre-built deb, rpm and apk packages. Alternatively we also provide the binaries directly.

Check out the GitHub release artifacts for all of these files and archives.

Arch Linux

You can get hcloud-upload-image-bin from the AUR.

Use your preferred wrapper to install:

yay -S hcloud-upload-image-bin

go install

If you already have a recent Go toolchain installed, you can build & install the binary from source:

go install github.com/apricote/hcloud-upload-image@latest

Docker

There is a docker image published at ghcr.io/apricote/hcloud-upload-image.

docker run --rm -e HCLOUD_TOKEN="<your token>" ghcr.io/apricote/hcloud-upload-image:latest <command>

Usage

export HCLOUD_TOKEN="<your token>"
hcloud-upload-image upload \
  --image-url "https://example.com/disk-image-x86.raw.bz2" \
  --architecture x86 \
  --compression bz2

To learn more, you can use the embedded help output or check out the CLI help pages in this repository.:

hcloud-upload-image --help
hcloud-upload-image upload --help
hcloud-upload-image cleanup --help

Go Library

The functionality to upload images is also exposed in the library hcloudimages! Check out the reference documentation for more details.

Install

go get github.com/apricote/hcloud-upload-image/hcloudimages

Usages

package main

import (
	"context"
	"fmt"
	"net/url"

	"github.com/hetznercloud/hcloud-go/v2/hcloud"

	"github.com/apricote/hcloud-upload-image/hcloudimages"
)

func main() {
	client := hcloudimages.NewClient(
		hcloud.NewClient(hcloud.WithToken("<your token>")),
	)

	imageURL, err := url.Parse("https://example.com/disk-image-x86.raw.bz2")
	if err != nil {
		panic(err)
	}

	image, err := client.Upload(context.TODO(), hcloudimages.UploadOptions{
		ImageURL:         imageURL,
		ImageCompression: hcloudimages.CompressionBZ2,
		Architecture:     hcloud.ArchitectureX86,
	})
	if err != nil {
		panic(err)
	}

	fmt.Printf("Uploaded Image: %d", image.ID)
}

Contributing

If you have any questions, feedback or ideas, feel free to open an issue or pull request.

License

This project is licensed under the MIT license, unless the file explicitly specifies another license.

Support Disclaimer

This is not an official Hetzner Cloud product in any way and Hetzner Cloud does not provide support for this.

Uploading Images

Check out these docs from other projects to learn how to use hcloud-upload-image:

Fedora CoreOS ↗

Flatcar Container Linux ↗

Talos Linux ↗

hcloud-upload-image

Manage custom OS images on Hetzner Cloud.

Synopsis

Manage custom OS images on Hetzner Cloud.

Options

  -h, --help            help for hcloud-upload-image
  -v, --verbose count   verbose debug output, can be specified up to 2 times

SEE ALSO

hcloud-upload-image upload

Upload the specified disk image into your Hetzner Cloud project.

Synopsis

This command implements a fake "upload", by going through a real server and snapshots. This does cost a bit of money for the server.

Image Size

The image size for raw disk images is only limited by the servers root disk.

The image size for qcow2 images is limited to the rescue systems root disk. This is currently a memory-backed file system with 960 MB of space. A qcow2 image not be larger than this size, or the process will error. There is a warning being logged if hcloud-upload-image can detect that your file is larger than this size.

hcloud-upload-image upload (--image-path=<local-path> | --image-url=<url>) --architecture=<x86|arm> [flags]

Examples

  hcloud-upload-image upload --image-path /home/you/images/custom-linux-image-x86.bz2 --architecture x86 --compression bz2 --description "My super duper custom linux"
  hcloud-upload-image upload --image-url https://examples.com/image-arm.raw --architecture arm --labels foo=bar,version=latest
  hcloud-upload-image upload --image-url https://examples.com/image-x86.qcow2 --architecture x86 --format qcow2

Options

      --architecture string     CPU architecture of the disk image [choices: x86, arm]
      --compression string      Type of compression that was used on the disk image [choices: bz2, xz]
      --description string      Description for the resulting image
      --format string           Format of the image. [choices: qcow2]
  -h, --help                    help for upload
      --image-path string       Local path to the disk image that should be uploaded
      --image-url string        Remote URL of the disk image that should be uploaded
      --labels stringToString   Labels for the resulting image (default [])
      --server-type string      Explicitly use this server type to generate the image. Mutually exclusive with --architecture.

Options inherited from parent commands

  -v, --verbose count   verbose debug output, can be specified up to 2 times

SEE ALSO

hcloud-upload-image cleanup

Remove any temporary resources that were left over

Synopsis

If the upload fails at any point, there might still exist a server or ssh key in your Hetzner Cloud project. This command cleans up any resources that match the label "apricote.de/created-by=hcloud-upload-image".

If you want to see a preview of what would be removed, you can use the official hcloud CLI and run:

$ hcloud server list -l apricote.de/created-by=hcloud-upload-image
$ hcloud ssh-key list -l apricote.de/created-by=hcloud-upload-image

This command does not handle any parallel executions of hcloud-upload-image and will remove in-use resources if called at the same time.

hcloud-upload-image cleanup [flags]

Options

  -h, --help   help for cleanup

Options inherited from parent commands

  -v, --verbose count   verbose debug output, can be specified up to 2 times

SEE ALSO

Go Library

You can find the documentation at pkg.go.dev/github.com/apricote/hcloud-upload-image/hcloudimages ↗.

Changelog CLI

1.0.1 (2025-05-09)

Bug Fixes

  • timeout while waiting for SSH to become available (#92) (e490b9a)

1.0.0 (2025-05-04)

Features

0.3.1 (2024-12-07)

Bug Fixes

  • cli: local install fails because of go.mod replace (#47) (66dc5f7)

0.3.0 (2024-06-23)

Features

Bug Fixes

  • error early when the image write fails (#34) (256989f), closes #33

0.2.1 (2024-05-10)

Bug Fixes

  • cli: completion requires HCLOUD_TOKEN (#19) (bb2ca48)

0.2.0 (2024-05-09)

Features

Bug Fixes

0.1.1 (2024-05-04)

Bug Fixes

  • CLI does not produce release binaries (#3) (f373d4c)

0.1.0 (2024-05-04)

Features

  • cli: docs grouping and version (847b696)
  • cli: hide redundant log attributes (9e65452)
  • cli: upload command (b6ae95f)
  • documentation and cleanup command (c9ab40b)
  • initial library code (4f57df5)
  • log output (904e5e0)

Changelog Library

1.0.1 (2025-05-09)

Bug Fixes

  • timeout while waiting for SSH to become available (#92) (e490b9a)

1.0.0 (2025-05-04)

Features

0.3.1 (2024-12-07)

Bug Fixes

  • cli: local install fails because of go.mod replace (#47) (66dc5f7)

0.3.0 (2024-06-23)

Features

Bug Fixes

  • error early when the image write fails (#34) (256989f), closes #33