Integrations

Kubernetes Integration

Deploy the LogPulse agent to your Kubernetes cluster using a Helm chart. The agent runs as a DaemonSet with one OpenTelemetry Collector pod per node, automatically collecting container logs and enriching them with Kubernetes metadata — pod name, namespace, labels, node, and more. Logs are shipped to LogPulse via OTLP over HTTPS with gzip compression.

Overview

The LogPulse Kubernetes integration uses the OpenTelemetry Collector deployed as a DaemonSet. Each node runs a collector pod that reads container log files, enriches them with Kubernetes metadata via the K8s API, and ships them to LogPulse. The Helm chart handles all RBAC, service accounts, secrets, and configuration automatically.

Helm
One-command install
OTel
OpenTelemetry native
DaemonSet
One pod per node
RBAC
Auto-configured

Architecture

The LogPulse agent is deployed as a Kubernetes DaemonSet, ensuring one collector pod runs on every node in your cluster. Each pod reads container log files from the node's filesystem, parses the container log format, enriches logs with Kubernetes metadata from the API server, and exports them to LogPulse via OTLP HTTP.

OTLP HTTP
Pod A
api-server
Pod B
payment-svc
Pod C
frontend
Pod D
worker
Pod E
ingress
Pod F
cache
/var/log/containers/
Node 1
/var/log/containers/
Node 2
/var/log/containers/
Node 3
OTel Collector
filelog → k8sattr → batch
OTel Collector
filelog → k8sattr → batch
OTel Collector
filelog → k8sattr → batch
Kubernetes API Server
metadata enrichment (pods, labels, nodes)
LogPulse OTLP API
/v1/logs · gzip · Bearer auth

