Skip to content

Modern Prometheus exporter for Hetzner Storage Box with comprehensive metrics

License

Notifications You must be signed in to change notification settings

crstian19/prometheus-storagebox-exporter

Repository files navigation

Prometheus Hetzner Storage Box Exporter

Storage Box Exporter Logo

Modern Prometheus exporter for Hetzner Storage Box with comprehensive metrics

CI Go Report Card License Docker GitHub Release Go Version

Quick StartMetricsInstallationGrafana DashboardConfiguration


📋 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

Dashboard Overview - Click to download

Click on dashboard image to download the JSON file for import


🚀 Quick Start

Prerequisites

You need a Hetzner API token with read permissions:

  1. Log in to Hetzner Cloud Console
  2. Navigate to SecurityAPI Tokens
  3. Create a new token with Read permissions
  4. 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 specific
  • latest-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_interval in 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_scrape_duration_seconds Gauge Duration of the scrape in seconds
storagebox_exporter_scrape_errors_total Counter Total number of scrape errors
storagebox_exporter_up Gauge Exporter health status (1=healthy, 0=unhealthy)
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:

💡 For production: Import the dashboard manually using grafana-dashboard.json

Dashboard Panels

The dashboard includes:

  • Disk Usage Percentage (Gauge)
  • Disk Space Distribution (Pie)
  • Total Quota (Stat)
  • Total Used (Stat)
  • Free Space (Stat)
  • Data Files Usage (Stat)
  • Snapshots Usage (Stat)
  • Snapshot Overhead (Stat)
  • Disk Usage Over Time
  • Usage Breakdown Over Time
  • Disk Usage Percentage Over Time
  • Storage Growth Rate (1h)

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

Building

# Build binary
go build -o prometheus-storagebox-exporter .

# Build Docker image
docker build -t prometheus-storagebox-exporter .

# Run tests
go test -v ./...

# Run linter
golangci-lint run

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
  1. Check exporter health: curl http://localhost:9509/health
  2. Check metrics endpoint: curl http://localhost:9509/metrics
  3. Verify Prometheus configuration
  4. Check exporter logs: docker logs storagebox-exporter
Grafana dashboard shows "No data"
  1. Verify Prometheus is scraping the exporter
  2. Check the data source URL in Grafana
  3. Ensure the storage box variable has values
  4. Check the time range in Grafana

🤝 Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Adding New Metrics

  1. Update the collector in internal/collector/storagebox.go
  2. Add metric definitions
  3. Update the Grafana dashboard (grafana-dashboard.json) if needed
  4. Update this README

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.


🙏 Credits

Technologies

Inspiration


📞 Support

💝 Donate

If you find this project useful and want to support its development, you can donate via PayPal:

Donate

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

About

Modern Prometheus exporter for Hetzner Storage Box with comprehensive metrics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors 3

  •  
  •  
  •