Advanced Observability

SSO, Tracing, Alerting & Long-Term Storage

SSO Alertmanager Tempo Thanos Project F
0

What Are We Building?

Enterprise-grade observability enhancements

Requires: Project A (Authentik) + Project E (Grafana Stack)
Level Up Your Monitoring

Add SSO, distributed tracing, advanced alerting, and unlimited retention!

What We're Adding

Authentik SSO
Login to Grafana with your Authentik account
More Exporters
Metrics for Vault, Authentik, PostgreSQL
Project Dashboards
Visualize A, B, C, D in Grafana
Alertmanager
Advanced routing, grouping, silencing
Tempo Tracing
Distributed request tracing
Thanos Storage
Unlimited metric retention

Enhanced Architecture


Authentik
SSO

Grafana
OIDC

Prometheus
Metrics

Thanos
Sidecar

S3/Minio
Storage

Prometheus
Alerts

Alertmanager
Route

Slack/Email
Notify

Time Investment

Component Time Difficulty
Authentik SSO for Grafana 30-45 min Medium
Additional Exporters 30-45 min Easy
Project Dashboards 45-60 min Medium
Alertmanager Setup 30-45 min Medium
Tempo Tracing 45-60 min Advanced
Thanos Long-Term Storage 60-90 min Advanced

Total: 5-7 hours

1

Authentik SSO for Grafana

Login to Grafana with your Authentik account

Single Sign-On

One login for everything - Grafana, Vault, Teleport, and more!

1
Create OAuth2 Provider in Authentik
5 min
  1. Open Authentik Admin: https://authentik.yourdomain.com/if/admin
  2. Go to Applications Providers Create
  3. Select: OAuth2/OpenID Provider
  4. Fill in:
    Field Value
    Name Grafana OIDC Provider
    Authorization flow default-provider-authorization-implicit-consent
    Client type Confidential
    Client ID grafana
    Redirect URIs https://grafana.yourdomain.com/login/generic_oauth
  5. Click Create and copy the Client Secret
2
Create Application in Authentik
2 min
  1. Go to Applications Applications Create
  2. Fill in:
    Name Grafana
    Slug grafana
    Provider Grafana OIDC Provider
    Launch URL https://grafana.yourdomain.com
  3. Click Create
3
Configure Grafana OIDC
10 min
Update docker-compose.yml
# Update Grafana service in docker-compose.yml

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      # Basic settings
      - GF_SERVER_ROOT_URL=https://grafana.yourdomain.com
      - GF_SERVER_DOMAIN=grafana.yourdomain.com
      
      # Disable default login (optional)
      - GF_AUTH_DISABLE_LOGIN_FORM=false
      - GF_AUTH_DISABLE_SIGNOUT_MENU=false
      
      # OAuth2 / OIDC Configuration
      - GF_AUTH_GENERIC_OAUTH_ENABLED=true
      - GF_AUTH_GENERIC_OAUTH_NAME=Authentik
      - GF_AUTH_GENERIC_OAUTH_CLIENT_ID=grafana
      - GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET=YOUR_CLIENT_SECRET_HERE
      - GF_AUTH_GENERIC_OAUTH_SCOPES=openid profile email
      - GF_AUTH_GENERIC_OAUTH_AUTH_URL=https://authentik.yourdomain.com/application/o/authorize/
      - GF_AUTH_GENERIC_OAUTH_TOKEN_URL=https://authentik.yourdomain.com/application/o/token/
      - GF_AUTH_GENERIC_OAUTH_API_URL=https://authentik.yourdomain.com/application/o/userinfo/
      - GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH=contains(groups[*], 'Grafana Admins') && 'Admin' || contains(groups[*], 'Grafana Editors') && 'Editor' || 'Viewer'
      - GF_AUTH_GENERIC_OAUTH_ALLOW_SIGN_UP=true
      
      # Sign out redirect
      - GF_AUTH_SIGNOUT_REDIRECT_URL=https://authentik.yourdomain.com/application/o/grafana/end-session/
    volumes:
      - ./grafana/data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
    networks:
      - monitoring
