Skip to main content
Loki provides scalable log aggregation for the Kimbernetes cluster, storing logs collected by Grafana Alloy. It’s deployed in SingleBinary mode with MinIO for S3-compatible object storage, optimized for Kubernetes environments.

HelmRelease Configuration

overlays/base/grafana/grafana-loki/helm-release.yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: grafana-loki
spec:
  timeout: 15m
  chart:
    spec:
      chart: loki
      sourceRef:
        kind: HelmRepository
        name: grafana
      version: 6.49.0
  interval: 24h
  releaseName: loki-monolith
The release is named loki-monolith to distinguish it from distributed deployments.

Deployment Architecture

Loki runs in SingleBinary mode with all components in a single process:
overlays/base/grafana/grafana-loki/helm-release.yaml
deploymentMode: SingleBinary

singleBinary:
  replicas: 1
  persistence:
    storageClass: local-storage
  extraArgs:
    - -store.retention=31d

Why SingleBinary Mode?

Simplicity

Single deployment to manage, easier troubleshooting

Resource Efficiency

Lower overhead compared to microservices mode

Sufficient for Most Workloads

Handles moderate log volumes effectively

Easy Upgrades

No complex migration between versions
For high-scale deployments (>100GB/day), consider switching to microservices mode with distributed components.

Storage Configuration

Loki uses a hybrid storage approach:

Index and Cache Storage

loki:
  schemaConfig:
    configs:
      - from: "2024-04-01"
        store: tsdb
        object_store: s3
        schema: v13
        index:
          prefix: loki_index_
          period: 24h
TSDB (Time Series Database) index format provides better query performance and compression compared to legacy BoltDB.

Object Storage (MinIO)

overlays/base/grafana/grafana-loki/helm-release.yaml
minio:
  enabled: true
  persistence:
    size: 30Gi
    storageClass: local-storage
MinIO provides S3-compatible storage for log chunks:
  • Capacity: 30Gi
  • Storage Class: local-storage
  • Access: Internal cluster service
MinIO exposes two services:
# S3 API endpoint (used by Loki)
loki-monolith-minio.observability.svc:9000

# Web UI (for administration)
loki-monolith-minio-console.observability.svc:9001
Access the MinIO console:
kubectl port-forward -n observability svc/loki-monolith-minio-console 9001:9001

Persistent Volume

A PV is configured for WAL and temporary data:
overlays/kimawesome/infrastructure/observability/grafana-loki/loki-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: loki-pv
spec:
  capacity:
    storage: 50Gi
  accessModes:
    - ReadWriteOnce
  storageClassName: local-storage
  hostPath:
    path: /mnt/loki-data

Configuration

Authentication

loki:
  auth_enabled: false
Authentication is disabled for internal cluster use. Enable auth_enabled: true and configure tenants for multi-tenant deployments.

Replication

loki:
  commonConfig:
    replication_factor: 1
Single replica configuration suitable for development and small-scale production.

Limits and Features

overlays/base/grafana/grafana-loki/helm-release.yaml
loki:
  pattern_ingester:
    enabled: true
  limits_config:
    allow_structured_metadata: true
    volume_enabled: true
  ruler:
    enable_api: true
Automatically extracts log patterns for faster queries:
# Query detected patterns
{namespace="default"} | pattern
Patterns help identify common log structures without indexing every field.

Retention Policy

Logs are retained for 31 days:
singleBinary:
  extraArgs:
    - -store.retention=31d
Retention is applied to the entire Loki instance. For per-tenant retention, configure limits_config.retention_period with authentication enabled.

LogQL Query Examples

{namespace="default"}

Advanced LogQL

{namespace="default"}
  | pattern `<ip> - - <_> "<method> <path> <_>" <status> <_>`
  | status >= 400
Extract structured data from unstructured logs.

Accessing Loki

1

Via Grafana Datasource

