· loki / elasticsearch / logging

Loki vs Elasticsearch: Which Log Stack Should You Pick?

Loki cuts storage costs by indexing only labels; Elasticsearch indexes everything for instant full-text search. Pragmatic decision guide for dev teams in 2026.

By · Updated May 26, 2026

2,784 words · 14 min read

Pick Loki if you run Kubernetes or Docker workloads, already use Prometheus and Grafana, and want the cheapest possible log storage on S3 or GCS without operating a complex cluster. Pick Elasticsearch if you need ad-hoc full-text search across every field of every log line, run security or compliance workloads, or need rich analytics beyond “show me pod X’s errors.”

Who this is for

SREs, DevOps engineers, and backend developers choosing a log aggregation stack for Kubernetes or Docker workloads who have heard of both Loki and Elasticsearch (or the ELK stack) but need a clear cost/complexity/capability breakdown to justify the choice to their team.

Version pins

All findings below are based on:

ToolVersionNotes
Grafana Loki3.5.5September 11, 2025 patch release (release)
Elasticsearch8.19 / 9.x8.19 is the final 8.x minor (July 2025); 9.x is the current line
Grafana11.xVisualization layer for Loki
Kibana8.19 / 9.xVisualization layer for Elasticsearch
Grafana AlloylatestReplaces Promtail as the Loki collection agent (Promtail EOL: March 2, 2026)

Source verification: Loki 3.5 release notes — grafana.com/docs/loki/latest/release-notes/v3-5/; Elasticsearch 8.19 EoL note — elastic.co/subscriptions.


Why this choice matters now

Log volume scales with container count. A 50-node Kubernetes cluster can easily push 50–500 GB/day of logs. At those volumes, the difference between “index everything” (Elasticsearch) and “index only labels” (Loki) translates directly into cloud spend and operational burden — often by an order of magnitude.

Loki crossed the CNCF Graduated threshold and reached 3.x in 2024, with architecture changes significant enough that comparisons written against Loki 2.x are largely stale. The key 3.x shifts:

  • Promtail → Grafana Alloy: unified telemetry agent merging metrics, logs, traces, and profiles in one binary. Promtail enters LTS Feb 2025, EOL March 2026. (InfoQ: Grafana Loki 3.4)
  • TSDB index format (recommended over BoltDB): Prometheus-derived time-series indexing that unlocks new Loki query features.
  • Thanos Object Storage Client integration (3.4): aligns Loki’s storage config with Grafana Mimir and Pyroscope.
  • Out-of-order log ingestion (3.4+): time_sharding_enabled: true accepts logs from any time period, removing a major operational headache.

Architecture comparison: how each indexes logs

Loki — label-based, chunk-compressed

Loki’s design principle: don’t index log content, only index metadata.

Every log stream is identified by a unique set of labels (e.g. {namespace="prod", app="api", pod="api-7d9f4b-xyz"}). Loki stores log lines for a given label set as chunks — compressed, block-level containers — in object storage (S3, GCS, Azure Blob, or local filesystem). The index is a tiny “table of contents” that maps label sets to chunk locations.

Write path: Grafana Alloy scrapes logs, attaches labels, and pushes to the Loki Distributor. The Distributor hashes the stream to route it to the right Ingester, which buffers chunks in memory. Chunks are flushed to object storage and a TSDB index is written when full or on a schedule.

Read path: The Query Frontend splits a LogQL query into sub-queries and sends them to Queriers. Queriers first check in-memory Ingesters, then lazily load chunks from object storage, decompress, and scan with regex/filter. Results are deduplicated and merged.

Three deployment modes (Loki architecture docs):

ModeSuitable forNotes
Single binaryDev, small clusters (<100 GB/day)One process, easiest ops
Simple scalableMedium (100 GB–1 TB/day)Read/Write/Backend split
MicroservicesLarge-scale productionEach component scales independently

