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.
The following images are synced to ghcr.io/<owner>/<repo>/<image>:latest (and per-arch tags). The table is updated by the sync workflow.
| Image | Architectures | Vulnerabilities |
|---|---|---|
| alma-10 | amd64 | |
| alma-8 | amd64 | |
| alma-9 | amd64 | |
| centos-10 | amd64 | |
| centos-9 | amd64 | |
| debian-11 | amd64,arm64 | |
| debian-12 | amd64,arm64 | |
| debian-13 | amd64,arm64 | |
| opensuse-15 | amd64 | |
| opensuse-16 | amd64 | |
| rocky-10 | amd64 | |
| rocky-8 | amd64 | |
| rocky-9 | amd64 | |
| ubuntu-2204 | amd64,arm64 | |
| ubuntu-2404 | amd64,arm64 |
To add or change images or architectures, edit config/images.yaml (and the fallback list in scripts/download.sh if not using yq).
With Docker:
docker pull ghcr.io/<owner>/<repo>/debian-13:latestWithout 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:latestFor private GHCR images, use a Personal Access Token with read:packages as the password.
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.
A scheduled workflow runs once a month (1st at 03:00 UTC) and on demand:
- Downloads configured Hetzner bootimages from the mirror (
config/images.yaml) - Imports each tarball as a Docker image per architecture (amd64 / arm64)
- Pushes arch-specific images to GHCR:
ghcr.io/<owner>/<repo>/<image>:<arch> - Creates multi-arch manifest lists:
ghcr.io/<owner>/<repo>/<image>:latest
Run manually: Actions → Sync Hetzner Base Images → Run workflow.
Under Settings → Secrets and variables → Actions:
| Secret | Description |
|---|---|
HETZNER_MIRROR_USER |
Hetzner mirror username |
HETZNER_MIRROR_PASS |
Hetzner mirror password |
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 amd64This produces metal-base:debian-13-amd64 and caches the tarball in dist/.
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.
.
├── .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