Loki is pre-configured as a Grafana datasource. Access through:
  • Grafana → Explore → Select “Loki” datasource
2

Direct API Access

Port forward to Loki service:
kubectl port-forward -n observability svc/loki-monolith 3100:3100
Query API:
# Query logs
curl -G -s "http://localhost:3100/loki/api/v1/query_range" \
  --data-urlencode 'query={namespace="default"}' \
  --data-urlencode 'start=1h' | jq

# Get labels
curl -s http://localhost:3100/loki/api/v1/labels | jq
3

LogCLI Tool

Use Grafana’s LogCLI for command-line queries:
# Install LogCLI
go install github.com/grafana/loki/cmd/logcli@latest

# Forward port and query
export LOKI_ADDR=http://localhost:3100
logcli query '{namespace="default"}'

Performance Optimization

Query Performance

# Good: Specific label selector
{namespace="default", app="my-app"}

# Bad: Broad selector
{namespace=~".*"}
More specific label filters reduce the number of log streams Loki must search.
# Good: Recent time range
{namespace="default"}[1h]

# Bad: Large time range
{namespace="default"}[30d]
Shorter time ranges query fewer chunks and return faster.
# Good: Filter early in pipeline
{namespace="default"} |= "error" | json

# Bad: Parse before filtering
{namespace="default"} | json | status >= 400
Line filters (|=, !=, |~, !~) are faster than parsers.

Resource Allocation

For high log volumes, increase resources:
singleBinary:
  resources:
    limits:
      memory: 4Gi
      cpu: 2000m
    requests:
      memory: 2Gi
      cpu: 1000m

Cache Configuration

The deployment includes built-in caching:
overlays/base/grafana/grafana-loki/helm-release.yaml
chunksCache:
  resources:
    limits:
      memory: "1Gi"
    requests:
      memory: "512Mi"
resultsCache:
  resources:
    limits:
      memory: "256Mi"
    requests:
      memory: "128Mi"

Troubleshooting

Check pod status:
kubectl get pod -n observability -l app.kubernetes.io/name=loki
kubectl describe pod -n observability -l app.kubernetes.io/name=loki
View logs:
kubectl logs -n observability -l app.kubernetes.io/name=loki
Common issues:
  • PVC not bound (check storage class)
  • MinIO not ready (check MinIO pods)
  • Resource limits too low
Verify Alloy is sending logs:
# Check Alloy write metrics
kubectl port-forward -n observability ds/grafana-monitoring-alloy-logs 12345:12345
curl http://localhost:12345/metrics | grep loki_write_sent_bytes_total
Check Loki ingestion:
kubectl port-forward -n observability svc/loki-monolith 3100:3100
curl http://localhost:3100/metrics | grep loki_ingester_streams
Check query performance:
kubectl port-forward -n observability svc/loki-monolith 3100:3100
curl http://localhost:3100/metrics | grep loki_query_duration_seconds
Optimize queries:
  • Use more specific label selectors
  • Reduce time ranges
  • Add line filters before parsers
Check MinIO storage usage:
kubectl exec -n observability -it deploy/loki-monolith-minio -- df -h /export
Options:
  • Reduce retention period
  • Increase MinIO PVC size
  • Migrate to external S3

Migrating to Distributed Mode

For production at scale, consider distributed deployment:
1

Plan the Migration

  • Export existing logs or accept data loss
  • Plan for separate read/write/backend components
  • Configure object storage (S3, GCS, etc.)
2

Update HelmRelease

deploymentMode: Distributed
read:
  replicas: 3
write:
  replicas: 3
backend:
  replicas: 3
3

Update Datasource

Point to the new gateway service:
url: http://loki-gateway.observability.svc:80

Next Steps

Visualize in Grafana

Create dashboards for log analytics

Configure Alloy

Customize log collection pipelines

Add Metrics

Correlate logs with metrics

Overview

Return to observability architecture

Build docs developers (and LLMs) love