Prometheus Hetzner Storage Box Exporter
July 15, 2026 Β· View on GitHub
Modern Prometheus exporter for Hetzner Storage Box with comprehensive metrics
Quick Start β’ Metrics β’ Installation β’ Grafana Dashboard β’ Configuration
π Overview
A Prometheus exporter for Hetzner Storage Box using the modern Hetzner Cloud API.
- β 15+ comprehensive metrics - Storage usage, access settings, snapshots, and protection status
- π³ Multi-architecture Docker - Images for amd64 and arm64
- βΈοΈ Kubernetes ready - Pre-built manifests included
- π― Lightweight - Under 50MB memory usage
- π Grafana dashboard - 21-panel dashboard ready to import (download)
- π Bearer token auth - Secure API authentication
Key Features
- All-in-one solution - Includes Docker images, K8s manifests, and Grafana dashboard
- Additional metrics - Access settings (SSH, Samba, WebDAV, ZFS), snapshot plans, and delete protection
- Production ready - Health checks, structured logging, and comprehensive error handling
π Dashboard Preview
π Quick Start
Prerequisites
You need a Hetzner API token with read permissions:
- Log in to Hetzner Cloud Console
- Navigate to Security β API Tokens
- Create a new token with Read permissions
- Copy the token for configuration
Docker Compose (Recommended)
docker-compose up -d
Docker
docker run -d \
--name storagebox-exporter \
-p 9509:9509 \
-e HETZNER_TOKEN="your-api-token" \
ghcr.io/crstian19/prometheus-storagebox-exporter:latest
# With token file (recommended for NixOS)
echo "your-api-token" > /run/secrets/hetzner-token
docker run -d \
--name storagebox-exporter \
-p 9509:9509 \
-v /run/secrets/hetzner-token:/run/secrets/hetzner-token:ro \
-e HETZNER_TOKEN_FILE="/run/secrets/hetzner-token" \
ghcr.io/crstian19/prometheus-storagebox-exporter:latest
π³ Multi-Architecture Docker Images
Our Docker images are built for multiple architectures with automatic platform detection:
# Automatically pulls the right image for your platform:
docker pull ghcr.io/crstian19/prometheus-storagebox-exporter:latest
# Available architectures:
# - Linux amd64 (Intel/AMD)
# - Linux arm64 (ARM 64-bit)
# - macOS amd64 (Intel Mac)
# - macOS arm64 (Apple Silicon)
π·οΈ Available Tags:
latest- Latest release (multi-arch)v0.x.x- Specific version (multi-arch)latest-amd64- Intel/AMD specificlatest-arm64- ARM specific
Binary
# Linux amd64
wget https://github.com/crstian19/prometheus-storagebox-exporter/releases/latest/download/prometheus-storagebox-exporter_linux_x86_64.tar.gz
tar xzf prometheus-storagebox-exporter_linux_x86_64.tar.gz
# Run with environment variable
export HETZNER_TOKEN="your-api-token"
./prometheus-storagebox-exporter
# Or with token file (recommended for NixOS)
echo "your-api-token" > /run/secrets/hetzner-token
export HETZNER_TOKEN_FILE="/run/secrets/hetzner-token"
./prometheus-storagebox-exporter
Access Metrics
Open http://localhost:9509/metrics to view the exported metrics.
βοΈ Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
HETZNER_TOKEN | required | Hetzner API token (mutually exclusive with HETZNER_TOKEN_FILE) |
HETZNER_TOKEN_FILE | optional | Path to file containing Hetzner API token (mutually exclusive with HETZNER_TOKEN) |
LISTEN_ADDRESS | :9509 | Address to listen on |
METRICS_PATH | /metrics | Path for metrics endpoint |
LOG_LEVEL | info | Log level (debug, info, warn, error) |
CACHE_TTL | 0 | Cache TTL in seconds, 0 to disable (default: disabled) |
CACHE_MAX_SIZE | 0 | Cache maximum size in bytes, 0 for unlimited |
CACHE_CLEANUP_INTERVAL | 0 | Cache cleanup interval in seconds, 0 for 10s default |
CACHE_STORAGE_TYPE | memory | Cache storage type (memory, redis) |
Command-line Flags
./prometheus-storagebox-exporter --help
Flags:
--hetzner-token string Hetzner API token (can also be set via HETZNER_TOKEN env var)
--hetzner-token-file string Path to file containing Hetzner API token (can also be set via HETZNER_TOKEN_FILE env var)
--listen-address string Address to listen on for HTTP requests (default ":9509")
--metrics-path string Path under which to expose metrics (default "/metrics")
--log-level string Log level (debug, info, warn, error) (default "info")
--cache-ttl int Cache TTL in seconds, 0 to disable (can also be set via CACHE_TTL env var, default: 0 - disabled)
--cache-max-size int64 Cache maximum size in bytes, 0 for unlimited (can also be set via CACHE_MAX_SIZE env var, default: 0 - unlimited)
--cache-cleanup-interval int Cache cleanup interval in seconds, 0 for default (can also be set via CACHE_CLEANUP_INTERVAL env var, default: 0 - 10s)
--cache-storage-type string Cache storage type (memory, redis) (can also be set via CACHE_STORAGE_TYPE env var, default: memory)
--version Show version information and exit
Cache Configuration (Optional)
β οΈ Cache is disabled by default following Prometheus best practices. Use
scrape_intervalin Prometheus instead of caching for most use cases.
When to Enable Cache
- Multiple Prometheus instances: Prevent duplicate API calls
- Rate limiting concerns: Reduce API request frequency
- Development/Testing: Minimize API calls during testing
Cache Configuration Examples
# Enable cache with 60s TTL, 1MB size limit
export CACHE_TTL=60
export CACHE_MAX_SIZE=1048576
export CACHE_CLEANUP_INTERVAL=30
# Enable with memory limit only (10s cleanup interval)
export CACHE_TTL=120
export CACHE_MAX_SIZE=5242880
# Enable with custom cleanup interval
export CACHE_TTL=300
export CACHE_CLEANUP_INTERVAL=60
Prometheus Configuration (Recommended Alternative)
# prometheus.yml
scrape_configs:
- job_name: 'storagebox-exporter'
scrape_interval: 5m # Control scraping frequency
scrape_timeout: 30s
static_configs:
- targets: ['localhost:9509']
π Metrics
The exporter exposes 15+ metrics organized in 4 categories:
Core Storage Metrics
| Metric | Type | Description | Labels |
|---|---|---|---|
storagebox_disk_quota_bytes | Gauge | Total storage quota in bytes | id, name, server, location |
storagebox_disk_usage_bytes | Gauge | Total used diskspace in bytes | id, name, server, location |
storagebox_disk_usage_data_bytes | Gauge | Diskspace used by files in bytes | id, name, server, location |
storagebox_disk_usage_snapshots_bytes | Gauge | Diskspace used by snapshots in bytes | id, name, server, location |
Information & Status Metrics
| Metric | Type | Description | Labels |
|---|---|---|---|
storagebox_info | Info | Storage box information (value always 1) | id, name, username, server, location, storage_type, system |
storagebox_status | Gauge | Current status (1=active, 0=inactive) | id, name, status |
storagebox_created_timestamp | Gauge | Unix timestamp of creation | id, name |
Access Settings Metrics
| Metric | Type | Description | Labels |
|---|---|---|---|
storagebox_access_ssh_enabled | Gauge | SSH access enabled (1=yes, 0=no) | id, name |
storagebox_access_samba_enabled | Gauge | Samba/CIFS access enabled (1=yes, 0=no) | id, name |
storagebox_access_webdav_enabled | Gauge | WebDAV access enabled (1=yes, 0=no) | id, name |
storagebox_access_zfs_enabled | Gauge | ZFS access enabled (1=yes, 0=no) | id, name |
storagebox_reachable_externally | Gauge | External reachability (1=yes, 0=no) | id, name |
Protection & Snapshot Metrics
| Metric | Type | Description | Labels |
|---|---|---|---|
storagebox_snapshot_plan_enabled | Gauge | Automatic snapshots configured (1=yes, 0=no) | id, name |
storagebox_protection_delete | Gauge | Delete protection status (1=protected, 0=no) | id, name |
Exporter Metrics
| Metric | Type | Description |
|---|---|---|
storagebox_exporter_up | Gauge | Whether the last scrape of the Hetzner API succeeded (1=healthy, 0=unhealthy). On failure, storage box metrics are omitted |
storagebox_exporter_build_info | Gauge | Build information (value always 1). Labels: version, revision, goversion, build_date |
storagebox_exporter_scrape_duration_seconds | Gauge | Duration of the scrape in seconds |
storagebox_exporter_scrape_errors_total | Counter | Total number of scrape errors |
storagebox_exporter_cache_hits_total | Counter | Total number of cache hits (0 when cache disabled) |
storagebox_exporter_cache_misses_total | Counter | Total number of cache misses (increments every scrape when cache disabled) |
Dashboard Features
- π Overview Section: Gauges for disk usage percentage and disk space distribution
- π Time Series Graphs:
- Disk usage over time with quota visualization
- Usage breakdown (Data vs Snapshots) with dual Y-axes
- Disk usage percentage trends
- Storage growth rate analysis (1h intervals)
- π Detailed Table: Complete storage box details with all metrics
- π§ Access Status: Visual indicators for SSH, Samba, WebDAV, and ZFS access
- π‘οΈ Configuration Info: Snapshot plan and delete protection status
- π Multi-box Support: Variable to filter by specific storage box or view all
Quick test with Docker Compose
The repository includes a complete Docker Compose test with pre-configured dashboard:
# Start all services (Exporter + Prometheus + Grafana)
./test-env.sh
# Or manually:
docker-compose -f docker-compose.dev.yml up -d
Access points:
- π― Grafana Dashboard: http://localhost:3000 (admin/admin) - Dashboard is pre-imported
- π Prometheus: http://localhost:9090
- π§ Exporter: http://localhost:9509/metrics
π‘ For production: Import the dashboard manually using grafana-dashboard.json
Dashboard Panels
The dashboard includes:
|
|
Access Settings Panels:
- SSH Access Status
- Samba Access Status
- WebDAV Access Status
- ZFS Access Status
- External Reachability Status
Configuration Panels:
- Storage Box Status
- Snapshot Plan Status
- Delete Protection Status
Details Table:
- Storage Box Details (comprehensive table)
π³ Docker Deployment
Docker Compose
Complete docker-compose.yml example with Prometheus and Grafana:
Click to expand Docker Compose
version: '3.8'
services:
# Storage Box Exporter
storagebox-exporter:
image: ghcr.io/crstian19/prometheus-storagebox-exporter:latest
container_name: storagebox-exporter
restart: unless-stopped
ports:
- "9509:9509"
environment:
- HETZNER_TOKEN=${HETZNER_TOKEN}
# Optional cache configuration (uncomment to enable)
# - CACHE_TTL=60
# - CACHE_MAX_SIZE=1048576
# - CACHE_CLEANUP_INTERVAL=30
networks:
- monitoring
# Prometheus
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/usr/share/prometheus/console_libraries'
- '--web.console.templates=/usr/share/prometheus/consoles'
- '--web.enable-lifecycle'
networks:
- monitoring
depends_on:
- storagebox-exporter
# Grafana
grafana:
image: grafana/grafana:10.2.0
container_name: grafana
restart: unless-stopped
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana-data:/var/lib/grafana
- ./grafana-provisioning:/etc/grafana/provisioning:ro
networks:
- monitoring
depends_on:
- prometheus
networks:
monitoring:
driver: bridge
volumes:
prometheus-data:
grafana-data:
Prometheus Configuration
Add to your prometheus.yml:
scrape_configs:
- job_name: 'hetzner-storagebox'
static_configs:
- targets: ['storagebox-exporter:9509']
scrape_interval: 60s
scrape_timeout: 30s
βΈοΈ Kubernetes Deployment
Quick Deploy
# Apply all manifests
kubectl apply -f k8s/
# Check status
kubectl get pods -n monitoring
Manifests
Click to expand Kubernetes YAML
apiVersion: v1
kind: Secret
metadata:
name: storagebox-exporter-secret
namespace: monitoring
type: Opaque
stringData:
hetzner-token: "your-api-token-here"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: storagebox-exporter
namespace: monitoring
labels:
app: storagebox-exporter
spec:
replicas: 1
selector:
matchLabels:
app: storagebox-exporter
template:
metadata:
labels:
app: storagebox-exporter
spec:
containers:
- name: storagebox-exporter
image: ghcr.io/crstian19/prometheus-storagebox-exporter:latest
ports:
- containerPort: 9509
name: metrics
env:
- name: HETZNER_TOKEN
valueFrom:
secretKeyRef:
name: storagebox-exporter-secret
key: hetzner-token
livenessProbe:
httpGet:
path: /health
port: metrics
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /health
port: metrics
initialDelaySeconds: 5
periodSeconds: 10
resources:
requests:
memory: "32Mi"
cpu: "50m"
limits:
memory: "64Mi"
cpu: "100m"
---
apiVersion: v1
kind: Service
metadata:
name: storagebox-exporter
namespace: monitoring
labels:
app: storagebox-exporter
spec:
type: ClusterIP
ports:
- port: 9509
targetPort: metrics
name: metrics
selector:
app: storagebox-exporter
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: storagebox-exporter
namespace: monitoring
labels:
app: storagebox-exporter
spec:
selector:
matchLabels:
app: storagebox-exporter
endpoints:
- port: metrics
interval: 60s
scrapeTimeout: 30s
ποΈ Development
Setup
The toolchain is pinned with mise (mise.toml) and git hooks are
managed with lefthook. One-time setup per clone:
mise install # installs Go + golangci-lint + goreleaser + ko + lefthook + gitleaks
make setup # installs the git hooks (lefthook install) and downloads deps
Hooks: pre-commit runs gitleaks + gofmt + incremental lint; pre-push runs the full
make test and make lint β the same checks as CI, so failures surface locally first.
Building
# Build binary
make build # or: go build -o prometheus-storagebox-exporter .
# Run tests / linter (same as CI)
make test
make lint
# Container image (no Dockerfile): built with ko via goreleaser on release.
# The Dockerfile is kept only for local `docker build` / docker-compose.
KO_DOCKER_REPO=ko.local ko build . --bare --platform=linux/amd64,linux/arm64
Project Structure
.
βββ main.go # Application entry point
βββ internal/
β βββ collector/ # Prometheus collector implementation
β βββ hetzner/ # Hetzner API client
β βββ config/ # Configuration handling
βββ grafana-provisioning/ # Grafana dashboard provisioning
βββ k8s/ # Kubernetes manifests
βββ .github/workflows/ # CI/CD pipelines
βββ Dockerfile # Multi-stage Docker build
βββ docker-compose.yml # Docker Compose configuration
βββ docker-compose.dev.yml # Development environment
βββ DESIGN.md # Architecture documentation
π Troubleshooting
Common Issues
Error: "HETZNER_TOKEN or HETZNER_TOKEN_FILE environment variable is required"
Make sure you've set either the HETZNER_TOKEN environment variable or the HETZNER_TOKEN_FILE environment variable.
# Option 1: Direct token
export HETZNER_TOKEN="your-token-here"
./prometheus-storagebox-exporter
# Option 2: Token from file (recommended for NixOS)
echo "your-token-here" > /run/secrets/hetzner-token
export HETZNER_TOKEN_FILE="/run/secrets/hetzner-token"
./prometheus-storagebox-exporter
Error: "cannot specify both HETZNER_TOKEN and HETZNER_TOKEN_FILE"
You cannot specify both token methods simultaneously. Choose either HETZNER_TOKEN or HETZNER_TOKEN_FILE, not both.
# Correct: Use only one method
export HETZNER_TOKEN="your-token-here"
# or
export HETZNER_TOKEN_FILE="/path/to/token/file"
Error: "API request failed with status 401"
Your API token is invalid or has expired. Generate a new token from the Hetzner Cloud Console with Read permissions.
Error: "API request failed with status 403"
Your API token doesn't have sufficient permissions. Ensure the token has at least Read permissions.
No metrics appearing in Prometheus
- Check exporter health:
curl http://localhost:9509/health - Check metrics endpoint:
curl http://localhost:9509/metrics - Verify Prometheus configuration
- Check exporter logs:
docker logs storagebox-exporter
Grafana dashboard shows "No data"
- Verify Prometheus is scraping the exporter
- Check the data source URL in Grafana
- Ensure the storage box variable has values
- Check the time range in Grafana
π€ Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Adding New Metrics
- Update the collector in
internal/collector/storagebox.go - Add metric definitions
- Update the Grafana dashboard (grafana-dashboard.json) if needed
- Update this README
π License
This project is licensed under the MIT License - see the LICENSE file for details.
π Credits
Technologies
- Hetzner Storage Box - Backup storage solution
- Hetzner Cloud API - Modern API infrastructure
- Prometheus - Monitoring toolkit and time series database
- Grafana - Analytics and monitoring platform
- Go - Programming language
Inspiration
- fleaz/prometheus-storagebox-exporter - Original implementation
- Prometheus exporter best practices
π Support
- Issues: Report a bug
- Features: Request a feature
- Security: Report a vulnerability
- Documentation: DESIGN.md for architecture details
π Donate
If you find this project useful and want to support its development, you can donate via PayPal:
Your support helps keep this project maintained and improved! π
β If this project helped you, consider giving it a star!
Made with β€οΈ from πͺπΈ for the Prometheus and Hetzner communities