Deploying to Kubernetes
Guardian discovers a Kubernetes cluster’s nodes and pods with a single in-cluster collector — the same agent binary you deploy on physical hosts, running in a different mode. It watches the cluster’s own API server from inside the cluster and reports up to the hub, so it works for private/on-prem clusters with no public API endpoint just as well as managed ones.
This is inventory only for now — nodes and pods show up in Kubernetes in the dashboard, but a NotReady node or a crash-looping pod doesn’t yet open an alert or incident the way a host going offline does.
What it discovers
Section titled “What it discovers”- Nodes — name and Ready/NotReady status
- Pods — namespace, name, which node it’s scheduled on, and status (Running, Pending, CrashLoopBackOff, etc.)
Nothing else yet — no Deployments, Services, or Ingress. See the project roadmap if you need broader coverage.
Deploy the collector
Section titled “Deploy the collector”You need cluster-admin (or equivalent) access to apply a ClusterRole.
- Go to Deploy Agent in the dashboard and generate an enrollment token, same as for a host agent.
- Save the two manifests below as
rbac.yamlanddeployment.yaml. - Edit
deployment.yaml: replace<VERSION>with a released agent version, and fill inENROLLMENT_TOKENandGUARDIAN_HUB_URLin its Secret. - Apply both:
kubectl apply -f rbac.yamlkubectl apply -f deployment.yamlrbac.yaml
Section titled “rbac.yaml”# rbac.yaml: read-only access (get, list, watch on nodes, pods and namespaces). No write access, no secrets access.apiVersion: v1kind: Namespacemetadata: name: gt-guardian---apiVersion: v1kind: ServiceAccountmetadata: name: gt-guardian-collector namespace: gt-guardian---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: gt-guardian-collectorrules: - apiGroups: [""] resources: ["nodes", "pods", "namespaces"] verbs: ["get", "list", "watch"]---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: gt-guardian-collectorsubjects: - kind: ServiceAccount name: gt-guardian-collector namespace: gt-guardianroleRef: kind: ClusterRole name: gt-guardian-collector apiGroup: rbac.authorization.k8s.iodeployment.yaml
Section titled “deployment.yaml”# deployment.yaml: one collector for the whole cluster. Apply rbac.yaml first.apiVersion: v1kind: Secretmetadata: name: gt-guardian-collector namespace: gt-guardiantype: OpaquestringData: ENROLLMENT_TOKEN: "REPLACE_WITH_YOUR_ENROLLMENT_TOKEN" GUARDIAN_HUB_URL: "https://your-hub.example.com"---apiVersion: apps/v1kind: Deploymentmetadata: name: gt-guardian-collector namespace: gt-guardianspec: replicas: 1 selector: matchLabels: app: gt-guardian-collector template: metadata: labels: app: gt-guardian-collector spec: serviceAccountName: gt-guardian-collector containers: - name: collector image: ghcr.io/gt-arsenals-ltd/gt-guardian-agent:<VERSION> envFrom: - secretRef: name: gt-guardian-collector resources: requests: cpu: 50m memory: 64Mi limits: cpu: 250m memory: 256MiThis creates a gt-guardian namespace, a read-only ServiceAccount/ClusterRole (get/list/watch on nodes, pods, and namespaces — nothing else, no write access, no secrets access), and a single-replica Deployment. One collector watches the whole cluster — this is not a per-node DaemonSet.
Verify it worked
Section titled “Verify it worked”Check Kubernetes in the dashboard — the cluster should appear within about 30 seconds, with its node and pod counts. Click it to expand the node and pod lists; filter pods by namespace if there are a lot of them.
On the cluster itself:
kubectl get pods -n gt-guardiankubectl logs -n gt-guardian deploy/gt-guardian-collectorShould show one Running pod, with [K8s] Node and Pod informers synced — watching cluster state in its logs.
Uninstalling
Section titled “Uninstalling”kubectl delete -f deployment.yamlkubectl delete -f rbac.yamlFrom the dashboard, also remove the cluster’s entry from Agents the same way you’d remove a host agent — deleting the Kubernetes resources above doesn’t do this automatically.
Multiple clusters
Section titled “Multiple clusters”Deploy the collector to each cluster you want visibility into — every cluster with its own enrollment shows up as its own entry, there’s no merged multi-cluster view.