Self-hosted S3-compatible object storage for backups: MinIO behind an Nginx proxy, with an rclone-based backup agent and versioned buckets. Your offsite backups without a cloud bill.
Self-hosted S3-compatible object storage for backups: MinIO behind an Nginx proxy, with an rclone-based backup agent and versioned buckets. Your offsite backups without a cloud bill.
Metrics, logs, and dashboards in one compose file: Prometheus scraping node-exporter and cAdvisor, Loki with Alloy for log aggregation, and a pre-provisioned Grafana. The stack I use to watch everything else on this site.
A deployment guide with working examples — not a downloadable product. Everything below is the actual configuration I use, abridged to the parts that matter.
services:
prometheus:
image: prom/prometheus:${PROMETHEUS_VERSION:-v2.55.0}
container_name: prometheus
restart: unless-stopped
networks:
- observability
volumes:
- ./config/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.path=/prometheus
- --storage.tsdb.retention.time=${PROMETHEUS_RETENTION:-30d}
- --web.console.libraries=/etc/prometheus/console_libraries
- --web.console.templates=/etc/prometheus/consoles
- --web.enable-lifecycle
logging:
driver: json-file
options:
max-size: 10m
max-file: '3'
labels:
- observability.service=prometheus
grafana:
image: grafana/grafana:${GRAFANA_VERSION:-11.4.0}
container_name: grafana
restart: unless-stopped
networks:
- observability
volumes:
- grafana_data:/var/lib/grafana
- ./config/grafana/datasources:/etc/grafana/provisioning/datasources:ro
- ./config/grafana/dashboards:/etc/grafana/provisioning/dashboards:ro
- ./config/grafana/dashboards-definitions:/var/lib/grafana/dashboards:ro
environment:
- GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER:-admin}
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-admin}
- GF_INSTALL_PLUGINS=${GRAFANA_PLUGINS:-grafana-piechart-panel}
- GF_SERVER_ROOT_URL=${GRAFANA_ROOT_URL:-http://localhost:3000}
- GF_AUTH_ANONYMOUS_ENABLED=${GRAFANA_ANON_AUTH:-false}
logging:
driver: json-file
options:
max-size: 10m
max-file: '3'
labels:
- observability.service=grafana
Excerpt — prometheus, grafana from the compose file. The remaining services (proxies, init jobs, exporters) follow the same pattern and mount their configuration from a config/ directory.
Copy .env.example to .env and at minimum set:
NETWORK_NAME=observability
PROMETHEUS_VERSION=v2.55.0
PROMETHEUS_RETENTION=30d
NODE_EXPORTER_VERSION=v1.8.2
CADVISOR_VERSION=latest
LOKI_VERSION=3.2.0
GRAFANA_ADMIN_PASSWORD in .envGRAFANA_ANON_AUTH=falseremote_write if using long-term storageconfig/loki.ymldocker compose restart after config changesdocker compose down + tar volumesA guide, not a product. This page is deployment documentation with working examples — there is no zip, no download, no support contract. You adapt the patterns to your own environment, and you own the result.
Want this running production-grade in your infrastructure instead? I set this up for teams as the one-day Observability workshop — details on the consulting page.