Grafana Loki
Loki is Grafana Labs' log aggregation system. It collects logs from all your machines and containers, indexes only their labels instead of every line, and stores the compressed log data in cheap object storage. You then search it with LogQL, a Prometheus-like query language, mostly through Grafana.
itObservability and performance | OpenSkills.info
Course pathWalk it in order
Look it upDip in anytime
Go furtherLeaves this page
Don't Panic
Don't Panic — Grafana Loki
Loki is a log aggregation system with a strict budget. It keeps every log your infrastructure produces, makes them searchable, and pays for it all by refusing to index the words. Only the labels get indexed; the log lines themselves live, compressed, in the same kind of object storage that holds your backups. That is the entire trick, and the entire product.
The problem it replaced was the full-text index. Systems like Elasticsearch index every field of every line, which is magnificent and expensive: an index roughly as large as the logs, on hardware you provision, patch, and mourn. Loki's designers looked at what people actually type into log search boxes, usually "show me this app's errors in production", and noticed those questions need a handful of labels, not every word. So Loki keeps a small index as a table of contents and greps the matching compressed chunks at query time.
Three ideas do the heavy lifting.
Streams. Every log line gets labels at the moment it is shipped: environment, namespace, app name. Every unique combination of label values is its own stream, stored separately. This is the most consequential design in Loki, and it is also the trap: give in to the temptation to label requests by client IP or status code, and every value becomes its own stream, your index balloons, and your queries crawl. Bounded values only. Everything high-cardinality, trace IDs, pod names, user IDs, goes into structured metadata, which is stored but never indexed.
The pipeline. An agent, Grafana Alloy these days, collects logs and pushes them over HTTP. Inside Loki, a distributor validates and rate-limits, ingesters hold chunks in memory with a write-ahead log for safety, and full chunks flush to object storage. Reads split across queriers that fetch from memory for recent logs and storage for old ones. Each piece scales on its own, or the whole thing runs as one binary on a Raspberry Pi. The same binary, in fact; the difference is a startup flag.
LogQL. The query language is Prometheus's, applied to logs. A selector in braces, then filters: contains this, exclude that, parse the JSON, compare the parsed field. And the trick that makes it feel like a superpower: wrap a log query in count_over_time or rate, and your logs become metrics. Those metrics feed dashboards, and the ruler evaluates them continuously and fires alerts. Logs to metrics to pages, without leaving the log system.
What will surprise you? Two things, and Loki admits both openly. Retention is off by default: without explicitly enabling the compactor, Loki keeps everything forever, which is either a feature or a finance meeting, depending on when you notice. And quiet periods return "no data" rather than zero, a small cruelty that breaks alert rules until you learn the or on() vector(0) incantation.
What Loki is not: a free-text search engine. If your work means searching every word of every log over years, security forensics in particular, Elasticsearch-class systems remain the right tool. Loki's bet is that most log questions start from "which app, which environment", and it wins that bet on price every time.
If you read one more thing, make it the Cheatsheet: the LogQL grammar and the label rules in one screen. The Landscape tab shows the neighbors, Fluent Bit, Elasticsearch, the whole Grafana stack. And the Reference tab starts, pointedly, with the labels page, because in Loki, labels are the whole ballgame.
Where this skill leads
Relevant careers
See how this topic contributes to broader role-level skill maps.
Sources
- https://grafana.com/docs/loki/latest/
Supports
- Loki is a set of open source components composing a fully featured logging stack
- Small index and highly compressed chunks lower cost
- Log data compressed and stored in chunks in object stores (S3, GCS) or local filesystem
- Labels-based indexing model differs from most logging systems
- Structured metadata introduced in Loki 3.0 for high-cardinality values without new streams
- Parsed fields extracted at query time with json, logfmt, pattern, regexp
- ingestion_rate_mb default 4, ingestion_burst_size_mb default 6, per-stream limits
- Too many outstanding requests guidance: split_queries_by_interval, chunk_target_size, max_chunk_age
- Retention handled by the Compactor with retention_enabled, retention_period, delete_request_store
- or on() vector(0) pattern for zero on quiet periods
- Docker Compose datasource URL must use service name, not localhost
- https://grafana.com/docs/loki/latest/get-started/overview/
Supports
- Loki is horizontally scalable, highly available, multi-tenant, inspired by Prometheus
- Loki focuses on logs, collects via push instead of pull
- Log stream definition and label-based indexing
- Three-component stack: agent (Alloy), Loki, Grafana or LogCLI
- Scalability from Raspberry Pi to petabytes per day
- SSD mode deprecated, removed in Loki 4.0, HA monolithic as replacement
- Multi-tenancy with tenant ID isolation
- Efficient storage in compressed chunks; object storage reliability and cost
- LogQL inspired by PromQL
- Ruler component evaluating queries for alerting with Alertmanager
- https://grafana.com/docs/loki/latest/get-started/deployment-modes/
Supports
- Monolithic mode -target=all for volumes up to approximately 20GB/day
- HA monolithic with memberlist, shared store, replication factor 3
- SSD mode deprecated, removed in Loki 4.0
- SSD targets write/read/backend and their component membership
- SSD scales close to a TB of logs per day
- Microservices mode for very large clusters, most complex
- -target flag selects components in the single binary
- -list-targets flag
- https://grafana.com/docs/loki/latest/get-started/architecture/
Supports
- Single object storage backend with index shipper (TSDB or BoltDB)
- Single-store GA with Loki 2.0
- Index as table of contents; chunk as container of one stream's entries
- TSDB recommended; BoltDB deprecated
- Write path steps: distributor receives push, hashes streams, sends to ingesters and replicas, ingester builds chunks, quorum acknowledgment
- Read path steps: query frontend splits sub-queries, scheduler queues, querier pulls from ingesters then store, deduplication, frontend merges
- Multi-tenancy via X-Scope-OrgID header; tenant id fake when disabled
- https://grafana.com/docs/loki/latest/get-started/components/
Supports
- Distributor stateless; validation, label normalization, rate limiting divided across distributors
- Consistent hashing ring via memberlist or KV store (Consul named)
- Replication factor generally 3; quorum floor(rf/2)+1; 2 of 3 writes must succeed
- Replication plus WAL complementary; data loss requires both to fail
- Ingester states PENDING/JOINING/ACTIVE/LEAVING/UNHEALTHY
- Chunks compressed and marked read-only on capacity, age, or flush
- WAL records incoming writes for crash recovery
- Query frontend splitting, queueing, caching of metric results and negative caching of log queries
- Query scheduler per-tenant fairness
- Querier fetches from ingesters and backing store, deduplicates
- Index gateway serves metadata queries for shipper stores
- Compactor merges index files per day/tenant, handles retention and deletion, singleton
- Ruler evaluates rules, remote rule evaluation via query frontend
- Pattern ingester with drain algorithm, disabled by default
- Bloom planner/builder/gateway experimental, use structured metadata
- https://grafana.com/docs/loki/latest/get-started/labels/
Supports
- Labels organize messages into streams; each stream needs at least one label
- Content of log lines not indexed; search iterates within stream
- service_name default label and discover_service_name fallback list
- Default OTel resource attributes as labels with dots replaced by underscores
- 15 index label default limit
- k8s.pod.name and service.instance.id no longer recommended as default labels
- Labeling is iterative; start small
- High cardinality causes huge index and thousands of tiny chunks
- Fewer labels, 10-15 max recommended
- Labels for low-cardinality dimensions; not for log level, message, exception
- Out-of-order writes accepted by default; two-hour window per stream
- Label name regex [a-zA-Z_:][a-zA-Z0-9_:]*; dots converted to underscores
- Cardinality example: 4 actions x 4 status codes = 16 streams from 4 lines
- https://grafana.com/docs/loki/latest/get-started/labels/bp-labels/
Supports
- Static labels good (region, cluster, app, namespace, environment)
- Dynamic labels sparingly; avoid level label in favor of |= "level=error" line filter
- chunk_target_size 1MB guidance; flush metric sum by (reason) (rate(loki_ingester_chunks_flushed_total[1m]))
- Label values bounded; single digits to tens for dynamic labels
- Under 100,000 active streams per tenant; under a million streams in 24 hours for 10 TB/day tenants
- logcli series --analyze-labels example showing requestId cardinality problem
- Use the Series API to inspect streams
- https://grafana.com/docs/loki/latest/get-started/labels/structured-metadata/
Supports
- Structured metadata added in chunk format v4, schema version >= 13, requires tsdb index
- Attach metadata without indexing or including in log line
- Use for high-cardinality values like pod names, process IDs, container IDs, user IDs
- allow_structured_metadata in limits_config; required for OTLP ingestion
- Structured metadata size counts toward ingestion rate limiting
- https://grafana.com/docs/loki/latest/query/
Supports
- LogQL format {stream selector} | log pipeline
- Stream selector mandatory; operators =, !=, =~, !~
- Line filter operators |=, !=, |~, !~
- Parsers json, logfmt, pattern, regexp, unpack
- Format expressions line_format and label_format change only displayed results
- Schema at query time concept
- Log queries vs metric queries
- Grafana Logs Drilldown and Explore; LogCLI as command-line interface
- https://grafana.com/docs/loki/latest/query/metric_queries/
Supports
- Metric queries extend log queries with functions creating metrics from logs
- rate, count_over_time, bytes_rate, bytes_over_time, absent_over_time functions
- Unwrapped range aggregations over extracted labels
- Example sum by (host) (rate({job="mysql"} |= "error" != "timeout" | json | duration > 10s [1m]))
- https://grafana.com/docs/loki/latest/operations/storage/retention/
Supports
- Retention achieved through the Compactor; compactor.retention-enabled not set by default so logs live forever
- Compactor singleton; compaction and retention on independent schedules
- Retention algorithm: per-day tables, mark chunks, update index, async deletion
- Chunks deleted asynchronously by sweeper with retention_delete_delay
- Changes to retention period not retroactive
- Granular per-tenant/per-stream retention supported
- https://grafana.com/docs/loki/latest/operations/storage/logs-deletion/
Supports
- Deletion API requires retention_enabled and delete_request_store
- deletion_mode values disabled, filter-only, filter-and-delete (default)
- delete_request_cancel_period default 24h
- Line-filtered deletions split by delete_max_interval (24h default)
- https://grafana.com/docs/loki/latest/operations/storage/wal/
Supports
- WAL guarantees persistence of acknowledged data across process crashes
- Replay on restart before registering ready
- Two loss scenarios: corrupted/deleted WAL, no space left on disk
- Disk-full protection rejects writes at 90 percent usage; loki_ingester_wal_disk_full_failures_total
- loki_ingester_wal_corruptions_total metric
- Backpressure with ingester.wal-replay-memory-ceiling
- https://grafana.com/docs/loki/latest/operations/storage/schema/
Supports
- schema_config with from dates; new installs use recent past date
- tsdb is the only recommended store; boltdb-shipper deprecated, removed in Loki 4.0
- schema v13 most recent and recommended
- Structured metadata and OTLP require tsdb + schema v13; CONFIG ERROR otherwise
- Changing schema needs a future from date on existing installs
- https://grafana.com/docs/loki/latest/operations/storage/tsdb/
Supports
- TSDB recommended since v2.8; inspired by Prometheus TSDB
- Dynamic query sharding targeting 300-600 MB per shard
- tsdb_max_bytes_per_shard default 600MB
- tsdb_max_query_parallelism default 128
- No index cache needed for TSDB
- https://grafana.com/docs/loki/latest/operations/multi-tenancy/
Supports
- Multi-tenant by default with auth_enabled true
- X-Scope-OrgID header; 401 no org id without it
- Single tenant id fake when disabled
- Tenant IDs up to 150 bytes; 20 bytes usually enough
- Multi-tenant queries with pipe-separated IDs; push and tail reject multi-tenant headers
- __tenant_id__ label filtering
- https://grafana.com/docs/loki/latest/send-data/
Supports
- Grafana Alloy recommended; vendor-neutral OTel Collector distribution
- Native OTLP ingestion over HTTP
- Third-party clients: Docker driver, Fluent Bit, Fluentd, Logstash (discouraged for new deployments)
- Vector Loki sink, language libraries
- https://grafana.com/docs/loki/latest/reference/api/
Supports
- POST /loki/api/v1/push with protobuf (default) or JSON body
- POST /otlp/v1/logs for OTLP
- Query endpoints require valid LogQL
- Push and tail exposed by distributor/write/all components
- https://grafana.com/docs/loki/latest/query/logcli/getting-started/
Supports
- logcli commands: query, instant-query, labels, series, fmt, stats, volume
- --analyze-labels flag for high-cardinality debugging
- LOKI_ADDR, LOKI_USERNAME, LOKI_PASSWORD, LOKI_ORG_ID environment variables
- Default limit 30 entries; --limit flag
- Parallel download flags for large exports
- stats command requires TSDB
- logcli cannot ingest logs
- https://grafana.com/docs/loki/latest/setup/install/
Supports
- Installation methods: Helm (recommended), Tanka, Docker/Docker Compose, local, source
- Loki has no included authentication layer; authenticating reverse proxy required
- General process: install Loki and Alloy, configure, start, verify
- https://grafana.com/docs/loki/latest/setup/install/docker/
Supports
- Docker quick start with loki-local-config.yaml from v3.7.0
- grafana/loki:3.7.0 image, port 3100
- /ready and /metrics verification endpoints
- Image runs as user loki UID 10001
- https://github.com/grafana/loki/releases
Supports
- Release tags and dates: v0.1.0 (2019-06), v1.0.0 (2019-11-20), v2.0.0 (2020-10-26), v2.8.0 (2023-04-04), v3.0.0 (2024-04-08), v3.3.0 (2024-11-19), v3.5.0 (2025-04-22), v3.6.0 (2025-11-17), v3.7.0 (2026-03-26)
- https://github.com/grafana/loki/blob/main/CHANGELOG.md
Supports
- 1.0.0 (2019-11-19) announcement: one year after KubeCon Seattle 2018, ~1.5TB/10 billion lines per day internally at Grafana Labs
- 2.0.0 (2020/10/26) boltdb-shipper production ready, LogQL parsers/filters/formatting, alerts from logs
- https://grafana.com/docs/loki/latest/release-notes/v3-4/
Supports
- Promtail deprecated as code merged into Grafana Alloy (3.0 era deprecations documented in 3.4 release notes)
- BoltDB store deprecation
- https://grafana.com/docs/loki/latest/send-data/otel/
Supports
- Native OTLP ingestion via POST /otlp/v1/logs
- https://grafana.com/docs/alloy/latest/
Supports
- Alloy as vendor-neutral OpenTelemetry Collector distribution with native Loki pipelines
- loki.process stages including stage.structured_metadata
- Migration from Promtail
- https://grafana.com/llms.txt
Supports
- Curated documentation index for Grafana Loki
