README
ΒΆ
Prometheus Hetzner Storage Box Exporter
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
Documentation
ΒΆ
There is no documentation for this package.