Hetzner Images

March 14, 2026 · View on GitHub

Sync base images Scan images

Container registry of standard Hetzner bootimages — downloaded from the Hetzner bootimages mirror and published to GitHub Container Registry (GHCR) as multi-arch images. No custom build step: this repository only syncs official Hetzner base tarballs into a container registry.

You can use them as base images in Docker builds. Use hetzner-download-image.sh to download any Docker image (including custom images you built from these bases) from a registry to a Hetzner root server in rescue mode, in a format the Hetzner installimage script understands.

Images in registry

The following images are synced to ghcr.io/<owner>/<repo>/<image>:latest (and per-arch tags). The table is updated by the sync workflow.

ImageArchitecturesVulnerabilities
alma-10amd64
alma-8amd64
alma-9amd64
centos-10amd64
centos-9amd64
debian-11amd64,arm64
debian-12amd64,arm64
debian-13amd64,arm64
opensuse-15amd64
opensuse-16amd64
rocky-10amd64
rocky-8amd64
rocky-9amd64
ubuntu-2204amd64,arm64
ubuntu-2404amd64,arm64

To add or change images or architectures, edit config/images.yaml (and the fallback list in scripts/download.sh if not using yq).

Pulling images

With Docker:

docker pull ghcr.io/<owner>/<repo>/debian-13:latest

Without Docker (e.g. Hetzner root server in rescue mode):

Use hetzner-download-image.sh to download images as rootfs tarballs for installimage (no Docker daemon required):

curl -sSL https://raw.githubusercontent.com/mystack-cloud/hetzner-download-image.sh/main/get.hetzner-download-image.sh | sh -s
hetzner-download-image.sh -o debian-13.tar.gz /tmp/ ghcr.io/<owner>/<repo>/debian-13:latest
# For private GHCR: hetzner-download-image.sh -u USER:TOKEN ghcr.io/<owner>/<repo>/debian-13:latest

For private GHCR images, use a Personal Access Token with read:packages as the password.

Custom images

To build your own image (custom packages, files, etc.), use one of the images from this registry as the base in a Dockerfile:

FROM ghcr.io/<owner>/<repo>/debian-13:latest
RUN apt-get update && apt-get install -y your-packages
COPY your-files /target/

Build and push with your usual tooling; no build step is provided in this repository.

Base image sync (monthly)

A scheduled workflow runs once a month (1st at 03:00 UTC) and on demand:

  1. Downloads configured Hetzner bootimages from the mirror (config/images.yaml)
  2. Imports each tarball as a Docker image per architecture (amd64 / arm64)
  3. Pushes arch-specific images to GHCR: ghcr.io/<owner>/<repo>/<image>:<arch>
  4. Creates multi-arch manifest lists: ghcr.io/<owner>/<repo>/<image>:latest

Run manually: Actions → Sync Hetzner Base Images → Run workflow.

Required secrets

Under Settings → Secrets and variables → Actions:

SecretDescription
HETZNER_MIRROR_USERHetzner mirror username
HETZNER_MIRROR_PASSHetzner mirror password

Local download (optional)

To download and import a single base image locally (e.g. for testing):

export HETZNER_MIRROR_USER=your-user HETZNER_MIRROR_PASS=your-pass
docker compose run --rm download debian-13 amd64

This produces metal-base:debian-13-amd64 and caches the tarball in dist/.

Security scanning

A weekly workflow (Scan images) runs Trivy against each image in the registry. Findings appear in Security → Code scanning, in each scan job’s summary, and as artifacts (per-image reports, 30-day retention). Schedule: Sundays 04:00 UTC; can also be triggered manually.

Project structure

.
├── .github/workflows/
│   ├── scan-images.yml        # Trivy vulnerability scan (weekly + manual)
│   ├── sync-base-images.yml   # Bootimages → GHCR (monthly + manual)
│   └── update-readme.yml      # Update README table (standalone or after sync)
├── config/
│   └── images.yaml            # Image/arch → Hetzner bootimage filename
├── Dockerfile                 # Tool image for download service (curl, Docker CLI, yq)
├── docker-compose.yml         # download service
├── scripts/
│   ├── download.sh            # Download from Hetzner mirror + docker import
│   └── list.sh                # List image/arch from config (for CI matrix)
└── test/
    └── scripts/
        └── test-prepare.sh     # Test prepare-job logic locally