Docs navigation
Get started
AI agents
Connect
Operate
Day to day
When it breaks
Govern access
Team & account
Access
Identity concepts
Provider guides
Account
Kubernetes DaemonSet
Run one runner per node with a DaemonSet. The pod spec controls what each runner can read or change. This example uses the official image and persistent host storage.
To read what a containerized runner can reach, see Install in a container.
-
kubectlaccess that can create namespaces, Secrets, and DaemonSets. -
Outbound HTTPS from every node to
emisar.dev:443andregistry.emisar.dev:443. See Network requirements for the optional GitHub fallback domains.
Prepare the deployment#
This manifest runs as root and reads node processes, networks, journals, and files. Remove every grant that your packs do not need.
-
Create a namespace named
emisar-system. Configure its admission policy to allow this host-access profile. - Create a reusable enrollment key.
- Create the Secret. Enter the key at the hidden prompt.
kubectl create namespace emisar-system read -rsp "Enrollment key: " EMISAR_ENROLLMENT_KEY printf '\n' kubectl -n emisar-system create secret generic emisar-runner \ --from-literal=enrollment-key="$EMISAR_ENROLLMENT_KEY" unset EMISAR_ENROLLMENT_KEY
Pick the image: each runner release notes its image digest. Replace
<image-digest>
with the 64 hex characters after sha256:.
The DaemonSet#
This spec lets the runner observe each node. The node name is the runner identity, and the runner token, dispatch state, and local journal stay on the node.
apiVersion: apps/v1 kind: DaemonSet metadata: name: emisar-runner namespace: emisar-system spec: selector: matchLabels: app: emisar-runner template: metadata: labels: app: emisar-runner spec: hostPID: true # see every process on the node hostNetwork: true # see the node's real interfaces and ports containers: - name: runner image: ghcr.io/andrewdryga/emisar-runner@sha256:<image-digest> securityContext: runAsUser: 0 # journald + /host reads are root-only; see below env: - name: EMISAR_GROUP value: "k8s-nodes" # actions can target the whole group — one per fleet - name: EMISAR_RUNNER_ID valueFrom: fieldRef: fieldPath: spec.nodeName # the node, not the pod, is the identity - name: EMISAR_ENROLLMENT_KEY valueFrom: secretKeyRef: name: emisar-runner key: enrollment-key volumeMounts: - name: state mountPath: /var/lib/emisar - name: audit-journal mountPath: /var/log/emisar - name: host-root # node filesystem, read-only, for file inspection mountPath: /host readOnly: true - name: journal # journald logs on systemd nodes mountPath: /var/log/journal readOnly: true - name: systemd-run # systemd state, for unit status reads mountPath: /run/systemd readOnly: true volumes: - name: state hostPath: path: /var/lib/emisar type: DirectoryOrCreate - name: audit-journal hostPath: path: /var/log/emisar type: DirectoryOrCreate - name: host-root hostPath: path: / - name: journal hostPath: path: /var/log/journal - name: systemd-run hostPath: path: /run/systemd
Deploy and verify#
- Save the manifest as
emisar-runner.yaml. - Run
kubectl apply -f emisar-runner.yaml. -
Run
kubectl -n emisar-system rollout status daemonset/emisar-runner --timeout=5m. - Open Runners. Confirm each node has one online runner. Tainted control-plane nodes are skipped unless you add a toleration.
- Run
debugging.loadavgon one node runner. - Open Audit. Confirm that the event records the runner, action, operator, and reason.
What the pod spec grants#
hostPID
exposes node processes. hostNetwork
exposes node interfaces and ports. The read-only mounts expose the journal, systemd state,
and node files under /host.
The manifest uses runAsUser: 0
because most node files and journals require root; the image otherwise runs as user 65532. Actions run the programs inside the image, and the official image
carries only its documented observation tools — if a pack needs another program, build an extended image.
The host mounts are read-only except for runner state and its local journal. Remove runAsUser, hostPID, hostNetwork, or any mount your
packs do not need.
Letting a node action mutate#
A node mutation needs more than the read-only profile. Restarting a unit, for example,
needs writable systemd and D-Bus sockets — mount
/run/systemd
and /run/dbus
writable. Grant exactly what the intended actions need and nothing more; the runner's user
and polkit pattern on the
Linux host page
is the same discipline on a host.
After it is connected#
Manage the runner fleet covers work after the first connection — groups, labels, pack credentials, updates, reconnects, and removal. To roll the DaemonSet across clusters in phases, see the Go to production.