Replace Values!

Replace yourdomain.com with your actual domain and YOUR_CLIENT_SECRET_HERE with the secret from Authentik.

4
Create Groups in Authentik (RBAC)
5 min

Create groups in Authentik for role mapping:

  1. Go to Directory Groups Create
  2. Create these groups:
    Group Name Grafana Role
    Grafana Admins Admin (full access)
    Grafana Editors Editor (create dashboards)
    Grafana Viewers Viewer (read-only)
  3. Add yourself to Grafana Admins
5
Restart and Test SSO
3 min
Restart Grafana
cd ~/monitoring

# Restart Grafana
docker compose up -d grafana

# Check logs
docker logs grafana --tail=50
SSO Active!

Open Grafana and click "Sign in with Authentik". You'll be redirected to Authentik for login!

2

Additional Exporters

Collect metrics from all your services

Metrics Everywhere

Monitor Authentik, Vault, PostgreSQL, Traefik, and more!

1
PostgreSQL Exporter
10 min

Monitor your PostgreSQL databases (Authentik, etc.):

Docker Compose Service
# Add to docker-compose.yml

  postgres-exporter:
    image: prometheuscommunity/postgres-exporter:latest
    container_name: postgres-exporter
    restart: unless-stopped
    environment:
      - DATA_SOURCE_NAME=postgresql://username:password@postgres:5432/authentik?sslmode=disable
    ports:
      - "9187:9187"
    networks:
      - monitoring

# Add to prometheus.yml scrape_configs:
  - job_name: 'postgres'
    static_configs:
      - targets: ['postgres-exporter:9187']
2
Blackbox Exporter (HTTP/TCP Probes)
10 min

Monitor endpoint availability and response times:

Blackbox Configuration
# Create blackbox.yml config
cat > ~/monitoring/blackbox/blackbox.yml << 'EOF'
modules:
  http_2xx:
    prober: http
    timeout: 5s
    http:
      valid_http_versions: ["HTTP/1.1", "HTTP/2.0"]
      valid_status_codes: [200, 201, 301, 302]
      method: GET
      follow_redirects: true
      preferred_ip_protocol: "ip4"
  
  tcp_connect:
    prober: tcp
    timeout: 5s
EOF

# Docker Compose service
  blackbox-exporter:
    image: prom/blackbox-exporter:latest
    container_name: blackbox-exporter
    restart: unless-stopped
    ports:
      - "9115:9115"
    volumes:
      - ./blackbox/blackbox.yml:/etc/blackbox_exporter/config.yml:ro
    command:
      - '--config.file=/etc/blackbox_exporter/config.yml'
    networks:
      - monitoring

# Prometheus scrape config for HTTP probes
  - job_name: 'blackbox-http'
    metrics_path: /probe
    params:
      module: [http_2xx]
    static_configs:
      - targets:
          - https://authentik.yourdomain.com
          - https://vault.yourdomain.com
          - https://grafana.yourdomain.com
          - https://teleport.yourdomain.com
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: blackbox-exporter:9115
3
Traefik Metrics
5 min
Enable Traefik Metrics
# Add to traefik.yml or command line:
metrics:
  prometheus:
    buckets:
      - 0.1
      - 0.3
      - 1.2
      - 5.0
    addEntryPointsLabels: true
    addRoutersLabels: true
    addServicesLabels: true
    entryPoint: metrics

entryPoints:
  metrics:
    address: ":8082"

# Prometheus scrape config
  - job_name: 'traefik'
    static_configs:
      - targets: ['traefik:8082']

Exporter Summary

Exporter Monitors Port
node-exporter Linux system (CPU, memory, disk) 9100
cAdvisor Docker containers 8080
postgres-exporter PostgreSQL databases 9187
blackbox-exporter HTTP/TCP endpoints 9115
traefik Reverse proxy metrics 8082
3

