Send Kubernetes logs to Epok
Vector as a DaemonSet on every node. One pod per worker reads /var/log/containers/*.logvia the host mount, parses the CRI log format, and ships to Epok. Once logs arrive, Epok's 70+ Kubernetes detection rules fire automatically: CrashLoopBackOff, OOMKilled, ImagePullBackOff, FailedScheduling, probe failures, eviction patterns.
Time to first log: 5 min · Works on: EKS, GKE, AKS, k3s, kind, self-hosted · API key: app.getepok.dev → Settings → API Keys
1. Store the API key as a secret
bash
kubectl create namespace epok
kubectl -n epok create secret generic epok-credentials \
--from-literal=api-key=epk_REPLACE_ME2. Vector config (ConfigMap)
yaml
# epok-vector-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: epok-vector-config
namespace: epok
data:
vector.yaml: |
sources:
kube_logs:
type: kubernetes_logs
# Vector auto-discovers pods, attaches namespace/pod/container
# labels, and parses the CRI/JSON log format. No extra config
# needed for the common case.
glob_minimum_cooldown_ms: 200
transforms:
enrich:
type: remap
inputs: [kube_logs]
source: |
# Map the kubernetes_logs fields to Epok's expected shape.
.service = .kubernetes.container_name
.namespace = .kubernetes.pod_namespace
.pod = .kubernetes.pod_name
.node = .kubernetes.pod_node_name
._msg = .message
del(.message)
sinks:
epok:
type: elasticsearch
inputs: [enrich]
endpoint: https://ingest.getepok.dev
bulk:
index: logs
auth:
strategy: basic
user: ${EPOK_API_KEY}
password: x
# Sensible defaults for k8s log volume.
batch:
max_events: 1000
timeout_secs: 5
buffer:
type: memory
max_events: 5000
when_full: drop_newest3. RBAC for Vector
Vector needs read access to pods + namespaces to enrich log entries with metadata.
yaml
# epok-vector-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: epok-vector
namespace: epok
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: epok-vector
rules:
- apiGroups: [""]
resources: ["pods", "namespaces", "nodes"]
verbs: ["list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: epok-vector
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: epok-vector
subjects:
- kind: ServiceAccount
name: epok-vector
namespace: epok4. DaemonSet
yaml
# epok-vector-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: epok-vector
namespace: epok
spec:
selector:
matchLabels: { app: epok-vector }
template:
metadata:
labels: { app: epok-vector }
spec:
serviceAccountName: epok-vector
tolerations:
- operator: Exists # Run on every node, incl. tainted ones
containers:
- name: vector
# Pin to a specific version, not :latest, so a Vector release
# never breaks your log pipeline. Bump deliberately after testing.
image: timberio/vector:0.43.0-alpine
env:
- name: EPOK_API_KEY
valueFrom:
secretKeyRef:
name: epok-credentials
key: api-key
resources:
requests: { cpu: 50m, memory: 128Mi }
limits: { cpu: 500m, memory: 512Mi }
volumeMounts:
- { name: config, mountPath: /etc/vector }
- { name: var-log, mountPath: /var/log, readOnly: true }
- { name: var-lib, mountPath: /var/lib/docker, readOnly: true }
- { name: data, mountPath: /vector-data }
volumes:
- { name: config, configMap: { name: epok-vector-config } }
- { name: var-log, hostPath: { path: /var/log } }
- { name: var-lib, hostPath: { path: /var/lib/docker } }
- { name: data, emptyDir: {} }5. Apply everything
bash
kubectl apply -f epok-vector-config.yaml
kubectl apply -f epok-vector-rbac.yaml
kubectl apply -f epok-vector-daemonset.yaml
# Watch the pods come up
kubectl -n epok rollout status daemonset/epok-vector
# Tail one of the agents to confirm
kubectl -n epok logs -l app=epok-vector --tail=20 -fVerify
- Open app.getepok.dev → Live Tail. Container logs should stream in within 30–60 s, tagged with
namespace,pod,container. - Trigger a failure:
kubectl delete pod <some-pod>and force a CrashLoopBackOff. Within ~2 minutes the K8s Intelligence detector fires an alert in New Errors. - Open Services. Each unique
container_nameappears as a service card with hit counts.
Common gotchas
- Containerd vs Docker. Modern clusters use containerd — logs live under
/var/log/containers/(already mounted above). The/var/lib/dockermount is a no-op on pure-containerd nodes and harmless to leave in. - PodSecurity / Pod Security Admission. DaemonSets that mount host paths need a privileged or baseline policy. If your cluster enforces
restricted, create theepoknamespace with thepod-security.kubernetes.io/enforce=privilegedlabel. - EKS + Fargate.Fargate nodes can't run DaemonSets. Use Fluent Bit's built-in EKS Fargate logging instead — config snippet in the AWS guide.
- Resource limits. The 500 m CPU / 512 Mi memory cap is right for ~5 k logs/sec/node. Bump if you see Vector OOMing under heavy log bursts.
Want pod-level dashboards out of the box? Once logs are flowing, Epok auto-builds Service Health views per container. No dashboards to configure.