Runners on Kubernetes
The kubernetes executor creates a pod per job: an init container fetches the workspace, the job’s container.image (or a default) runs the steps, service containers run as sidecars, and the pod is deleted afterwards. Nothing is shared between jobs.
With the Helm chart
Section titled “With the Helm chart”The Slipway chart can deploy runners next to (or without) the control plane. They are off by default because runners usually live next to the workloads they build, often in another cluster.
runner: enabled: true executor: kubernetes # kubernetes | docker (docker-in-docker, privileged) workload: Deployment # or DaemonSet replicaCount: 2 serverUrl: https://slipway.example.com labels: [linux, k8s] capacity: 4 # concurrent jobs per runner process token: existingSecret: slipway-runner-token # key: token kubernetes: jobNamespace: slipway-jobs rbac: true # Role + RoleBinding for pods, secrets, PVCs in jobNamespace jobServiceAccount: default defaultJobImage: docker.io/library/ubuntu:24.04 terminationGracePeriodSeconds: 3600 # let running jobs finish when drainingkubectl create namespace slipway-jobskubectl -n slipway create secret generic slipway-runner-token --from-literal=token=<registration token>helm upgrade --install slipway-runners ./deploy/helm/slipway -n slipway -f values.yamlPlain manifests
Section titled “Plain manifests”A minimal runner Deployment, ServiceAccount and Role:
apiVersion: v1kind: ServiceAccountmetadata: { name: slipway-runner, namespace: slipway }---apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: { name: slipway-runner, namespace: slipway-jobs }rules: - apiGroups: [""] resources: [pods, pods/log, pods/exec, secrets, persistentvolumeclaims] verbs: [create, get, list, watch, delete]---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: { name: slipway-runner, namespace: slipway-jobs }subjects: [{ kind: ServiceAccount, name: slipway-runner, namespace: slipway }]roleRef: { apiGroup: rbac.authorization.k8s.io, kind: Role, name: slipway-runner }---apiVersion: apps/v1kind: Deploymentmetadata: { name: slipway-runner, namespace: slipway }spec: replicas: 2 selector: { matchLabels: { app: slipway-runner } } template: metadata: { labels: { app: slipway-runner } } spec: serviceAccountName: slipway-runner terminationGracePeriodSeconds: 3600 securityContext: { runAsNonRoot: true, runAsUser: 10001, seccompProfile: { type: RuntimeDefault } } containers: - name: runner image: ghcr.io/slipway-systems/slipway-runner:latest args: [runner] env: - { name: SLIPWAY_RUNNER_SERVER, value: https://slipway.example.com } - { name: SLIPWAY_RUNNER_EXECUTOR, value: kubernetes } - { name: SLIPWAY_RUNNER_K8S_NAMESPACE, value: slipway-jobs } - { name: SLIPWAY_RUNNER_LABELS, value: "linux,k8s" } - { name: SLIPWAY_RUNNER_CONCURRENCY, value: "4" } - name: SLIPWAY_RUNNER_TOKEN valueFrom: { secretKeyRef: { name: slipway-runner-token, key: token } } securityContext: { allowPrivilegeEscalation: false, readOnlyRootFilesystem: false, capabilities: { drop: [ALL] } } resources: { requests: { cpu: 100m, memory: 128Mi }, limits: { memory: 1Gi } }Verify the exact verbs the runner needs for your version with slipway runner check against the cluster; the Helm chart’s Role is the reference.
Job pod settings (runner config)
Section titled “Job pod settings (runner config)”# runner config filekubernetes: namespace: slipway-jobs service_account: default # account the job pod runs as default_image: ubuntu:24.04 init_image: alpine/git:latest workspace_size: 10Gi image_pull_secrets: [registry-creds] node_selector: { workload: ci } cpu_request: 500m cpu_limit: "4" memory_request: 1Gi memory_limit: 8Gi pod_start_timeout: 5mDocker-in-Docker (not recommended)
Section titled “Docker-in-Docker (not recommended)”executor: docker in the chart runs the runner with a privileged rootful dind sidecar. Use it only on isolated, tainted nodes. Prefer the Kubernetes executor with a rootless builder (BuildKit, Kaniko or Buildah) for image builds.
Network and policy
Section titled “Network and policy”Runners only need outbound access to the Slipway server (and to registries and SCMs your jobs use). The chart can generate NetworkPolicies (networkPolicy.enabled, networkPolicy.egress.restrict). Job pods in jobNamespace should be denied access to the cluster API and cloud metadata endpoints unless a job needs them.