Project Dashboards

Visualize all your projects in Grafana

Unified Visibility

One dashboard to rule them all - see Authentik, Vault, Teleport, and Wazuh!

1
Home Lab Overview Dashboard
15 min

Create a master dashboard showing all projects:

Dashboard JSON (Import)
# Create this file and import into Grafana
# Dashboards Import Upload JSON

{
  "title": "Home Lab Overview",
  "uid": "homelab-overview",
  "tags": ["homelab", "overview"],
  "panels": [
    {
      "title": "Service Status",
      "type": "stat",
      "gridPos": { "x": 0, "y": 0, "w": 24, "h": 4 },
      "targets": [{
        "expr": "up{job=~\"blackbox-http\"}",
        "legendFormat": "{{instance}}"
      }],
      "fieldConfig": {
        "defaults": {
          "mappings": [
            { "type": "value", "options": { "0": { "text": "DOWN", "color": "red" } } },
            { "type": "value", "options": { "1": { "text": "UP", "color": "green" } } }
          ]
        }
      }
    },
    {
      "title": "CPU Usage",
      "type": "gauge",
      "gridPos": { "x": 0, "y": 4, "w": 6, "h": 6 },
      "targets": [{
        "expr": "100 - (avg(irate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)"
      }]
    },
    {
      "title": "Memory Usage",
      "type": "gauge",
      "gridPos": { "x": 6, "y": 4, "w": 6, "h": 6 },
      "targets": [{
        "expr": "(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100"
      }]
    },
    {
      "title": "Disk Usage",
      "type": "gauge",
      "gridPos": { "x": 12, "y": 4, "w": 6, "h": 6 },
      "targets": [{
        "expr": "(1 - (node_filesystem_avail_bytes{mountpoint=\"/\"} / node_filesystem_size_bytes{mountpoint=\"/\"})) * 100"
      }]
    },
    {
      "title": "Container Count",
      "type": "stat",
      "gridPos": { "x": 18, "y": 4, "w": 6, "h": 6 },
      "targets": [{
        "expr": "count(container_last_seen{name!=\"\"})"
      }]
    }
  ]
}
2
Authentik Monitoring Dashboard
10 min

Authentik exposes Prometheus metrics natively:

Enable Authentik Metrics
# Add to Authentik docker-compose environment:
AUTHENTIK_METRICS__ENABLED=true

# Prometheus scrape config
  - job_name: 'authentik'
    static_configs:
      - targets: ['authentik-server:9300']

# Key metrics to monitor:
# - authentik_flows_execution_count
# - authentik_outpost_connection_count
# - authentik_policies_execution_time
# - authentik_stages_execution_time
3
HashiCorp Vault Dashboard
10 min
Vault Telemetry Configuration
# Add to vault config (vault-config.hcl):
telemetry {
  prometheus_retention_time = "60s"
  disable_hostname = true
}

# Prometheus scrape config
  - job_name: 'vault'
    metrics_path: '/v1/sys/metrics'
    params:
      format: ['prometheus']
    scheme: https
    tls_config:
      insecure_skip_verify: true
    bearer_token: YOUR_VAULT_TOKEN
    static_configs:
      - targets: ['vault:8200']

# Key metrics:
# - vault_core_unsealed (1 = unsealed)
# - vault_token_count
# - vault_secret_lease_count
# - vault_audit_log_request_count
4
Wazuh Security Dashboard
15 min

Send Wazuh alerts to Loki for correlation:

Wazuh to Loki Integration
# Add Wazuh alerts to Promtail config:

scrape_configs:
  - job_name: wazuh-alerts
    static_configs:
      - targets:
          - localhost
        labels:
          job: wazuh
          __path__: /var/ossec/logs/alerts/alerts.json

    pipeline_stages:
      - json:
          expressions:
            level: rule.level
            description: rule.description
            agent: agent.name
      - labels:
          level:
          agent:

