A preconfigured, self-hosted observability stack for local development, homelabs, and small teams. Run Prometheus, Loki, Tempo, Alloy, Grafana, and optional Pyroscope in Docker with ready-made dashboards and instrumented examples.
Quick Reference:
make install→make demo→make open→ Explore your data!
One-command path:make bootstrap
Before you begin, ensure you have:
- Docker 20.10+ (Install Docker) or Podman 4.0+ with podman-compose
- Docker Compose v2+ (included with Docker Desktop)
- Make (pre-installed on macOS/Linux, Windows)
- 2GB+ RAM recommended
- curl for health checks (pre-installed on most systems)
Verify your setup:
docker --version # Should be 20.10+ (or podman --version)
docker compose version # Should be v2+
make --version# Clone the repo
git clone https://github.com/matijazezelj/oib.git && cd oib
# Configure credentials (required)
cp .env.example .env
# Edit .env and set a secure GRAFANA_ADMIN_PASSWORD
# Install all stacks
make install
# Verify installation
make healthInstall individual stacks:
make install-logging # Loki + Alloy
make install-metrics # Prometheus + Alloy + cAdvisor
make install-telemetry # Tempo + Alloy
make install-grafana # Unified Grafana with all datasourcesAfter installation, verify everything is working:
# Check health of all services
make health
# Expected output: all services show ✓
# 🏥 Health Check
# Grafana:
# ✓ Grafana is healthy
# Logging:
# ✓ Loki is healthy
# ✓ Alloy (logging) is healthy
# ...Then open http://localhost:3000 (or your configured GRAFANA_PORT) and log in with credentials from your .env file. Grafana opens on the OIB Stack Health dashboard.
After installation, generate some demo data to see everything working:
# Generate sample logs, metrics, and traces
make demo
# Open Grafana in your browser
make openThis creates sample data across all three pillars so you can immediately explore the dashboards.
| Stack | Components | Purpose |
|---|---|---|
| Logging | Loki, Alloy | Centralized log aggregation |
| Metrics | Prometheus, Alloy, cAdvisor, Blackbox Exporter | Metrics collection (host, containers & endpoint probing) |
| Telemetry | Tempo, Alloy | Distributed tracing |
| Profiling | Pyroscope | Continuous profiling (optional) |
| Testing | k6 | Load testing with Prometheus metrics output |
| Grafana | Grafana (unified) | Visualization for all stacks |
After installation, each stack will display integration endpoints:
- Loki API:
http://localhost:3100(localhost only - use from host or oib-network) - Alloy UI:
http://localhost:12345(view pipeline status) - Auto-collection: Alloy automatically collects logs from all Docker containers
From Docker containers: Use
oib-loki:3100onoib-network
- Prometheus:
http://localhost:9090(localhost only) - Alloy Metrics UI:
http://localhost:12347(host metrics pipeline) - cAdvisor:
http://localhost:8080(localhost only)
From Docker containers: Use hostnames like
oib-prometheus:9090onoib-network
- OTLP gRPC:
localhost:4317by default; setOTEL_BIND_ADDR=0.0.0.0to accept traces from LAN clients - OTLP HTTP:
http://localhost:4318by default; setOTEL_BIND_ADDR=0.0.0.0to accept traces from LAN clients - Tempo API:
http://localhost:3200(localhost only)
From Docker containers: Use
oib-alloy-telemetry:4317onoib-network
- Pyroscope:
http://localhost:4040(localhost only) - Install:
make install-profiling
From Docker containers: Use
oib-pyroscope:4040onoib-network
- Grafana UI:
http://localhost:3000by default; setBIND_ADDR=0.0.0.0to expose it on the LAN - Credentials: Set in root
.env(copy from.env.example)
OIB comes with seven ready-to-use dashboards:
| Dashboard | Description |
|---|---|
| System Overview | Container CPU/memory, disk usage, network I/O |
| Host Metrics | Detailed host system metrics (CPU, memory, disk, network) via Alloy |
| Logs Explorer | Log volume, live logs, errors/warnings panel |
| Traces Explorer | TraceQL examples, Python, Node.js, Ruby & PHP code samples |
| Profiles Explorer | CPU, memory, and goroutine profiling with Pyroscope |
| Request Latency | Endpoint probing (Blackbox), k6 load test metrics, latency percentiles |
| Stack Health | Health and ingestion status for the OIB components |
All configuration is managed through a single .env file at the project root.
# Copy the example and customize
cp .env.example .env| Variable | Default | Description |
|---|---|---|
GRAFANA_ADMIN_USER |
admin |
Grafana admin username |
GRAFANA_ADMIN_PASSWORD |
(required) | Grafana admin password |
BIND_ADDR |
127.0.0.1 |
Bind address for Grafana/browser-facing endpoints |
GRAFANA_PORT |
3000 |
Grafana web UI port |
LOKI_PORT |
3100 |
Loki API port (localhost only) |
PROMETHEUS_PORT |
9090 |
Prometheus API port (localhost only) |
ALLOY_METRICS_PORT |
12347 |
Alloy metrics UI port (localhost only) |
CADVISOR_PORT |
8080 |
cAdvisor port (localhost only) |
BLACKBOX_PORT |
9115 |
Blackbox Exporter port (localhost only) |
TEMPO_HTTP_PORT |
3200 |
Tempo HTTP API port (localhost only) |
TEMPO_GRPC_PORT |
9095 |
Tempo gRPC port (localhost only) |
OTEL_BIND_ADDR |
127.0.0.1 |
Bind address for OTLP receivers; use 0.0.0.0 for LAN trace ingestion |
OTEL_GRPC_PORT |
4317 |
OTLP gRPC receiver |
OTEL_HTTP_PORT |
4318 |
OTLP HTTP receiver |
PROMETHEUS_RETENTION_TIME |
15d |
Prometheus data retention time |
PROMETHEUS_RETENTION_SIZE |
5GB |
Prometheus data retention size |
Note: Loki and Tempo retention are configured in their respective config files, not via environment variables.
By default, OIB uses pinned (stable) versions for all images. You can override these in your .env file:
| Variable | Default | Description |
|---|---|---|
GRAFANA_VERSION |
12.3.2 |
Grafana image tag |
LOKI_VERSION |
3.6.5 |
Loki image tag |
ALLOY_VERSION |
v1.13.0 |
Alloy image tag |
PROMETHEUS_VERSION |
v3.9.1 |
Prometheus image tag |
CADVISOR_VERSION |
v0.52.1 |
cAdvisor image tag |
BLACKBOX_VERSION |
v0.28.0 |
Blackbox Exporter image tag |
TEMPO_VERSION |
2.10.0 |
Tempo image tag |
PYROSCOPE_VERSION |
1.18.0 |
Pyroscope image tag |
Quick commands:
# Run all services with :latest images
make latest
# Revert to pinned (stable) versions
make install
# Override a single service version
GRAFANA_VERSION=12.0.0 make update-grafana# Installation
make install # Install all stacks
make install-logging # Install logging stack only
make install-metrics # Install metrics stack only
make install-telemetry # Install telemetry stack only
make install-grafana # Install unified Grafana
# Health & Diagnostics
make health # Quick health check of all services
make doctor # Diagnose common issues (Docker, ports, config)
make status # Show all services with health indicators
make check-ports # Check if required ports are available
make ps # Show running OIB containers
make validate # Validate configuration files
# Load Testing
make test-load # Run k6 basic load test
make test-stress # Run stress test (find breaking point)
make test-spike # Run spike test (sudden traffic)
make test-api # Run API endpoint load test
# Utilities
make open # Open Grafana in browser
make demo # Generate sample data (logs, metrics, traces)
make demo-examples # Run example apps and generate traffic
make bootstrap # Install + demo + open Grafana
make disk-usage # Show disk space used by OIB
make version # Show versions of running components
# Management
make stop # Stop all stacks
make start # Start all stacks
make restart # Restart all stacks
make info # Show integration endpoints
# Logs
make logs # Tail logs from all stacks
make logs-grafana # Tail Grafana logs
make logs-logging # Tail Loki + Alloy logs
make logs-metrics # Tail Prometheus + exporters logs
make logs-telemetry # Tail Tempo + Alloy logs
make logs-profiling # Tail Pyroscope logs
# Maintenance
make update # Pull pinned version images and restart
make update-grafana # Update Grafana only
make update-logging # Update Loki + Alloy
make update-metrics # Update Prometheus + exporters
make update-telemetry # Update Tempo + Alloy
make latest # Pull and run :latest versions of all images
make clean # Remove unused Docker resources
# Cleanup
make uninstall # Remove all stacks and volumes (with confirmation)oib/
├── Makefile # Main entry point
├── README.md
├── .env.example # Environment variables template
├── logging/
│ ├── README.md # Logging stack documentation
│ ├── compose.yaml
│ └── config/
│ ├── loki-config.yml
│ └── alloy-config.alloy
├── metrics/
│ ├── README.md # Metrics stack documentation
│ ├── compose.yaml
│ └── config/
│ ├── prometheus.yml
│ ├── alloy-metrics.alloy # Host metrics via Alloy
│ ├── blackbox.yml # Blackbox exporter probe modules
│ └── rules/ # Alerting rules (future)
├── telemetry/
│ ├── README.md # Telemetry stack documentation
│ ├── compose.yaml
│ └── config/
│ ├── tempo.yaml
│ └── alloy-config.alloy
├── grafana/
│ ├── README.md # Grafana documentation
│ ├── compose.yaml
│ └── provisioning/
│ ├── datasources/
│ │ └── datasources.yml
│ └── dashboards/
│ └── json/
│ ├── system-overview.json
│ ├── host-metrics.json
│ ├── logs-explorer.json
│ ├── traces-explorer.json
│ ├── profiles-explorer.json
│ └── request-latency.json
├── testing/
│ ├── README.md # Load testing documentation
│ ├── compose.yaml # k6 load testing
│ └── scripts/
│ ├── basic-load.js
│ ├── stress-test.js
│ ├── spike-test.js
│ └── api-load.js
└── examples/
├── README.md # Example integration guide
├── python-flask/ # Python Flask example app
├── node-express/ # Node.js Express example app
├── ruby-rails/ # Ruby on Rails example app
└── php-laravel/ # PHP Laravel example app
Edit metrics/config/prometheus.yml to add your services:
scrape_configs:
- job_name: 'my-app'
static_configs:
- targets: ['host.docker.internal:8080']cAdvisor collects container metrics automatically. In the System Overview dashboard, containers are displayed by their short ID (first 12 characters), which matches the output of docker ps. Use docker ps on your host to map IDs to container names.
# In your app's compose.yaml
services:
my-app:
logging:
driver: loki
options:
loki-url: "http://localhost:3100/loki/api/v1/push"
labels: "app"services:
my-app:
labels:
- "prometheus.scrape=true"
- "prometheus.port=8080"# Python example with OpenTelemetry
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
exporter = OTLPSpanExporter(endpoint="localhost:4317", insecure=True)// Node.js example - note: requires insecure credentials for non-TLS
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
const grpc = require('@grpc/grpc-js');
const exporter = new OTLPTraceExporter({
url: 'http://localhost:4317',
credentials: grpc.credentials.createInsecure(),
});💡 See
examples/for complete working Python Flask, Node.js Express, Ruby on Rails, and PHP apps with full observability.
Blackbox Exporter performs synthetic monitoring by probing your endpoints from the outside.
- HTTP/HTTPS endpoint health checks
- TCP port connectivity tests
- ICMP ping, DNS resolution, gRPC health checks
- SSL certificate validation
OIB monitors its own services by default:
- Grafana, Prometheus, Loki, Tempo
Edit metrics/config/prometheus.yml:
- job_name: 'blackbox-http'
static_configs:
- targets:
- http://your-app:8080/health
- https://api.example.com/statusView results in Grafana → OIB - Request Latency dashboard.
Run load tests with metrics streaming to Prometheus:
make test-load # Basic load test
make test-stress # Find breaking point
make test-spike # Sudden traffic spikes
# Test custom target
cd testing
docker compose --profile test run --rm \
-e TARGET_URL=http://your-app:8080 \
k6 run /scripts/basic-load.jsSee testing/README.md for custom test scripts.
All stacks run on a shared Docker network oib-network allowing inter-service communication.
OIB includes safer defaults for local and self-hosted evaluation:
- Required Grafana password: Installation stops until the placeholder in
.envis replaced - Localhost binding: Internal services (Prometheus, Loki, Tempo, etc.) only listen on
127.0.0.1 - Non-privileged containers: cAdvisor uses minimal capabilities instead of privileged mode
- Resource limits: All containers have CPU/memory limits
- No-new-privileges: Containers cannot gain additional privileges
- Non-root users: Example apps run as non-root users
- Docker HEALTHCHECK: All example Dockerfiles include health checks for container orchestration
No browser-facing or ingestion ports are publicly exposed by default. To accept LAN traffic, explicitly set BIND_ADDR=0.0.0.0 for Grafana or OTEL_BIND_ADDR=0.0.0.0 for OTLP, then apply appropriate firewall and TLS controls.
Important
Automatic Docker log collection reads logs from every running container through the Docker socket, not only containers created by OIB. Container logs can contain credentials, tokens, personal data, or other sensitive values. Review logging/config/alloy-config.alloy and your applications' logging practices before enabling OIB on a shared host.
The default single-host configuration is intended for development, homelabs, learning, and evaluation. Review the production hardening guide before considering a production deployment.
Default retention policies (adjust in config files based on storage):
| Component | Retention | Config File |
|---|---|---|
| Loki (logs) | 7 days | logging/config/loki-config.yml |
| Tempo (traces) | 3 days | telemetry/config/tempo.yaml |
| Prometheus (metrics) | 15 days or 5GB | metrics/compose.yaml |
- Persist data: All data is stored in Docker volumes prefixed with
oib- - Resource limits: Adjust memory/CPU limits in docker-compose files for your hardware
- Ports: Default ports can be changed via environment variables (see
.env.example)
Docker is not running
# Check Docker status
docker info
# macOS: Start Docker Desktop from Applications
# Linux: sudo systemctl start dockerPort already in use
# Check which ports are in use
make check-ports
# Find what's using a specific port
lsof -i :3000
# Change ports in .env file
GRAFANA_PORT=3001Services not healthy
# Run diagnostics
make doctor
# Check service logs
make logs-grafana
make logs-logging
make logs-metrics
make logs-telemetry
# Restart services
make restartPermission denied (Linux)
# Add your user to docker group
sudo usermod -aG docker $USER
# Log out and back in, or run:
newgrp dockerTraces not appearing in Tempo
# Verify OTLP endpoint is accessible
curl -v http://localhost:4318/v1/traces
# Check Alloy telemetry logs
make logs-telemetry
# Ensure your app uses correct endpoint:
# - From host: localhost:4317 (gRPC) or localhost:4318 (HTTP)
# - From Docker: oib-alloy-telemetry:4317 on oib-networkLogs not appearing in Loki
# Verify Loki is ready
curl http://localhost:3100/ready
# Check Alloy logging pipeline
curl http://localhost:12345/metrics | grep loki
# Ensure containers are on oib-network for auto-collection
docker network inspect oib-networkHigh memory usage
# Check container resource usage
docker stats
# Reduce retention in config files:
# - logging/config/loki-config.yml: retention_period
# - telemetry/config/tempo.yaml: block_retention
# - metrics/compose.yaml: --storage.tsdb.retention.size- Run
make doctorfor automated diagnostics - Check
make logsfor error messages - Open an issue with the output of
make doctor
PRs welcome! Please follow the existing structure when adding new stacks.
This project is licensed under the MIT License.
Third-party containers and software retain their upstream licenses and are not relicensed under MIT. See NOTICE.md for attribution, trademark, and third-party licensing information.
The Grafana Labs Marks are trademarks of Grafana Labs, and are used with Grafana Labs’ permission. We are not affiliated with, endorsed or sponsored by Grafana Labs or its affiliates.