An open API service indexing awesome lists of open source software.

https://github.com/crstian19/prometheus-storagebox-exporter

Modern Prometheus exporter for Hetzner Storage Box with comprehensive metrics
https://github.com/crstian19/prometheus-storagebox-exporter

Last synced: 4 months ago
JSON representation

Modern Prometheus exporter for Hetzner Storage Box with comprehensive metrics

Awesome Lists containing this project

README

          

# Prometheus Hetzner Storage Box Exporter

Storage Box Exporter Logo

**Modern Prometheus exporter for Hetzner Storage Box with comprehensive metrics**

![CI](https://img.shields.io/github/actions/workflow/status/crstian19/prometheus-storagebox-exporter/ci.yml?label=CI&logo=github&logoColor=white&style=for-the-badge&branch=main)
![Go Report Card](https://goreportcard.com/badge/github.com/crstian19/prometheus-storagebox-exporter?style=for-the-badge&logo=go&logoColor=white)
![License](https://img.shields.io/github/license/crstian19/prometheus-storagebox-exporter?style=for-the-badge&logo=unlicense&logoColor=white)
![Docker](https://img.shields.io/badge/docker-ghcr.io-blue?style=for-the-badge&logo=docker&logoColor=white)
![GitHub Release](https://img.shields.io/github/release/crstian19/prometheus-storagebox-exporter?style=for-the-badge&logo=github&logoColor=white&label=release)
![Go Version](https://img.shields.io/github/go-mod/go-version/crstian19/prometheus-storagebox-exporter?style=for-the-badge&logo=go&logoColor=white&label=go)

[Quick Start](#-quick-start) β€’ [Metrics](#-metrics) β€’ [Installation](#-installation) β€’ [Grafana Dashboard](#-grafana-dashboard) β€’ [Configuration](#%EF%B8%8F-configuration)

---

## πŸ“‹ Overview

A Prometheus exporter for [Hetzner Storage Box](https://www.hetzner.com/storage/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](grafana-provisioning/dashboards/grafana-dashboard.json))
- πŸ”’ **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](https://console.hetzner.cloud/)
2. Navigate to **Security** β†’ **API Tokens**
3. Create a new token with **Read** permissions
4. Copy the token for configuration

### Docker Compose (Recommended)

```bash
docker-compose up -d
```

### Docker

```bash
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:

```bash
# 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

```bash
# 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

```bash
./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

```bash
# 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)

```yaml
# 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:

```bash
# 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](grafana-provisioning/dashboards/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

```yaml
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`:

```yaml
scrape_configs:
- job_name: 'hetzner-storagebox'
static_configs:
- targets: ['storagebox-exporter:9509']
scrape_interval: 60s
scrape_timeout: 30s
```

---

## ☸️ Kubernetes Deployment

### Quick Deploy

```bash
# Apply all manifests
kubectl apply -f k8s/

# Check status
kubectl get pods -n monitoring
```

### Manifests

Click to expand Kubernetes YAML

```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

```bash
# 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.

```bash
# 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.

```bash
# 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](grafana-provisioning/dashboards/grafana-dashboard.json)) if needed
4. Update this README

---

## πŸ“„ License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

---

## πŸ™ Credits

### Technologies
- [Hetzner Storage Box](https://www.hetzner.com/storage/storage-box) - Backup storage solution
- [Hetzner Cloud API](https://docs.hetzner.cloud/) - Modern API infrastructure
- [Prometheus](https://prometheus.io/) - Monitoring toolkit and time series database
- [Grafana](https://grafana.com/) - Analytics and monitoring platform
- [Go](https://golang.org/) - Programming language

### Inspiration
- [fleaz/prometheus-storagebox-exporter](https://github.com/fleaz/prometheus-storagebox-exporter) - Original implementation
- [Prometheus exporter best practices](https://github.com/prometheus/client_golang)

---

## πŸ“ž Support

- **Issues**: [Report a bug](https://github.com/crstian19/prometheus-storagebox-exporter/issues)
- **Features**: [Request a feature](https://github.com/crstian19/prometheus-storagebox-exporter/issues/new?template=feature_request.md)
- **Security**: [Report a vulnerability](https://github.com/crstian19/prometheus-storagebox-exporter/security)
- **Documentation**: [DESIGN.md](DESIGN.md) for architecture details

## πŸ’ Donate

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

[![Donate](https://img.shields.io/badge/Donate-PayPal-blue?style=for-the-badge&logo=paypal&logoColor=white)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=MUW2XFMQB2782)

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