# Sample LogQL queries for Wazuh:
# High severity alerts
{job="wazuh"} | json | level >= 10

# Authentication failures
{job="wazuh"} |= "authentication" |= "failed"
4

Alertmanager

Advanced alert routing and management

Smart Alert Routing

Route critical alerts to Slack, warnings to email, group by service!

1
Deploy Alertmanager
10 min
Docker Compose Service
# Add to docker-compose.yml

  alertmanager:
    image: prom/alertmanager:latest
    container_name: alertmanager
    restart: unless-stopped
    ports:
      - "9093:9093"
    volumes:
      - ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
      - ./alertmanager/data:/alertmanager
    command:
      - '--config.file=/etc/alertmanager/alertmanager.yml'
      - '--storage.path=/alertmanager'
    networks:
      - monitoring
2
Configure Alert Routing
15 min
alertmanager.yml
cat > ~/monitoring/alertmanager/alertmanager.yml << 'EOF'
global:
  resolve_timeout: 5m
  slack_api_url: 'YOUR_SLACK_WEBHOOK_URL'

route:
 receiver: 'default-receiver'
 group_by: ['alertname', 'severity', 'service']
 group_wait: 30s
 group_interval: 5m
 repeat_interval: 4h
 
 routes:
 # Critical alerts -> Slack immediately
 - match:
 severity: critical
 receiver: 'slack-critical'
 group_wait: 10s
 repeat_interval: 1h
 
 # High alerts -> Slack
 - match:
 severity: high
 receiver: 'slack-high'
 
 # Warning alerts -> Email
 - match:
 severity: warning
 receiver: 'email-warnings'
 
 # Security alerts -> Dedicated channel
 - match:
 service: wazuh
 receiver: 'slack-security'

receivers:
 - name: 'default-receiver'
 slack_configs:
 - channel: '#alerts'
 send_resolved: true
 title: '{{ .Status | toUpper }}: {{ .CommonAnnotations.summary }}'
 text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'

 - name: 'slack-critical'
 slack_configs:
 - channel: '#alerts-critical'
 send_resolved: true
 color: '{{ if eq .Status "firing" }}danger{{ else }}good{{ end }}'
 title: ' CRITICAL: {{ .CommonAnnotations.summary }}'
 text: |
 *Alert:* {{ .CommonLabels.alertname }}
 *Description:* {{ .CommonAnnotations.description }}
 *Severity:* {{ .CommonLabels.severity }}
 {{ range .Alerts }}
 *Details:*
 {{ range .Labels.SortedPairs }} - {{ .Name }}: {{ .Value }}
 {{ end }}
 {{ end }}

 - name: 'slack-high'
 slack_configs:
 - channel: '#alerts'
 send_resolved: true
 color: 'warning'
 title: ' HIGH: {{ .CommonAnnotations.summary }}'

 - name: 'slack-security'
 slack_configs:
 - channel: '#security-alerts'
 send_resolved: true
 color: 'danger'
 title: ' Security: {{ .CommonAnnotations.summary }}'

 - name: 'email-warnings'
 email_configs:
 - to: 'alerts@yourdomain.com'
 from: 'alertmanager@yourdomain.com'
 smarthost: 'smtp.gmail.com:587'
 auth_username: 'your-email@gmail.com'
 auth_password: 'your-app-password'
 send_resolved: true

inhibit_rules:
 - source_match:
 severity: 'critical'
 target_match:
 severity: 'warning'
 equal: ['alertname', 'service']
