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.Ztag 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.
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 semanticsKubernetes DaemonSet — copy-pasteable YAML with line-by-line
securityContextrationale, eBPF prerequisites, and the PSS profile the manifest works underDownward API integration —
LINUXGUARD_NODE_NAMEandLINUXGUARD_POD_UIDviafieldRef, workload identity derivationEnrollment tokens — TOTP flow,
valueFrom.secretKeyRefpattern, why the agent unsets the env var at startupdocker-compose —
compose.yamlexample, env-var configuration, volume mounts, restart policiesPodman — rootless vs rootful trade-offs, systemd quadlet integration, equivalence to Docker commands
See also:
OCI multi-arch manifest —
docker manifest inspectoutput, supported platforms, image digest stability,--platformpull syntaxstartcommand reference — all flags consumed by every container scenario belowEnvironment variables — every
LINUXGUARD_*variable the agent reads at startup
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)
: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
/ read-writeA 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 toDegradedNoEBPFArchmode 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?