The data flow follows these steps:

  • Container runtime (containerd/Docker) writes stdout/stderr to /var/log/containers/*.log on the node
  • The OTel Collector's filelog receiver reads new log lines and parses the container log format (timestamp, stream, log body)
  • The k8sattributes processor queries the Kubernetes API to enrich each log with pod, namespace, deployment, node, and label metadata
  • The resource processor adds the cluster name to all logs
  • The batch processor groups logs (1000 per batch, 5s timeout) and exports via OTLP HTTP with gzip to LogPulse

Prerequisites

Before installing the LogPulse agent, ensure you have:

  • Kubernetes 1.24 or later (any distribution: EKS, GKE, AKS, k3s, kind, minikube, etc.)
  • Helm 3.8 or later installed on your workstation
  • kubectl configured to access your target cluster
  • A LogPulse API key (create one in Integrations → HTTP API in the dashboard)
  • Cluster-admin permissions (to create ClusterRole and ClusterRoleBinding)
Note: The Helm chart is compatible with all major Kubernetes distributions, including managed services (EKS, GKE, AKS), self-managed clusters, and local development tools (minikube, kind, k3s).

Helm Installation

The LogPulse agent Helm chart (logpulse-agent) deploys an OpenTelemetry Collector as a DaemonSet. It automatically creates the ServiceAccount, ClusterRole, ClusterRoleBinding, ConfigMap, and Secret required for operation.

Quick Start

Get up and running in under 2 minutes:

1Add the LogPulse Helm repository
Add Helm repository
helm repo add logpulse https://charts.logpulse.io
helm repo update
2Create a dedicated namespace
Create namespace
kubectl create namespace logpulse
3Install the agent
Install the LogPulse agent
helm install logpulse-agent logpulse/logpulse-agent \
  --namespace logpulse \
  --set apiKey="lp_your_api_key_here" \
  --set cluster.name="my-production-cluster"
4Verify the installation
Verify the installation
# Check DaemonSet status
kubectl get daemonset -n logpulse

# Check pods are running on each node
kubectl get pods -n logpulse -o wide

# View collector logs
kubectl logs -n logpulse -l app.kubernetes.io/name=logpulse-agent --tail=50
Tip: Logs should appear in your LogPulse dashboard within 30 seconds of installation. Search for namespace="default" to see your first Kubernetes logs.

Configuration Values

The Helm chart supports the following configuration values. Override them with --set flags or a custom values.yaml file.

ParameterDefaultDescription
apiKey""Your LogPulse API key (required). Stored as a Kubernetes Secret.
cluster.name"production"Cluster name for identification. Appears in logs as the 'cluster' field.
endpoint"https://api.logpulse.nl"LogPulse API endpoint. Change for self-hosted deployments.
image.repository"otel/opentelemetry-collector-contrib"OpenTelemetry Collector container image. The contrib variant includes the k8sattributes processor.
image.tag"0.96.0"OTel Collector version. Tested with 0.96.0.
resources.limits.cpu"200m"Maximum CPU allocation per collector pod.
resources.limits.memory"256Mi"Maximum memory allocation per collector pod.
resources.requests.cpu"50m"Minimum guaranteed CPU per collector pod.
resources.requests.memory"128Mi"Minimum guaranteed memory per collector pod.
logs.excludeNamespaces[kube-system, ...]Namespaces to exclude from log collection. Default: kube-system, kube-public, kube-node-lease.
logs.includeNamespaces[]Only collect logs from these namespaces. Empty means all namespaces (minus excludes).
logs.containerLogPath"/var/log/containers"Path to container log files on each node.
logs.includePodLabelstrueInclude pod labels as log metadata. Enables filtering by label in LPQL.
logs.includePodAnnotationsfalseInclude pod annotations as log metadata. Disabled by default to reduce data volume.
serviceAccount.createtrueCreate a dedicated ServiceAccount for the agent.
healthCheck.port13133Port for the OTel Collector health check endpoint (liveness + readiness probes).

Namespace Filtering

By default, the agent collects logs from all namespaces except kube-system, kube-public, and kube-node-lease. You can customize this with include and exclude lists.

Exclude specific namespaces
helm install logpulse-agent logpulse/logpulse-agent \
  --namespace logpulse \
  --set apiKey="lp_your_key" \
  --set logs.excludeNamespaces[0]=kube-system \
  --set logs.excludeNamespaces[1]=kube-public \
  --set logs.excludeNamespaces[2]=kube-node-lease \
  --set logs.excludeNamespaces[3]=monitoring \
  --set logs.excludeNamespaces[4]=logpulse

To collect only from specific namespaces, use includeNamespaces. When set, only these namespaces are collected and excludeNamespaces is ignored.

Include only specific namespaces
# Only collect logs from 'production' and 'staging' namespaces
helm install logpulse-agent logpulse/logpulse-agent \
  --namespace logpulse \
  --set apiKey="lp_your_key" \
  --set logs.includeNamespaces[0]=production \
  --set logs.includeNamespaces[1]=staging
Warning: If you use includeNamespaces, make sure to exclude the logpulse namespace if the collector is deployed there — otherwise you'll create a feedback loop where the collector logs its own log collection.

Kubernetes Metadata

The k8sattributes processor enriches each log event with Kubernetes metadata from the API server. These fields are stored as dedicated columns in the LogPulse database, optimized for fast filtering and RBAC-scoped queries.

Metadata Fields

The following Kubernetes metadata fields are automatically extracted and stored as first-class fields in LogPulse. They are queryable via LPQL without using the attributes map.

FieldOTLP AttributeDescription
clusterk8s.cluster.nameThe cluster name (set via Helm values cluster.name)
namespacek8s.namespace.nameKubernetes namespace the pod runs in
podk8s.pod.nameFull pod name (e.g., api-server-7d4b8c9f6-x2k9m)
containerk8s.container.nameContainer name within the pod
nodek8s.node.nameNode the pod is scheduled on
labelsk8s.pod.labels.*Pod labels as key-value map (e.g., labels.app, labels.version)
annotationsk8s.pod.annotation.*Pod annotations as key-value map (disabled by default)
Note: These fields have dedicated ClickHouse columns with bloom filter and token indexes for sub-second query performance, even at billions of log events. The ORDER BY includes namespace for optimal RBAC-scoped queries.

Labels & Annotations

Pod labels are included by default and stored as a Map(String, String). This lets you query by any label key-value pair. Pod annotations are disabled by default to reduce storage, but can be enabled in the Helm values.

values.yaml — Enable pod annotations
logs:
  includePodLabels: true       # Enabled by default
  includePodAnnotations: true  # Disabled by default, enable if needed

Query logs by label in LPQL:

LPQL — Query by pod label
labels.app="payment-service" level="error"
| stats count by pod, container
Tip: Use consistent pod labels across your deployments (e.g., app, version, team) for effective log filtering. These labels are the most powerful way to group and filter Kubernetes logs in LogPulse.

OpenTelemetry Collector

The Helm chart deploys the OpenTelemetry Collector contrib image (otel/opentelemetry-collector-contrib) which includes the k8sattributes processor, filelog receiver, and all required components. The collector configuration is generated by the Helm chart and stored as a ConfigMap.

Pipeline Configuration

The OTel Collector pipeline is configured with a filelog receiver, k8sattributes processor, resource processor, batch processor, and OTLP HTTP exporter. Below is the full configuration generated by the Helm chart:

OTel Collector Pipeline (auto-generated by Helm chart)
receivers:
  filelog:
    include:
      - /var/log/containers/*.log
    exclude:
      - /var/log/containers/*_kube-system_*.log
      - /var/log/containers/*_kube-public_*.log
      - /var/log/containers/*_kube-node-lease_*.log
    start_at: end
    include_file_path: true
    operators:
      # Parse container log format: <timestamp> <stream> <flags> <log>
      - type: regex_parser
        id: container_parser
        regex: '^(?P<time>[^ ]+) (?P<stream>stdout|stderr) (?P<logtag>[^ ]*) (?P<log>.*)$'
        timestamp:
          parse_from: attributes.time
          layout: '%Y-%m-%dT%H:%M:%S.%fZ'
      # Parse Kubernetes metadata from filename
      - type: regex_parser
        id: k8s_filename_parser
        regex: '^.*\/(?P<pod_name>[^_]+)_(?P<namespace>[^_]+)_(?P<container_name>[^-]+)-(?P<container_id>[^.]+)\.log$'
        parse_from: attributes["log.file.path"]
      # Move parsed log to body
      - type: move
        from: attributes.log
        to: body

processors:
  k8sattributes:
    auth_type: "serviceAccount"
    passthrough: false
    extract:
      metadata:
        - k8s.namespace.name
        - k8s.pod.name
        - k8s.pod.uid
        - k8s.deployment.name
        - k8s.daemonset.name
        - k8s.statefulset.name
        - k8s.job.name
        - k8s.cronjob.name
        - k8s.node.name
        - k8s.container.name
      labels:
        - tag_name: k8s.pod.labels.$$1
          key_regex: (.*)
          from: pod
    pod_association:
      - sources:
          - from: resource_attribute
            name: k8s.pod.name
          - from: resource_attribute
            name: k8s.namespace.name

  resource:
    attributes:
      - key: k8s.cluster.name
        value: "my-cluster"
        action: upsert

  batch:
    timeout: 5s
    send_batch_size: 1000

exporters:
  otlphttp:
    endpoint: https://api.logpulse.nl/v1/logs
    headers:
      Authorization: "Bearer ${env:LOGPULSE_API_KEY}"
    compression: gzip
    retry_on_failure:
      enabled: true
      initial_interval: 1s
      max_interval: 30s
      max_elapsed_time: 300s

extensions:
  health_check:
    endpoint: 0.0.0.0:13133

service:
  extensions: [health_check]
  pipelines:
    logs:
      receivers: [filelog]
      processors: [k8sattributes, resource, batch]
      exporters: [otlphttp]

k8sattributes Processor

The k8sattributes processor is the key component that enriches log events with Kubernetes metadata. It authenticates with the K8s API server using the ServiceAccount token and watches for pod events to maintain an in-memory cache of pod metadata.

The following metadata is extracted from the Kubernetes API:

MetadataDescription
k8s.namespace.nameThe namespace the pod belongs to
k8s.pod.nameThe full pod name
k8s.pod.uidThe unique pod identifier
k8s.deployment.nameThe owning Deployment (if applicable)
k8s.daemonset.nameThe owning DaemonSet (if applicable)
k8s.statefulset.nameThe owning StatefulSet (if applicable)
k8s.job.nameThe owning Job (if applicable)
k8s.cronjob.nameThe owning CronJob (if applicable)
k8s.node.nameThe node the pod is scheduled on
k8s.container.nameThe container name within the pod
Note: The k8sattributes processor uses pod_association to match logs to pods using the pod name and namespace parsed from the log file path. This works reliably because Kubernetes container log filenames contain the pod name and namespace.

RBAC Permissions

The Helm chart automatically creates a ClusterRole and ClusterRoleBinding with the minimum required permissions. The k8sattributes processor needs read-only access to Kubernetes resources to look up pod metadata.

ClusterRole (auto-created by Helm chart)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: logpulse-agent
rules:
  # Required for k8sattributes processor
  - apiGroups: [""]
    resources:
      - pods
      - namespaces
      - nodes
    verbs: ["get", "watch", "list"]
  - apiGroups: ["apps"]
    resources:
      - replicasets
      - deployments
      - daemonsets
      - statefulsets
    verbs: ["get", "watch", "list"]
  - apiGroups: ["batch"]
    resources:
      - jobs
      - cronjobs
    verbs: ["get", "watch", "list"]

Each permission group serves a specific purpose:

API GroupResourcesVerbsReason
"" (core)pods, namespaces, nodesget, watch, listLook up pod metadata, namespace labels, and node information for log enrichment
appsreplicasets, deployments, daemonsets, statefulsetsget, watch, listResolve owning Deployment/DaemonSet/StatefulSet to add workload context to logs
batchjobs, cronjobsget, watch, listResolve owning Job/CronJob for batch workload logs
Tip: The ClusterRole only grants read-only access (get, watch, list). The agent cannot modify any Kubernetes resources. This is the minimum permission set required by the k8sattributes processor.

DaemonSet Details

The DaemonSet ensures one collector pod runs on every eligible node. The pod mounts the node's container log directory as read-only and watches for new log lines. A config checksum annotation triggers automatic rolling restarts when the ConfigMap changes.

Volume Mounts

The collector pod mounts three volumes, all in read-only mode:

VolumeHost PathMount PathDescription
config(ConfigMap)/etc/otel-collectorOTel Collector YAML configuration (generated by Helm chart)
varlog/var/log/var/logNode log directory containing container log files and symlinks
varlibdockercontainers/var/lib/docker/containers/var/lib/docker/containersActual container log files (referenced by symlinks in /var/log/containers)
Warning: The container log path (/var/log/containers) works with containerd and Docker. If your cluster uses a different container runtime or log path, adjust logs.containerLogPath in the Helm values.

Environment Variables

The DaemonSet injects these environment variables into each collector pod:

VariableSourceDescription
LOGPULSE_API_KEYSecret (api-key)LogPulse API key injected from a Kubernetes Secret (never exposed in ConfigMap)
OTEL_K8S_NODE_NAMEfieldRef: spec.nodeNameCurrent node name from the Kubernetes downward API, used by k8sattributes
OTEL_K8S_POD_NAMEfieldRef: metadata.nameCollector pod name from the downward API
OTEL_K8S_NAMESPACEfieldRef: metadata.namespaceCollector pod namespace from the downward API

Health Checks

The OTel Collector exposes a health check endpoint used by Kubernetes liveness and readiness probes. If the health check fails, Kubernetes automatically restarts the pod.

ProbePathPortTiming
Liveness/13133Initial delay: 10s, period: 10s — restarts pod if collector hangs
Readiness/13133Initial delay: 5s, period: 5s — marks pod ready for service

Security

The agent follows security best practices. The filesystem is read-only, the container is not privileged, and the API key is stored as a Kubernetes Secret. The pod runs as root (UID 0) only because it needs read access to /var/log/containers, which is owned by root on most distributions.

SettingValueDescription
runAsNonRootfalseSet to false because /var/log access requires root on most distributions
runAsUser0Root user for file access (container log files are owned by root)
readOnlyRootFilesystemtrueEntire root filesystem is mounted read-only for defense in depth
privilegedfalseContainer runs unprivileged (no kernel capabilities)

The API key is stored as a Kubernetes Secret and injected via environment variable. It never appears in the ConfigMap, pod spec, or collector logs.

API key stored as Kubernetes Secret
apiVersion: v1
kind: Secret
metadata:
  name: logpulse-agent-secret
type: Opaque
stringData:
  api-key: "lp_your_api_key_here"
Warning: For production clusters, use an external secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.) via the External Secrets Operator instead of plain Kubernetes Secrets. Add the appropriate annotations to the ServiceAccount for IRSA (EKS) or Workload Identity (GKE).

Querying Kubernetes Logs

Once logs are flowing, you can query them using LPQL with Kubernetes-specific fields. The dedicated K8s columns (cluster, namespace, pod, container, node, labels) are indexed for fast filtering.

LPQL Examples

Here are common query patterns for Kubernetes log analysis:

Filter by namespace
namespace="production" level="error"
Filter by pod name (wildcard)
namespace="production" pod="api-server-*" level="error"
| stats count by pod
Cross-namespace error overview
level="error" cluster="production"
| stats count by namespace, pod
| sort count desc
Container restarts (OOMKilled, CrashLoopBackOff)
"OOMKilled" OR "CrashLoopBackOff" OR "Back-off restarting"
| stats count by namespace, pod, container
Logs from a specific deployment
labels.app="payment-service" namespace="production"
| timechart count by level
Node-level analysis
node="ip-10-0-1-42" level="error"
| stats count by namespace, pod
Find pods with highest error rates
level="error" last 1h
| stats count as errors by namespace, pod
| sort errors desc
| head 20

Advanced Configuration

Customize the agent for your specific cluster requirements using a custom values file or --set overrides.

Custom Values File

For complex configurations, create a custom-values.yaml file. This gives you full control over all Helm chart parameters:

custom-values.yaml
# LogPulse API key (required)
apiKey: "lp_your_api_key_here"

# Cluster identification
cluster:
  name: "production-eu-west-1"

# Custom endpoint (self-hosted)
# endpoint: "https://your-logpulse-instance.internal"

# Collect from specific namespaces only
logs:
  includeNamespaces:
    - production
    - staging
  excludeNamespaces: []
  includePodLabels: true
  includePodAnnotations: true

# Resource tuning for high-volume clusters
resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 100m
    memory: 256Mi

# Run on specific nodes
nodeSelector:
  node-role.kubernetes.io/worker: ""

# Service account annotations (e.g., for IRSA on EKS)
serviceAccount:
  annotations:
    eks.amazonaws.com/role-arn: "arn:aws:iam::123456789:role/logpulse-agent"
Install with custom values file
helm install logpulse-agent logpulse/logpulse-agent \
  --namespace logpulse \
  --create-namespace \
  -f custom-values.yaml

Resource Tuning

The default resource limits (200m CPU, 256Mi memory) work well for most clusters. Adjust based on your log volume and node count:

Cluster SizeCPU LimitMemory LimitNotes
Small (< 10 nodes, < 1K events/sec)200m256MiDefault settings, suitable for development and small production clusters
Medium (10-50 nodes, 1-10K events/sec)500m512MiIncrease limits to handle higher throughput without backpressure
Large (50+ nodes, 10K+ events/sec)1000m1GiMaximum allocation for high-volume clusters with many pods per node
Tip: Monitor collector pod CPU and memory usage with 'kubectl top pods -n logpulse'. If pods are consistently hitting their limits, increase the resources. If they're far below, reduce to save cluster resources.

Tolerations & Affinity

By default, the agent tolerates all taints (NoSchedule and NoExecute with operator: Exists) to ensure it runs on every node, including master/control-plane nodes. This ensures complete log coverage.

Default tolerations (run on all nodes)
tolerations:
  - operator: Exists
    effect: NoSchedule
  - operator: Exists
    effect: NoExecute

To restrict the agent to specific nodes (e.g., only worker nodes):

Restrict to worker nodes only
tolerations: []
nodeSelector:
  node-role.kubernetes.io/worker: ""

Vector Alternative

If you prefer Vector over the OpenTelemetry Collector, you can deploy Vector as a DaemonSet using its native kubernetes_logs source. Vector automatically discovers and collects container logs with Kubernetes metadata. Note that Vector sends logs via the HTTP ingest API instead of OTLP, so Kubernetes metadata fields are stored in the attributes map rather than dedicated columns.

Vector DaemonSet — kubernetes_logs source
sources:
  k8s_logs:
    type: kubernetes_logs
    self_node_name: "${VECTOR_SELF_NODE_NAME}"
    extra_label_selector: "app!=logpulse-agent"
    extra_namespace_label_selector: "kubernetes.io/metadata.name!=kube-system"

transforms:
  enrich:
    type: remap
    inputs:
      - k8s_logs
    source: |
      .index = "kubernetes"
      .source = string!(.kubernetes.pod_namespace) + "/" + string!(.kubernetes.pod_name)
      .sourcetype = "kubernetes"
      .fields.namespace = string!(.kubernetes.pod_namespace)
      .fields.pod = string!(.kubernetes.pod_name)
      .fields.container = string!(.kubernetes.container_name)
      .fields.node = string!(.kubernetes.pod_node_name)

sinks:
  logpulse:
    type: http
    inputs:
      - enrich
    uri: "https://api.logpulse.io/api/v1/ingest/vector"
    method: post
    encoding:
      codec: json
    auth:
      strategy: bearer
      token: "${LOGPULSE_API_KEY}"
    batch:
      max_bytes: 1048576
      timeout_secs: 5
Note: The Helm chart (OTel Collector) is the recommended approach for Kubernetes because it uses OTLP with the k8sattributes processor, which stores K8s metadata in dedicated indexed columns for faster queries and namespace-based RBAC. The Vector alternative is useful when you already run Vector in your cluster.

Troubleshooting

Common issues and solutions when deploying the LogPulse agent on Kubernetes:

IssueDiagnosisSolution
No logs appearing in LogPulsekubectl logs shows the collector running, but no data in dashboardVerify the API key is correct and the endpoint is reachable. Check for 401 errors in collector logs. Ensure the Secret exists: kubectl get secret -n logpulse
Logs from some namespaces missingSome pods' logs appear but others don'tCheck excludeNamespaces in your values. The namespace may be excluded. Verify the DaemonSet is running on the correct nodes: kubectl get pods -n logpulse -o wide
Logs arrive without K8s metadataLogs show in LogPulse but namespace, pod, and labels are emptyCheck RBAC permissions: kubectl auth can-i list pods --as=system:serviceaccount:logpulse:logpulse-agent --all-namespaces. The ClusterRole may not be bound correctly.
Collector pods OOMKilledPods restart with OOMKilled statusIncrease memory limits: --set resources.limits.memory=512Mi. High-volume clusters may need 1Gi or more. Also check batch.send_batch_size isn't too large.
401 Unauthorized errors in collector logsExporter logs show HTTP 401 errorsVerify the Secret contains a valid API key: kubectl get secret -n logpulse logpulse-agent-secret -o jsonpath='{.data.api-key}' | base64 -d. Ensure the key is active in the LogPulse dashboard.
Collector pod in CrashLoopBackOffPod keeps restartingCheck pod logs for config errors: kubectl logs -n logpulse -l app.kubernetes.io/name=logpulse-agent --previous. Common causes: invalid YAML in custom values, missing API key, or incorrect endpoint URL.
Useful debugging commands
# Check DaemonSet status
kubectl get ds -n logpulse

# View collector pod logs
kubectl logs -n logpulse -l app.kubernetes.io/name=logpulse-agent --tail=100

# Check pod events for errors
kubectl describe pod -n logpulse -l app.kubernetes.io/name=logpulse-agent

# Verify RBAC permissions
kubectl auth can-i list pods --as=system:serviceaccount:logpulse:logpulse-agent --all-namespaces

# Check Secret exists
kubectl get secret -n logpulse logpulse-agent-secret -o jsonpath='{.data.api-key}' | base64 -d

# View generated OTel config
kubectl get configmap -n logpulse logpulse-agent-config -o yaml

Uninstalling

To remove the LogPulse agent from your cluster, uninstall the Helm release. This removes the DaemonSet, ConfigMap, Secret, ServiceAccount, ClusterRole, and ClusterRoleBinding. Previously collected logs remain in LogPulse.

Uninstall the LogPulse agent
# Remove the Helm release
helm uninstall logpulse-agent --namespace logpulse

# Optionally remove the namespace
kubectl delete namespace logpulse

# Verify cleanup (ClusterRole/ClusterRoleBinding are cluster-scoped)
kubectl get clusterrole | grep logpulse
kubectl get clusterrolebinding | grep logpulse
Note: Uninstalling only stops future log collection. All logs already shipped to LogPulse remain searchable and subject to your retention policy.