DockLogDockLogBlog
5 min readDockLog

K8s pod logs without Loki

Running DockLog in kubernetes mode, kubeconfig mount, in-cluster deploy, WebSockets through ingress.

You don't need Fluent Bit and Loki to answer "what is this pod printing right now?"

For a small cluster, staging, or a k3s box in the garage, DockLog in kubernetes mode gives you live pod logs, basic workload views, and the same RBAC/alert stuff as Docker mode. One container, no DaemonSet.

It does not replace long-term log storage. Keep your cloud provider or Loki for retention if you need it. Use this for tailing during deploys and incidents. We wrote up how DockLog compares to Loki for the split-brain setup.

Runtime mode

ModeWhat you get
dockerDefault, socket only
kubernetesPods, deployments, events, pod logs
bothToggle in the UI when you have socket and kubeconfig on the same host

DockLog starts even if the cluster is unreachable; K8s pages just show a connection warning until credentials work.

Homelab: mount kubeconfig

DockLog on the host, not in the cluster:

yaml
services:
  docklog:
    image: aimldev/docklog:latest
    ports:
      - "127.0.0.1:8888:8000"
    environment:
      RUNTIME_MODE: kubernetes
      KUBECONFIG: /app/kube/config
      K8S_NAMESPACES: default,staging,production
      SECRET_KEY: ${SECRET_KEY}
      DB_PATH: /app/data/docklog.db
      TRUST_PROXY: "true"
      ALLOWED_ORIGINS: https://docklog.example.com
    volumes:
      - ~/.kube/config:/app/kube/config:ro
      - ./data:/app/data
    restart: unless-stopped

K8S_NAMESPACES limits what the instance queries. Leave it out only if you mean cluster-wide and your creds allow it.

Context matters: if your kubeconfig has three contexts, DockLog uses the current context. kubectl config use-context before docker compose up or set KUBECONFIG to a single-context file.

In-cluster (more production-shaped)

Namespace + ServiceAccount, bind a ClusterRole with at least get/list/watch on pods, pods/log, namespaces, events. Deploy the image with serviceAccountName: docklog, mount a PVC for /app/data, expose via Service and Ingress.

Add write verbs on pods only if you enable restart/delete in DockLog and accept that risk.

ServiceAccount and ClusterRole

yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: docklog
  namespace: monitoring
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: docklog-reader
rules:
  - apiGroups: [""]
    resources: ["pods", "pods/log", "namespaces", "events"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["apps"]
    resources: ["deployments", "replicasets", "statefulsets", "daemonsets"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: docklog-reader
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: docklog-reader
subjects:
  - kind: ServiceAccount
    name: docklog
    namespace: monitoring

For restart buttons, add delete on pods (restart is implemented via delete/recreate). Only if ALLOW_RESTART is on and you accept blast radius.

Deployment skeleton

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: docklog
  namespace: monitoring
spec:
  replicas: 1
  selector:
    matchLabels:
      app: docklog
  template:
    metadata:
      labels:
        app: docklog
    spec:
      serviceAccountName: docklog
      containers:
        - name: docklog
          image: aimldev/docklog:latest
          ports:
            - containerPort: 8000
          env:
            - name: RUNTIME_MODE
              value: kubernetes
            - name: K8S_NAMESPACES
              value: default,staging,production
            - name: SECRET_KEY
              valueFrom:
                secretKeyRef:
                  name: docklog-secrets
                  key: secret-key
            - name: DB_PATH
              value: /app/data/docklog.db
            - name: TRUST_PROXY
              value: "true"
            - name: ALLOWED_ORIGINS
              value: https://docklog.example.com
          volumeMounts:
            - name: data
              mountPath: /app/data
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: docklog-data

Expose via ClusterIP Service plus Ingress with proxy-read-timeout: "86400" on nginx ingress. TRUST_PROXY and ALLOWED_ORIGINS when TLS terminates at ingress. Full ingress examples: reverse proxy post.

Ingress and WebSockets

Same mistake every time: logs connect, scroll for 30 seconds, stop. Ingress didn't forward upgrades.

nginx ingress needs long timeouts (see annotations above). Plain nginx:

nginx
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;

Test: open pod logs, wait two minutes without scrolling. If the stream dies at 60s, it's the proxy.

Who sees which namespaces

Cluster RBAC = what the DockLog pod can reach. Per-user allowed_containers = what each login sees.

PatternWho sees it
stagingAll pods in namespace staging
production/api-*Pods named api-* in production
*-worker-*Pod name match across allowed namespaces

Combine with K8S_NAMESPACES so kube-system never appears even if someone fat-fingers a pattern.

Details: RBAC post.

Alerts on K8s events

Crash loop backoff and image pull failures show up as K8s event rules. Scope to production/* unless you like staging noise.

EventUsually means
CrashLoopBackOffApp exiting on start, check logs
ImagePullBackOffWrong tag, registry auth, rate limit
FailedSchedulingResources, taints, PVC pending

Channel setup: Slack/Teams post.

Hybrid host

RUNTIME_MODE=both, mount socket and kubeconfig. Logs page gets a Docker/K8s toggle. Common when compose runs your apps and k3s runs everything else.

yaml
environment:
  RUNTIME_MODE: both
  KUBECONFIG: /app/kube/config
  K8S_NAMESPACES: default,staging,production
volumes:
  - /var/run/docker.sock:/var/run/docker.sock
  - ~/.kube/config:/app/kube/config:ro
  - ./data:/app/data

Same user can have alice-* for Docker and staging/* for K8s in one allowed_containers field.

When it's broken

SymptomCheck
Empty pod listServiceAccount verbs, K8S_NAMESPACES
Logs drop after 60sIngress/proxy timeout
Wrong namespaces for a userTheir allowed_containers, not cluster RBAC
Works on laptop, not in clusterIn-cluster token vs mounted config confusion
Connection warning on startupCluster down or wrong context
Admin sees pods, user doesn'tUser pattern typo; use namespace/pod syntax

Works on laptop, not in cluster: mounted kubeconfig uses your user creds; in-cluster uses ServiceAccount token. Different RBAC bindings.

Verify ServiceAccount access with kubectl auth can-i list pods --as=system:serviceaccount:monitoring:docklog -A before blaming DockLog.

Official K8s guide for screenshots. Compose baseline if you're on the same machine as Docker. Compare with terminal tools: DockLog vs k9s. Wider tool landscape: K8s monitoring guide.

Continue reading