Kubernetes Deployment
This page gives you a working manifest for running a HarborGuard sensor in Kubernetes. There is no official Helm chart at this time - if and when one ships, this page will be updated.
Container runtime requirements
The sensor currently requires access to a Docker-compatible daemon. Internally it shells out to the docker CLI to pull and inspect images, so it talks to a socket that speaks the Docker Engine API.
This means nodes whose container runtime is containerd or CRI-O are not directly supported by mounting the runtime socket. You cannot point the sensor at /run/containerd/containerd.sock or /var/run/crio/crio.sock and have it work - the wire protocols are different.
Be aware that containerd is the default runtime on Kubernetes 1.24+, and on most managed clusters (EKS, GKE, AKS) since 2022. If you have not deliberately provisioned a Docker-based node pool, assume your nodes are containerd.
Three workarounds are available today:
- Run the sensor on a dedicated VM or node pool with Docker installed. This is the recommended option for air-gapped clusters or organizations that want to keep scanning fully on-prem. Target the node pool with
nodeSelector(shown in the manifest below). For most workloads a single small VM running the sensor as a systemd service is simpler than running it in Kubernetes at all - see the Docker sensor guide. - Use a Docker-in-Docker sidecar. Run
docker:dindalongside the sensor in the same pod and point the sensor at the dind socket. This works but has real costs: dind requires a privileged container (securityContext.privileged: true), consumes additional CPU/memory, and means images get pulled twice (once into dind, once for inspection). Treat it as a last resort. - Use HarborGuard cloud scanning. If your cluster can reach
https://harborguard.coover HTTPS, you can skip the in-cluster sensor entirely and let our managed scanners pull from your registries. This is the lowest-friction option for clusters where the only reason to self-host was the lack of a containerd backend.
Native containerd support is on the roadmap. Until it ships, pick one of the three options above.
Prerequisites
- A namespace to put the sensor in (
harborguard-systemrecommended). - A node pool whose container runtime exposes
/var/run/docker.sock. Pure containerd nodes without dockershim do not work for socket-based pulling - see Containerd note. - An API key with the
developerrole. - Outbound HTTPS from the cluster to your HarborGuard URL.
Complete manifest
Save as harborguard-sensor.yaml and kubectl apply -f harborguard-sensor.yaml.
The manifest below mounts /var/run/docker.sock from the host via hostPath. Confirm the target node actually runs the Docker daemon - on containerd or CRI-O nodes this path does not exist (or points at something incompatible) and the sensor will fail at startup. Run kubectl get nodes -o wide and check the CONTAINER-RUNTIME column before applying.
Label the eligible nodes once:
Why these securityContext choices
| Setting | Why |
|---|---|
runAsUser: 0 | The Docker socket is owned by root:docker on most distros. Easiest portable answer is to run the container as root and drop all capabilities. If your node has a known docker GID, set runAsUser: 1000 and runAsGroup: <docker-gid> instead. |
allowPrivilegeEscalation: false | Sensor never needs to gain privileges at runtime. |
capabilities.drop: ["ALL"] | The sensor needs no Linux capabilities beyond what root already grants for socket I/O. |
seccompProfile: RuntimeDefault | Standard syscall sandbox. |
hostPath socket mount | Required so the sensor can pull and inspect images via the node's runtime. |
Containerd note
If your node uses containerd without a Docker socket, the hostPath: /var/run/docker.sock mount will fail. See Container runtime requirements at the top of this page for the supported workarounds.
DaemonSet vs Deployment
Use a Deployment with a single replica when the sensor scans images from a registry, regardless of where the images run. This is the common case.
Use a DaemonSet only if you want one sensor per node for node-local image scanning. Note that the atomic job-claim model already distributes work safely across replicas, so per-node DaemonSet is rarely needed.
Verifying
You should see register / heartbeat / claim lines. The sensor will appear in Sensors in the UI as online.
Troubleshooting
| Symptom | Likely cause |
|---|---|
Pod logs show docker: command not found or cannot connect to /var/run/docker.sock | The node is not running the Docker daemon. Check kubectl get nodes -o wide - if CONTAINER-RUNTIME is containerd://... or cri-o://..., follow Container runtime requirements. |
Pod stuck in ContainerCreating with a hostPath error | /var/run/docker.sock does not exist on the node. Same root cause as above. |
Sensor logs permission denied on the socket | The container UID lacks access to the docker group. Set runAsUser: 0 or align runAsGroup with the node's docker GID (getent group docker). |
| Sensor never appears in Sensors UI | Check HARBORGUARD_URL and that the API key has the developer role. Outbound HTTPS to harborguard.co (or your self-hosted URL) must be permitted. |
Helm
There is no official chart. If a community chart appears it will be linked from this page. For now, treat the manifest above as a template and parameterize with kustomize or your own values.