EOF
3
Create Alert Rules
15 min
alert-rules.yml
cat > ~/monitoring/prometheus/alert-rules.yml << 'EOF'
groups:
  - name: infrastructure
    rules:
      # High CPU
      - alert: HighCPU
        expr: 100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
        for: 5m
        labels:
          severity: warning
          service: infrastructure
        annotations:
          summary: "High CPU usage on {{ $labels.instance }}"
          description: "CPU usage is {{ $value | printf \"%.1f\" }}%"

      # Critical CPU
      - alert: CriticalCPU
        expr: 100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 95
        for: 2m
        labels:
          severity: critical
          service: infrastructure
        annotations:
          summary: "Critical CPU on {{ $labels.instance }}"
          description: "CPU at {{ $value | printf \"%.1f\" }}% - immediate attention needed"

      # High Memory
      - alert: HighMemory
        expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
        for: 5m
        labels:
          severity: warning
          service: infrastructure
        annotations:
          summary: "High memory usage on {{ $labels.instance }}"
          description: "Memory usage is {{ $value | printf \"%.1f\" }}%"

      # Disk Almost Full
      - alert: DiskAlmostFull
        expr: (1 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"})) * 100 > 85
        for: 5m
        labels:
          severity: critical
          service: infrastructure
        annotations:
          summary: "Disk almost full on {{ $labels.instance }}"
          description: "Disk usage is {{ $value | printf \"%.1f\" }}%"

  - name: services
    rules:
      # Service Down
      - alert: ServiceDown
        expr: up{job=~"blackbox-http"} == 0
        for: 1m
        labels:
          severity: critical
          service: "{{ $labels.instance }}"
        annotations:
          summary: "Service {{ $labels.instance }} is DOWN"
          description: "The service has been unreachable for more than 1 minute"

      # High Response Time
      - alert: HighResponseTime
        expr: probe_http_duration_seconds{phase="transfer"} > 2
        for: 5m
        labels:
          severity: warning
          service: "{{ $labels.instance }}"
        annotations:
          summary: "Slow response from {{ $labels.instance }}"
          description: "Response time is {{ $value | printf \"%.2f\" }}s"

  - name: containers
    rules:
      # Container Down
      - alert: ContainerDown
        expr: absent(container_last_seen{name=~".+"})
        for: 1m
        labels:
          severity: critical
          service: docker
        annotations:
          summary: "Container {{ $labels.name }} is down"
          description: "Container has stopped running"

      # High Container Memory
      - alert: ContainerHighMemory
        expr: (container_memory_usage_bytes{name!=""} / container_spec_memory_limit_bytes{name!=""}) * 100 > 80
        for: 5m
        labels:
          severity: warning
          service: docker
        annotations:
          summary: "Container {{ $labels.name }} high memory"
          description: "Memory usage is {{ $value | printf \"%.1f\" }}%"
EOF

# Update prometheus.yml to include rules:
rule_files:
  - /etc/prometheus/alert-rules.yml

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['alertmanager:9093']
5

Tempo Distributed Tracing

Track requests across services

End-to-End Visibility

See exactly where time is spent as requests flow through your stack!

1
Deploy Grafana Tempo
15 min
Tempo Configuration
# Create tempo config
mkdir -p ~/monitoring/tempo

cat > ~/monitoring/tempo/tempo.yml << 'EOF'
server:
  http_listen_port: 3200

distributor:
  receivers:
    jaeger:
      protocols:
        thrift_http:
          endpoint: 0.0.0.0:14268
        grpc:
          endpoint: 0.0.0.0:14250
    otlp:
      protocols:
        http:
          endpoint: 0.0.0.0:4318
        grpc:
          endpoint: 0.0.0.0:4317

ingester:
  trace_idle_period: 10s
  max_block_bytes: 1_000_000
  max_block_duration: 5m

compactor:
  compaction:
    compaction_window: 1h
    max_block_bytes: 100_000_000
    block_retention: 48h
    compacted_block_retention: 1h

storage:
  trace:
    backend: local
    local:
      path: /tmp/tempo/blocks
    wal:
      path: /tmp/tempo/wal
EOF

# Docker Compose service
  tempo:
    image: grafana/tempo:latest
    container_name: tempo
    restart: unless-stopped
    command: ["-config.file=/etc/tempo/tempo.yml"]
    ports:
      - "3200:3200"   # Tempo API
      - "4317:4317"   # OTLP gRPC
      - "4318:4318"   # OTLP HTTP
      - "14268:14268" # Jaeger HTTP
    volumes:
      - ./tempo/tempo.yml:/etc/tempo/tempo.yml:ro
      - ./tempo/data:/tmp/tempo
    networks:
      - monitoring
2
Add Tempo Data Source to Grafana
5 min
Grafana Datasource Config
# Add to datasources.yml

  - name: Tempo
    type: tempo
    access: proxy
    url: http://tempo:3200
    editable: false
    jsonData:
      httpMethod: GET
      tracesToLogs:
        datasourceUid: loki
        tags: ['job', 'instance']
        mappedTags: [{ key: 'service.name', value: 'service' }]
        mapTagNamesEnabled: true
        filterByTraceID: true
      tracesToMetrics:
        datasourceUid: prometheus
        tags: [{ key: 'service.name', value: 'service' }]
      serviceMap:
        datasourceUid: prometheus
      nodeGraph:
        enabled: true
Tracing Requires Instrumentation

Your applications need to send traces to Tempo. Use OpenTelemetry SDKs or Jaeger clients to instrument your code.

6

Thanos Long-Term Storage

Unlimited metric retention with object storage

Store Metrics Forever

Keep years of metrics in cheap object storage (S3/MinIO)

1
Deploy MinIO (Object Storage)
10 min
MinIO Docker Service
# Add MinIO to docker-compose.yml

  minio:
    image: minio/minio:latest
    container_name: minio
    restart: unless-stopped
    ports:
      - "9000:9000"
      - "9001:9001"
    environment:
      - MINIO_ROOT_USER=minioadmin
      - MINIO_ROOT_PASSWORD=YOUR_SECURE_PASSWORD
    command: server /data --console-address ":9001"
    volumes:
      - ./minio/data:/data
    networks:
      - monitoring

# Create bucket for Thanos
# Access MinIO console at http://SERVER_IP:9001
# Create bucket named "thanos"
2
Deploy Thanos Sidecar
15 min
Thanos Sidecar Configuration
# Create bucket config
cat > ~/monitoring/thanos/bucket.yml << 'EOF'
type: S3
config:
  bucket: thanos
  endpoint: minio:9000
  access_key: minioadmin
  secret_key: YOUR_SECURE_PASSWORD
  insecure: true
EOF

# Update Prometheus to allow Thanos access
# Add to prometheus command:
  prometheus:
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--storage.tsdb.retention.time=2h'  # Short retention, Thanos handles long-term
      - '--storage.tsdb.min-block-duration=2h'
      - '--storage.tsdb.max-block-duration=2h'
      - '--web.enable-lifecycle'
      - '--web.enable-admin-api'

# Add Thanos Sidecar
  thanos-sidecar:
    image: quay.io/thanos/thanos:latest
    container_name: thanos-sidecar
    restart: unless-stopped
    command:
      - sidecar
      - --tsdb.path=/prometheus
      - --prometheus.url=http://prometheus:9090
      - --objstore.config-file=/etc/thanos/bucket.yml
      - --grpc-address=0.0.0.0:10901
      - --http-address=0.0.0.0:10902
    volumes:
      - ./prometheus/data:/prometheus:ro
      - ./thanos/bucket.yml:/etc/thanos/bucket.yml:ro
    depends_on:
      - prometheus
      - minio
    networks:
      - monitoring
3
Deploy Thanos Query & Store
15 min
Thanos Query & Store Services
# Add to docker-compose.yml

  thanos-store:
    image: quay.io/thanos/thanos:latest
    container_name: thanos-store
    restart: unless-stopped
    command:
      - store
      - --data-dir=/tmp/thanos/store
      - --objstore.config-file=/etc/thanos/bucket.yml
      - --grpc-address=0.0.0.0:10901
      - --http-address=0.0.0.0:10902
    volumes:
      - ./thanos/bucket.yml:/etc/thanos/bucket.yml:ro
      - ./thanos/store:/tmp/thanos/store
    depends_on:
      - minio
    networks:
      - monitoring

  thanos-query:
    image: quay.io/thanos/thanos:latest
    container_name: thanos-query
    restart: unless-stopped
    ports:
      - "10902:10902"
    command:
      - query
      - --http-address=0.0.0.0:10902
      - --grpc-address=0.0.0.0:10901
      - --store=thanos-sidecar:10901
      - --store=thanos-store:10901
    depends_on:
      - thanos-sidecar
      - thanos-store
    networks:
      - monitoring

  thanos-compactor:
    image: quay.io/thanos/thanos:latest
    container_name: thanos-compactor
    restart: unless-stopped
    command:
      - compact
      - --data-dir=/tmp/thanos/compact
      - --objstore.config-file=/etc/thanos/bucket.yml
      - --wait
    volumes:
      - ./thanos/bucket.yml:/etc/thanos/bucket.yml:ro
      - ./thanos/compact:/tmp/thanos/compact
    depends_on:
      - minio
    networks:
      - monitoring
4
Point Grafana to Thanos
5 min
Update Grafana Datasource
# Update datasources.yml - change Prometheus URL to Thanos

  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://thanos-query:10902  # Changed from prometheus:9090
    isDefault: true
    editable: false
    jsonData:
      httpMethod: POST
      manageAlerts: true
      prometheusType: Thanos
      prometheusVersion: 0.31.0
Unlimited Retention!

Metrics are now stored in MinIO/S3. Query years of data seamlessly through Grafana!

7

Testing & Verification

Verify all enhancements work

Final Verification

Test every component of your advanced observability stack

Verification Checklist

  • SSO: Login to Grafana via Authentik
  • SSO: Correct role assigned (Admin/Editor/Viewer)
  • Exporters: All new targets UP in Prometheus
  • Dashboards: Home Lab Overview displays data
  • Alertmanager: Test alert routes to correct channel
  • Tempo: Traces visible in Grafana Explore
  • Thanos: Query returns data older than 2 hours
  • MinIO: Thanos bucket has data
Quick Health Checks
Test Commands
# Check all services
docker compose ps

# Test Alertmanager
curl http://localhost:9093/-/healthy

# Test Tempo
curl http://localhost:3200/ready

# Test Thanos Query
curl http://localhost:10902/-/healthy

# Check Thanos stores
curl http://localhost:10902/api/v1/stores

# Test MinIO
curl http://localhost:9000/minio/health/live

# Send test alert
curl -X POST http://localhost:9093/api/v1/alerts \
  -H "Content-Type: application/json" \
  -d '[{"labels":{"alertname":"TestAlert","severity":"warning"},"annotations":{"summary":"Test alert"}}]'

Congratulations!

Enterprise-grade observability achieved!

Advanced Observability Expert

You've built a production-ready monitoring stack with SSO, tracing, and unlimited retention!

Skills Demonstrated

  • OIDC/OAuth2 SSO integration
  • Multi-exporter Prometheus deployment
  • Custom dashboard creation
  • Advanced alert routing with Alertmanager
  • Distributed tracing with Tempo
  • Long-term storage with Thanos
  • Object storage (S3/MinIO)

Complete Portfolio Summary

Project Component Skills
A Authentik SSO Identity, MFA, OIDC/SAML
B HashiCorp Vault Secrets, JIT Access, Dynamic Creds
C Teleport ZTNA, SSH Certificates, RBAC
D Wazuh SIEM Security Monitoring, XDR, Compliance
E Grafana Stack Metrics, Logs, Dashboards
F Advanced Observability SSO, Tracing, Long-Term Storage