Container Deployment

Decision guide for deploying the LinuxGuard agent in containers — scenario matrix, anti-patterns, and per-orchestrator spokes for Docker, Podman, docker-compose, and Kubernetes.

This is the hub for running the LinuxGuard agent inside a container — Docker, Podman, docker-compose, or Kubernetes. Start here, pick the deployment scenario that matches your environment, and follow the linked spoke.

Important: The agent is published as a digest-pinned distroless image. Operators MUST pin to an immutable vX.Y.Z tag in production. Floating tags (:latest, :stable) are an anti-pattern — see below.

Scenario matrix

The right deployment shape depends on what you are protecting and how the orchestrator manages identity.

Scenario
Recommended
Why
Spoke

Pre-existing Kubernetes cluster, broad coverage of all host activity

DaemonSet

One pod per node; the agent observes every workload running on that node through host PID + tracefs

One-shot scan inside an ephemeral CI runner, build pod, or job

Ephemeral mode

TOTP enrol once, in-memory cert chain, no persistent identity to clean up

Mixed host + container workloads under Kubernetes

DaemonSet only — do NOT also run a per-pod sidecar

The node-level DaemonSet already observes the containerized workloads via host PID; a sidecar produces duplicate events (anti-pattern)

Single-node Docker host (lab, edge appliance, developer workstation)

Docker run or docker-compose

Lightweight; the agent runs as a long-lived container with a tmpfs cert cache

Rootless container runtime (security-hardened workstation, locked-down host)

Podman

Rootless mode trades eBPF capabilities for least-privilege execution; rootful mode preserves full telemetry

Pod-level isolation explicitly required (multi-tenant cluster where the DaemonSet model is rejected by policy)

Sidecar (per pod)

Heavier resource cost — only when DaemonSet cannot meet the policy constraint

Kubernetes DaemonSet (Sidecar section)

Air-gapped or strict restricted Pod Security Standard cluster

Documented gap — consult support

Many of the agent's required capabilities (CAP_BPF, CAP_PERFMON, host paths) are disallowed under restricted

What's in this section

  • Distroless image reference — what's in the image, what's excluded (no shell, no package manager), and how to verify the published digest

  • Ephemeral mode--ephemeral, --workload-id, PID 1 auto-detection, TLS cache restart semantics

  • Kubernetes DaemonSet — copy-pasteable YAML with line-by-line securityContext rationale, eBPF prerequisites, and the PSS profile the manifest works under

  • Downward API integrationLINUXGUARD_NODE_NAME and LINUXGUARD_POD_UID via fieldRef, workload identity derivation

  • Enrollment tokens — TOTP flow, valueFrom.secretKeyRef pattern, why the agent unsets the env var at startup

  • docker-composecompose.yaml example, env-var configuration, volume mounts, restart policies

  • Podman — rootless vs rootful trade-offs, systemd quadlet integration, equivalence to Docker commands

See also:

Anti-patterns

The four anti-patterns below break real production deployments. Each is called out in the relevant spoke; collected here for fast reference.

1. Floating image tags in production (:latest, :stable)

Pinning to :latest (or any other moving tag) makes the deployment irreproducible. A node that pulls the image at 09:00 may receive a different binary than a node that pulls at 13:00 after a publish. Audit replay, incident reconstruction, and rollback all break.

Correct: Pin to an immutable vX.Y.Z tag (e.g., packages.linuxguard.io/linuxguard-agent:v3.0.0) or to an image digest (@sha256:...). The OCI multi-arch manifest guarantees the same vX.Y.Z tag resolves to architecture-appropriate images on amd64 and arm64 — see OCI multi-arch manifest.

2. Running a DaemonSet AND a per-pod sidecar

A DaemonSet that runs with hostPID: true already observes every process on the node — including every containerized workload running on that node. Adding a sidecar to each application pod produces duplicate events for the same syscall: once from the node-level DaemonSet's perspective and once from the per-pod sidecar's perspective. Console alerts double-count; dashboards show inflated activity; downstream SIEM ingestion costs double.

Correct: Pick one model. DaemonSet covers all containerized workloads on the node. Sidecar is reserved for the narrow case where pod-level isolation is mandated by policy and a node-level pod is explicitly rejected.

3. Mounting host / read-write

A deployment that mounts the entire host filesystem (hostPath: /, mountPath: /host, readOnly: false) gives the agent container write access to every file on the node, including kernel modules, systemd unit files, and /etc/sudoers. The agent does not need that. The blast radius of a container escape (or a compromised agent image) is the entire node.

Correct: Mount only the specific host paths the agent needs, and mount each one read-only unless the spoke explicitly justifies write access. The four required paths for a DaemonSet are /sys/kernel/tracing (tracefs, read-only), /sys/fs/bpf (BPF FS, read-write for pinned maps), /proc (read-only for process introspection), and a tmpfs at /run/linuxguard (read-write, ephemeral) for the TLS cache. See Kubernetes DaemonSet § Host paths.

4. Re-using a TOTP enrollment token across pods

A TOTP --enroll-token is single-use and short-lived. The backend's /agent/enroll handler accepts each token exactly once for one workload identity. Re-using the same token across multiple pods in a DaemonSet (e.g., by baking it into a ConfigMap with the literal value) produces unpredictable behavior: the first pod enrols successfully, subsequent pods get 409 conflict or stale-identity errors, and the cluster's agent fleet ends up partially enrolled with silent gaps in coverage.

Correct: Each pod gets a fresh TOTP token, injected via Kubernetes valueFrom.secretKeyRef. The Secret is rotated externally (by an enrollment service or operator-driven process) so that each new pod sees a new value. The agent reads the env var once at startup, immediately calls os.Unsetenv("LINUXGUARD_ENROLL_TOKEN") to scrub it from /proc/<pid>/environ, and never persists it — see Enrollment tokens.

Architecture caveats

  • ARMv7 (arm/v7): the agent binary builds and the image is published in the OCI multi-arch manifest, but the eBPF object is a zero-byte sentinel — the loader falls through to DegradedNoEBPFArch mode and produces no behavioral telemetry. See Multi-Architecture Support. Treat ARMv7 container deployments as a documented gap in the evidence chain.

  • RISC-V (riscv64): best-effort. The published image may or may not include compiled eBPF probes depending on the toolchain at build time. Verify against the multi-arch manifest before relying on RISC-V in production.

  • s390x / ppc64le: unverified for container deployment. Surface unsupported in v4.0; consult support before deploying.


Next Step: Distroless image reference →

Related: Multi-Architecture Support | OCI Multi-Arch Manifest | start CLI reference | Environment variables

Last updated

Was this helpful?