Key constraint: Loki is not designed for high-cardinality labels. Using pod IPs, request IDs, or user IDs as labels causes “cardinality explosion” — thousands of tiny chunks, bloated index, degraded performance. The default cardinality limit is 100,000 streams per tenant; real workloads hit this when teams don’t follow labeling discipline. (GitHub issue #2863, Loki cardinality docs)

Elasticsearch — inverted index, full-text searchable

Elasticsearch uses an inverted index: every word in every log line is indexed, creating a term-to-document mapping. Any field is instantly queryable — no label predefinition required.

Write path: Logstash (or Elastic Agent / Beats) parses and transforms log lines, then ships them to Elasticsearch. Each document is parsed as JSON, fields are extracted, and an inverted index is built per shard. Index shards are stored on local NVMe/SSD for hot-tier performance.

Read path: A KQL or Lucene query hits a coordinating node, which fans out to relevant shards. Each shard searches its local index in parallel. Results are merged and returned.

Index Lifecycle Management (ILM): Elasticsearch automates moving indices through hot → warm → cold → frozen → delete phases based on age or size. This enables cost tiering: recent logs on fast NVMe, older logs on cheaper spinning disk or frozen snapshot storage. (Elastic ILM docs)

Elasticsearch 8.19 / 9.x log feature: Streams adds automatic field extraction in Kibana and handles ILM configuration automatically — a meaningful reduction in initial setup friction. The feature is still evolving across the 9.x line; check the Elastic release notes for the current status.


Performance & cost

Storage footprint

Elasticsearch’s inverted index structures often exceed the raw log size because every term is indexed. Loki stores compressed chunks without a full-text index, which keeps on-disk size substantially smaller. Teams migrating from ELK to Loki consistently report a significant reduction in storage footprint, though exact ratios depend heavily on log verbosity, field cardinality, and compression settings. (Plural.sh comparison)

The practical gap widens at scale. A Kubernetes cluster generating hundreds of GB/day will see a large difference in storage requirements between an Elasticsearch cluster — which needs local SSD for the hot tier — and Loki backed by object storage at commodity S3/GCS rates.

Query latency

  • Label-filtered queries (“show me all errors from the api pod in the last hour”): Loki wins — it goes directly to the right chunk set via the index.
  • Ad-hoc full-text queries (“find every log line containing ‘NullPointerException’ across all services”): Elasticsearch wins — the inverted index returns results immediately; Loki must load and grep every chunk that might match.

Resource footprint

Elasticsearch production minimums (Opster hardware guide):

  • 16 GB+ RAM per node (50% to JVM heap, capped at ~30 GB)
  • 4+ CPU cores per node
  • Fast SSD for hot tier

Loki’s ingestion path is lightweight. The Ingester is the most resource-intensive component, handling thousands of streams per GB of RAM. Object storage handles durability; Loki nodes are stateless and can run on commodity instances.

Cloud-managed pricing

Grafana Cloud (grafana.com/pricing):

TierIncludedPer-GB cost (above free)
Free50 GB/month, 14-day retention
ProFirst 50 GB included, 30-day retention$0.40/GB write + $0.05/GB processing + $0.10/GB/month retention

Elastic Cloud (Observability Serverless) (elastic.co/pricing/serverless-observability) — effective Nov 1, 2025:

TierIngestionRetention
Logs Essentialsfrom $0.07/GBfrom $0.017/GB/month
Completefrom $0.09/GBfrom $0.019/GB/month

Self-hosted TCO: Teams migrating from self-managed Elasticsearch to Loki on Kubernetes + S3 frequently report substantial cost reductions — primarily because Loki eliminates the need for high-memory, high-IOPS nodes for log storage. (Plural.sh comparison)


Ops complexity

Setup

Loki (Helm, Kubernetes):

helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
helm install loki grafana/loki-stack \
  --set loki.persistence.enabled=true \
  --set loki.persistence.size=10Gi

Single binary mode works for most teams getting started. Add Grafana Alloy as the collection agent (required in Loki 3.x — Promtail is EOL March 2026).

Elasticsearch (ECK operator, Kubernetes):

Note: ECK releases frequently. The commands below use v3.4.0 (current as of May 2026). Check the ECK quickstart for the latest version before installing.

# Install ECK operator
kubectl create -f https://download.elastic.co/downloads/eck/3.4.0/crds.yaml
kubectl apply -f https://download.elastic.co/downloads/eck/3.4.0/operator.yaml

# Deploy via Helm
helm repo add elastic https://helm.elastic.co
helm install es-quickstart elastic/eck-stack \
  -n elastic-stack --create-namespace \
  --set=eck-kibana.enabled=true

Requires managing persistent volumes with fast storage for the hot tier. ILM policies need upfront configuration for production.

Alerting

Loki has a native Ruler component (Loki alerting docs) that evaluates LogQL expressions continuously and fires alerts via Alertmanager — identical workflow to Prometheus. No additional tools required. Supports:

  • LogQL-based alerting rules
  • Recording rules (precomputed metrics written to Prometheus/Mimir/Thanos)
  • Horizontal scaling via hash ring

Elasticsearch alerting requires Kibana’s Watcher (X-Pack, included in Basic license) or the newer Kibana Alerts & Rules UI. Full alerting features (action connectors for PagerDuty, Slack, etc.) are available from the free Basic tier. Advanced ML-based anomaly alerting requires Platinum/Enterprise license. (Elastic subscriptions)

Dashboards

  • Loki: Grafana is the native UI. Deep integration: Explore view, logs-to-metrics correlation, unified dashboards mixing Prometheus metrics and Loki logs.
  • Elasticsearch: Kibana. More powerful for ad-hoc data exploration, Lens-based visualizations, Canvas for custom reports. Less tight coupling with metrics (though Elastic Observability bundles APM + metrics + logs).

Cardinality discipline (Loki-specific operational concern)

The biggest day-2 pain with Loki: cardinality. Teams that label logs with high-cardinality values (user IDs, IP addresses, request IDs) hit the default 100,000-stream limit and experience memory spikes, slow queries, and chunk storms. (GitHub issue #2863, cardinality docs)

Mitigation: Keep label sets small and static. Use Grafana Alloy’s relabeling rules to drop or hash high-cardinality values before they reach Loki. Put everything else in the log line itself and use LogQL filter expressions.


Quick-reference comparison table

DimensionLoki 3.5Elasticsearch 8.19/9.x
Index modelLabels only (metadata)Full-text inverted index
Query languageLogQLKQL / Lucene
Storage backendObject storage (S3, GCS)Local SSD / NVMe + optional tiering
Storage costSubstantially smaller (compressed chunks, no full-text index)Often exceeds raw log size (inverted index overhead)
Self-hosted RAM (prod)Low (stateless, commodity)16 GB+ per node
Ad-hoc full-text searchSlow (chunk scan)Fast (inverted index)
Label-filtered queriesFastFast (if indexed)
High-cardinality supportPoor (design limit)Excellent
AlertingNative (Ruler + LogQL)Native (Watcher, Kibana Rules)
VisualizationGrafanaKibana
Kubernetes-nativeYes (Alloy DaemonSet)Yes (Elastic Agent / Beats)
Managed cloudGrafana Cloud (50 GB free)Elastic Cloud (Serverless, pay-per-use)
Open-source licenseAGPLv3SSPL / Elastic License 2.0

When to choose Loki

  • You already use Prometheus + Grafana — Loki slots in without new tooling. For teams rounding out their observability stack, pairing Loki with an HTTP check is common; see Best Uptime Monitor 2026 for options that integrate with Grafana alerting.
  • Your queries are label-scoped: “show errors from namespace=prod, app=payments in the last 2h.” That covers 90% of operational troubleshooting.
  • You run Kubernetes and want DaemonSet-based collection with Grafana Alloy — labels from pod metadata (namespace, app, pod name) map cleanly to Loki’s model.
  • Cost is a constraint: object storage logs at S3 prices vs. SSD-backed Elasticsearch nodes is a large TCO difference at scale.
  • You are starting greenfield: Loki single-binary mode is operational in minutes.
  • You want Grafana Cloud’s free tier: 50 GB/month included — enough for small to medium projects.

When to choose Elasticsearch

  • You need ad-hoc full-text search: “find every occurrence of ‘out of memory’ across all services without knowing which namespace or pod.” No label predefinition required.
  • You run security / SIEM workloads: threat hunting, forensics, and regulatory compliance often require querying arbitrary fields after the fact. The ELK stack has a mature security ecosystem (Elastic SIEM, Elastic Security). If you are also evaluating APM and error tracking alongside your log stack, Sentry vs Datadog covers the trade-offs — Datadog includes log management as part of its full-stack observability suite.
  • You need advanced analytics: Kibana’s Lens visualizations, aggregations, and Elasticsearch’s aggregation framework go well beyond what Grafana’s log panel supports.
  • Your log format is unstructured and variable across many services: Logstash’s parsing pipelines handle complex multi-format ingestion more gracefully than Alloy’s relabeling rules.
  • You already have an existing ELK investment: migrating to Loki isn’t free. If your team knows Kibana and KQL, the operational cost of switching may outweigh storage savings.
  • You need fine-grained retention per index with automated tier migration: ILM policies give precise control over hot/warm/cold/frozen/delete transitions.

Quick-start: Loki on Kubernetes (Helm)

# 1. Add Grafana Helm repo
helm repo add grafana https://grafana.github.io/helm-charts && helm repo update

# 2. Install Loki stack (Loki + Grafana Alloy as collector)
helm install loki grafana/loki-stack \
  --set loki.persistence.enabled=true \
  --set loki.persistence.size=50Gi \
  --set grafana.enabled=true

# 3. Get Grafana admin password
kubectl get secret loki-grafana -o jsonpath="{.data.admin-password}" | base64 --decode

# 4. Port-forward Grafana and log in
kubectl port-forward svc/loki-grafana 3000:80

# 5. In Grafana → Explore → select Loki datasource → query:
# {namespace="default"} |= "error"

For production: switch to grafana/loki chart in Simple Scalable mode and configure an S3 bucket as the storage backend. Point to Loki sizing guidance for CPU/memory estimates by ingestion volume.


Quick-start: Elasticsearch + Kibana on Kubernetes (ECK)

Note: ECK releases frequently. The commands below use v3.4.0 (current as of May 2026). Check the ECK quickstart for the latest version before installing.

# 1. Install ECK CRDs and operator
kubectl create -f https://download.elastic.co/downloads/eck/3.4.0/crds.yaml
kubectl apply -f https://download.elastic.co/downloads/eck/3.4.0/operator.yaml

# 2. Create a minimal Elasticsearch cluster (1 node, dev use)
cat <<EOF | kubectl apply -f -
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 8.19.0
  nodeSets:
  - name: default
    count: 1
    config:
      node.store.allow_mmap: false
EOF

# 3. Install Kibana
cat <<EOF | kubectl apply -f -
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: quickstart
spec:
  version: 8.19.0
  count: 1
  elasticsearchRef:
    name: quickstart
EOF

# 4. Get Elasticsearch credentials and access Kibana
kubectl get secret quickstart-es-elastic-user \
  -o=jsonpath='{.data.elastic}' | base64 --decode
kubectl port-forward svc/quickstart-kb-http 5601

# 5. Deploy Elastic Agent as DaemonSet for log collection
# (via Fleet in Kibana, or via standalone manifest)

For production: add persistent volume claims with fast storage, configure ILM policies, and enable multi-node topology with dedicated master and data nodes.


Conclusion

The decision comes down to one question: do you need to search log content you haven’t pre-indexed?

If your log queries are always scoped by workload metadata (namespace, service, pod, environment), Loki gives you that at a fraction of the storage and operational cost. The object-storage backend, lightweight ingestion, and tight Grafana integration make it the right default for Kubernetes-native teams in 2026.

If you need to ask arbitrary questions of your logs after the fact — who had StatusCode=500 across all services last Tuesday, or which requests contained a specific payload string — Elasticsearch’s inverted index is the answer. It costs more and requires more operational care, but that query capability is structurally impossible to retrofit into Loki without a redesign.

A pragmatic path: start with Loki. If you hit the ceiling — high-cardinality log shapes, security forensics needs, or queries that span the entire log corpus without predefined labels — Elasticsearch is the upgrade path, not the starting point.


Caveats

  • Storage cost comparisons in this article are qualitative, not measurements on your specific workload. Log verbosity, field cardinality, and compression settings shift actual on-disk sizes significantly — benchmark your own data before committing to a budget.
  • Cloud pricing changes frequently. Verify current Grafana Cloud and Elastic Cloud Serverless rates before committing to a budget.
  • We didn’t run these stacks head-to-head under identical production workloads. The benchmark data cited is from third-party comparisons.
  • This article contains affiliate links to Grafana Cloud and Elastic Cloud. They don’t change the verdict.

Primary sources