PageSourceSearch

https://woodpecker-ci.org/assets/js/ea542b26.943f243c.js

js woodpecker-ci.org collected 2026-10-04 00:32:15 UTC 45,390 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkwoodpecker=self.webpackChunkwoodpecker||[]).push([["4583"],{3780(e,n,s){s.r(n),s.d(n,{metadata:()=>i,default:()=>h,frontMatter:()=>t,contentTitle:()=>a,toc:()=>l,assets:()=>c});var i=JSON.parse('{"id":"administration/configuration/backends/kubernetes","title":"Kubernetes","description":"The Kubernetes backend executes steps inside standalone Pods. A temporary PVC is created for the lifetime of the pipeline to transfer files between steps.","source":"@site/docs/30-administration/10-configuration/11-backends/20-kubernetes.md","sourceDirName":"30-administration/10-configuration/11-backends","slug":"/administration/configuration/backends/kubernetes","permalink":"/docs/next/administration/configuration/backends/kubernetes","draft":false,"unlisted":false,"editUrl":"https://github.com/woodpecker-ci/woodpecker/edit/main/docs/docs/30-administration/10-configuration/11-backends/20-kubernetes.md","tags":[],"version":"current","sidebarPosition":20,"frontMatter":{"toc_max_heading_level":2},"sidebar":"tutorialSidebar","previous":{"title":"Docker","permalink":"/docs/next/administration/configuration/backends/docker"},"next":{"title":"Local","permalink":"/docs/next/administration/configuration/backends/local"}}'),o=s(94686),r=s(23191);let t={toc_max_heading_level:2},a="Kubernetes",c={},l=[{value:"Metadata labels",id:"metadata-labels",level:2},{value:"Private registries",id:"private-registries",level:2},{value:"Step specific configuration",id:"step-specific-configuration",level:2},{value:"Resources",id:"resources",level:3},{value:"Runtime class",id:"runtime-class",level:3},{value:"Service account",id:"service-account",level:3},{value:"Workspace volume",id:"workspace-volume",level:3},{value:"User namespaces",id:"user-namespaces",level:3},{value:"Node selector",id:"node-selector",level:3},{value:"Tolerations",id:"tolerations",level:3},{value:"Affinity",id:"affinity",level:3},{value:"Agent-wide affinity",id:"agent-wide-affinity",level:4},{value:"Example: agent affinity for co-location",id:"example-agent-affinity-for-co-location",level:4},{value:"Example: Node affinity for GPU workloads",id:"example-node-affinity-for-gpu-workloads",level:4},{value:"Volumes",id:"volumes",level:3},{value:"Security context",id:"security-context",level:3},{value:"Annotations and labels",id:"annotations-and-labels",level:3},{value:"Tips and tricks",id:"tips-and-tricks",level:2},{value:"CRI-O",id:"cri-o",level:3},{value:"<code>KUBERNETES_SERVICE_HOST</code> environment variable",id:"kubernetes_service_host-environment-variable",level:3},{value:"Headless services",id:"headless-services",level:3},{value:"Environment variables",id:"environment-variables",level:2},{value:"BACKEND_K8S_NAMESPACE",id:"backend_k8s_namespace",level:3},{value:"BACKEND_K8S_NAMESPACE_PER_ORGANIZATION",id:"backend_k8s_namespace_per_organization",level:3},{value:"BACKEND_K8S_CLUSTER_DOMAIN",id:"backend_k8s_cluster_domain",level:3},{value:"BACKEND_K8S_VOLUME_SIZE",id:"backend_k8s_volume_size",level:3},{value:"BACKEND_K8S_STORAGE_CLASS",id:"backend_k8s_storage_class",level:3},{value:"BACKEND_K8S_STORAGE_RWX",id:"backend_k8s_storage_rwx",level:3},{value:"BACKEND_K8S_POD_LABELS",id:"backend_k8s_pod_labels",level:3},{value:"BACKEND_K8S_POD_LABELS_ALLOW_FROM_STEP",id:"backend_k8s_pod_labels_allow_from_step",level:3},{value:"BACKEND_K8S_POD_ANNOTATIONS",id:"backend_k8s_pod_annotations",level:3},{value:"BACKEND_K8S_POD_ANNOTATIONS_ALLOW_FROM_STEP",id:"backend_k8s_pod_annotations_allow_from_step",level:3},{value:"BACKEND_K8S_POD_TOLERATIONS",id:"backend_k8s_pod_tolerations",level:3},{value:"BACKEND_K8S_POD_TOLERATIONS_ALLOW_FROM_STEP",id:"backend_k8s_pod_tolerations_allow_from_step",level:3},{value:"BACKEND_K8S_POD_NODE_SELECTOR",id:"backend_k8s_pod_node_selector",level:3},{value:"BACKEND_K8S_POD_NODE_SELECTOR_ALLOW_FROM_STEP",id:"backend_k8s_pod_node_selector_allow_from_step",level:3},{value:"BACKEND_K8S_SECCTX_NONROOT \x3c!-- cspell:ignore SECCTX NONROOT --\x3e",id:"backend_k8s_secctx_nonroot-",level:3},{value:"BACKEND_K8S_USER_NAMESPACES",id:"backend_k8s_user_namespaces",level:3},{value:"BACKEND_K8S_USER_NAMESPACES_OVERRIDE_SECCTX_NONROOT \x3c!-- cspell:ignore SECCTX --\x3e",id:"backend_k8s_user_namespaces_override_secctx_nonroot-",level:3},{value:"BACKEND_K8S_PULL_SECRET_NAMES",id:"backend_k8s_pull_secret_names",level:3},{value:"BACKEND_K8S_PRIORITY_CLASS",id:"backend_k8s_priority_class",level:3}
1,{value:"BACKEND_K8S_PERMISSION_INIT_IMAGE",id:"backend_k8s_permission_init_image",level:3},{value:"BACKEND_K8S_SERVICE_ACCOUNT_NAME_ALLOW_FROM_STEP",id:"backend_k8s_service_account_name_allow_from_step",level:3},{value:"BACKEND_K8S_RUNTIME_CLASS_ALLOW_FROM_STEP",id:"backend_k8s_runtime_class_allow_from_step",level:3}];function d(e){let n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",hr:"hr",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,r.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.header,{children:(0,o.jsx)(n.h1,{id:"kubernetes",children:"Kubernetes"})}),"\n",(0,o.jsx)(n.p,{children:"The Kubernetes backend executes steps inside standalone Pods. A temporary PVC is created for the lifetime of the pipeline to transfer files between steps."}),"\n",(0,o.jsx)(n.h2,{id:"metadata-labels",children:"Metadata labels"}),"\n",(0,o.jsx)(n.p,{children:"Woodpecker adds some labels to the pods to provide additional context to the workflow. These labels can be used for various purposes, e.g. for simple debugging or as selectors for network policies."}),"\n",(0,o.jsx)(n.p,{children:"The following metadata labels are supported:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.code,{children:"woodpecker-ci.org/forge-id"})}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.code,{children:"woodpecker-ci.org/repo-forge-id"})}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.code,{children:"woodpecker-ci.org/repo-id"})}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.code,{children:"woodpecker-ci.org/repo-name"})}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.code,{children:"woodpecker-ci.org/repo-full-name"})}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.code,{children:"woodpecker-ci.org/branch"})}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.code,{children:"woodpecker-ci.org/org-id"})}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.code,{children:"woodpecker-ci.org/task-uuid"})}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.code,{children:"woodpecker-ci.org/step"})}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"private-registries",children:"Private registries"}),"\n",(0,o.jsxs)(n.p,{children:["In addition to ",(0,o.jsx)(n.a,{href:"/docs/next/usage/registries",children:"registries specified in the UI"}),", you may provide ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/",children:"registry credentials in Kubernetes Secrets"})," to pull private container images defined in your pipeline YAML."]}),"\n",(0,o.jsxs)(n.p,{children:["Place these Secrets in namespace defined by ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_NAMESPACE"})," and provide the Secret names to Agents via ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_PULL_SECRET_NAMES"}),"."]}),"\n",(0,o.jsx)(n.h2,{id:"step-specific-configuration",children:"Step specific configuration"}),"\n",(0,o.jsx)(n.h3,{id:"resources",children:"Resources"}),"\n",(0,o.jsxs)(n.p,{children:["The Kubernetes backend also allows for specifying requests and limits on a per-step basic, most commonly for CPU and memory.\nWe recommend to add a ",(0,o.jsx)(n.code,{children:"resources"})," definition to all steps to ensure efficient scheduling."]}),"\n",(0,o.jsxs)(n.p,{children:["Here is an example definition with an arbitrary ",(0,o.jsx)(n.code,{children:"resources"})," definition below the ",(0,o.jsx)(n.code,{children:"backend_options"})," section:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"steps:\n  - name: 'My kubernetes step'\n    image: alpine\n    commands:\n      - echo \"Hello world\"\n    backend_options:\n      kubernetes:\n        resources:\n          requests:\n            memory: 200Mi\n            cpu: 100m\n          limits:\n            memory: 400Mi\n            cpu: 1000m\n"})}),"\n",(0,o.jsxs)(n.p,{children:["You can use ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/concepts/policy/limit-range/",children:"Limit Ranges"})," if you want to set the limits by per-namespace basis."]}),"\n",(0,o.jsx)(n.h3,{id:"runtime-class",children:"Runtime class"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.code,{children:"runtimeClassName"})," specifies the name of the RuntimeClass which will be used to run this Pod. If no ",(0,o.jsx)(n.code,{children:"runtimeClassName"})," is specified, the default RuntimeHandler will be used.\nSee the ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/concepts/containers/runtime-class/",children:"Kubernetes documentation"})," for more information on specifying runtime classes."]}),"\n",(0,o.jsx)(n.h3,{id:"service-account",children:"Service account"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.code,{children:"serviceAccountName"})," specifies the name of the ServiceAccount which the Pod will mount. This service account must be created externally.\nSee the ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/concepts/security/service-accounts/",children:"Kubernetes documentation"})," for more information on using service accounts."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"steps:\n  - name: 'My kubernetes step'\n    image: alpine\n    commands:\n      - echo \"Hello world\"\n    backend_options:\n      kubernetes:\n        # Use the service account `default` in the current namespace.\n        # This usually the same as wherever woodpecker is deployed.\n        serviceAccountName: default\n"})}),"\n",(0,o.jsxs)(n.p,{children:["To give steps access to the Kubernetes API via service account, take a look at ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/reference/access-authn-authz/rbac/",children:"RBAC Authorization"})]}),"\n",(0,o.jsxs)(n.p,{children:["By default, setting ",(0,o.jsx)(n.code,{children:"serviceAccountName"})," from a step's backend options is ",(0,o.jsx)(n.strong,{children:"not allowed"})," for security reasons, as it would let any user with push access run pipeline pods under an arbitrary service account and inherit its permissions. To enable it, set ",(0,o.jsx)(n.a,{href:"#backend_k8s_service_account_name_allow_from_step",children:(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_SERVICE_ACCOUNT_NAME_ALLOW_FROM_STEP"})})," on the agent."]}),"\n",(0,o.jsx)(n.admonition,{type:"warning",children:(0,o.jsxs)(n.p,{children:["Enabling ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_SERVICE_ACCOUNT_NAME_ALLOW_FROM_STEP"})," in multi-tenant environments allows pipeline authors to run pods as any service account in the namespace, which may lead to privilege escalation. Only enable it if you trust everyone with push access."]})}
1),"\n",(0,o.jsx)(n.h3,{id:"workspace-volume",children:"Workspace volume"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.code,{children:"workspaceVolume"})," controls whether the default workspace volume is mounted into a service Pod. It only affects service\ncontainers and does not disable explicitly configured service volumes."]}),"\n",(0,o.jsx)(n.p,{children:"If unset, the default workspace volume is mounted."}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"services:\n  postgres:\n    image: postgres:16\n    backend_options:\n      kubernetes:\n        workspaceVolume: false\n"})}),"\n",(0,o.jsx)(n.h3,{id:"user-namespaces",children:"User namespaces"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.code,{children:"hostUsers"})," controls whether the Pod uses the host's user namespace. When set to ",(0,o.jsx)(n.code,{children:"false"}),", Kubernetes runs the Pod in a dedicated user namespace where UID 0 inside the container maps to a non-root UID on the host, providing an additional layer of isolation."]}),"\n",(0,o.jsxs)(n.p,{children:["See the ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/concepts/workloads/pods/user-namespaces/",children:"Kubernetes documentation"})," for more information on user namespaces."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"steps:\n  - name: build\n    image: alpine\n    commands:\n      - whoami\n    backend_options:\n      kubernetes:\n        hostUsers: false\n        securityContext:\n          runAsUser: 0\n"})}),"\n",(0,o.jsxs)(n.p,{children:["You can use ",(0,o.jsx)(n.a,{href:"#backend_k8s_user_namespaces",children:"WOODPECKER_BACKEND_K8S_USER_NAMESPACES"})," to run all pods in a dedicated user namespace by default."]}),"\n",(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["User namespaces require Kubernetes v1.25+ with the ",(0,o.jsx)(n.code,{children:"UserNamespacesSupport"})," feature gate enabled, and a compatible container runtime (e.g. CRI-O, containerd v2.0+)."]})}),"\n",(0,o.jsx)(n.h3,{id:"node-selector",children:"Node selector"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.code,{children:"nodeSelector"})," specifies the labels which are used to select the node on which the step will be executed."]}),"\n",(0,o.jsxs)(n.p,{children:["Labels defined here will be appended to a list which already contains ",(0,o.jsx)(n.code,{children:'"kubernetes.io/arch"'}),".\nBy default ",(0,o.jsx)(n.code,{children:'"kubernetes.io/arch"'}
1)," is inferred from the agents' platform. One can override it by setting that label in the ",(0,o.jsx)(n.code,{children:"nodeSelector"})," section of the ",(0,o.jsx)(n.code,{children:"backend_options"}),".\nWithout a manual overwrite, builds will be randomly assigned to the runners and inherit their respective architectures."]}),"\n",(0,o.jsxs)(n.p,{children:["To overwrite this, one needs to set the label in the ",(0,o.jsx)(n.code,{children:"nodeSelector"})," section of the ",(0,o.jsx)(n.code,{children:"backend_options"}),".\nA practical example for this is when running a matrix-build and delegating specific elements of the matrix to run on a specific architecture.\nIn this case, one must define an arbitrary key in the matrix section of the respective matrix element:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"matrix:\n  include:\n    - NAME: runner1\n      ARCH: arm64\n"})}),"\n",(0,o.jsxs)(n.p,{children:["And then overwrite the ",(0,o.jsx)(n.code,{children:"nodeSelector"})," in the ",(0,o.jsx)(n.code,{children:"backend_options"})," section of the step(s) using the name of the respective env var:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:'[...]\n    backend_options:\n      kubernetes:\n        nodeSelector:\n          kubernetes.io/arch: "${ARCH}"\n'})}),"\n",(0,o.jsxs)(n.p,{children:["You can use ",(0,o.jsx)(n.a,{href:"#backend_k8s_pod_node_selector",children:"WOODPECKER_BACKEND_K8S_POD_NODE_SELECTOR"})," if you want to set the node selector per Agent\nor ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#podnodeselector",children:"PodNodeSelector"})," admission controller if you want to set the node selector by per-namespace basis."]}),"\n",(0,o.jsxs)(n.p,{children:["By default, setting ",(0,o.jsx)(n.code,{children:"nodeSelector"})," from a step's backend options is ",(0,o.jsx)(n.strong,{children:"not allowed"}),", as it would otherwise let any user with push access pin pipeline pods onto chosen nodes. To enable it, set ",(0,o.jsx)(n.a,{href:"#backend_k8s_pod_node_selector_allow_from_step",children:(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_POD_NODE_SELECTOR_ALLOW_FROM_STEP"})})," on the agent."]}),"\n",(0,o.jsx)(n.h3,{id:"tolerations",children:"Tolerations"}),"\n",(0,o.jsxs)(n.p,{children:["When you use ",(0,o.jsx)(n.code,{children:"nodeSelector"})," and the node pool is configured with Taints, you need to specify the Tolerations. Tolerations allow the scheduler to schedule Pods with matching taints.\nSee the ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/",children:"Kubernetes documentation"})," for more information on using tolerations."]}),"\n",(0,o.jsx)(n.p,{children:"Example pipeline configuration:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"steps:\n  - name: build\n    image: golang\n    commands:\n      - go get\n      - go build\n      - go test\n    backend_options:\n      kubernetes:\n        serviceAccountName: 'my-service-account'\n        resources:\n          requests:\n            memory: 128Mi\n            cpu: 1000m\n          limits:\n            memory: 256Mi\n        nodeSelector:\n          beta.kubernetes.io/instance-type: Standard_D2_v3\n        tolerations:\n          - key: 'key1'\n            operator: 'Equal'\n            value: 'value1'\n            effect: 'NoSchedule'\n            tolerationSeconds: 3600\n        affinity:\n          nodeAffinity:\n            requiredDuringSchedulingIgnoredDuringExecution:\n              nodeSelectorTerms:\n                - matchExpressions:\n                    - key: topology.kubernetes.io/zone\n                      operator: In\n                      values:\n                        - eu-central-1a\n                        - eu-central-1b\n"})}),"\n",(0,o.jsx)(n.h3,{id:"affinity",children:"Affinity"}),"\n",(0,o.jsxs)(n.p,{children:["Kubernetes ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity",children:"affinity and anti-affinity"})," rules allow you to constrain which nodes your pods can be scheduled on based on node labels, or co-locate/spread pods relative to other pods."]}),"\n",(0,o.jsx)(n.p,{children:"You can configure affinity at two levels:"}),"\n",(0,o.jsxs)(n.ol,{children:["\n",(0,o.jsxs)(n.li,{children:[(0,o.jsxs)(n.strong,{children:["Per-step via ",(0,o.jsx)(n.code,{children:"backend_options.kubernetes.affinity"})]})," (shown in example above) - requires agent configuration to allow it"]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsxs)(n.strong,{children:["Agent-wide via ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_POD_AFFINITY"})]})," - applies to all pods unless overridden"]}),"\n"]}),"\n",(0,o.jsx)(n.h4,{id:"agent-wide-affinity",children:"Agent-wide affinity"}),"\n",(0,o.jsx)(n.p,{children:"To apply affinity rules to all workflow pods, configure the agent with YAML-formatted affinity:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:'WOODPECKER_BACKEND_K8S_POD_AFFINITY: |\n  nodeAffinity:\n    requiredDuringSchedulingIgnoredDuringExecution:\n      nodeSelectorTerms:\n        - matchExpressions:\n            - key: node-role.kubernetes.io/worker\n              operator: In\n              values:\n                - "true"\n'})}),"\n",(0,o.jsxs)(n.p,{children:["By default, per-step affinity settings are ",(0,o.jsx)(n.strong,{children:"not allowed"})," for security reasons. To enable them:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"WOODPECKER_BACKEND_K8S_POD_AFFINITY_ALLOW_FROM_STEP: true\n"})}),"\n",(0,o.jsx)(n.admonition,{type:"warning",children:(0,o.jsxs)(n.p,{children:["Enabling ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_POD_AFFINITY_ALLOW_FROM_STEP"})," in multi-tenant environments allows pipeline authors to control pod placement, which may have security or resource isolation implications."]})}),"\n",(0,o.jsxs)(n.p,{children:["When per-step affinity is allowed and specified, it ",(0,o.jsx)(n.strong,{children:"replaces"})," the agent-wide affinity entirely (not merged)."]}),"\n",(0,o.jsx)(n.h4,{id:"example-agent-affinity-for-co-location",children:"Example: agent affinity for co-location"}),"\n",(0,o.jsx)(n.p,{children:"This example configures all workflow pods within a workflow to be co-located on the same node, while requiring other workflows run on different nodes."}),"\n",(0,o.jsxs)(n.p,{children:["It uses ",(0,o.jsx)(n.code,{children:"matchLabelKeys"})," to dynamically match pods with the same ",(0,o.jsx)(n.code,{children:"woodpecker-ci.org/task-uuid"}),", and ",(0,o.jsx)(n.code,{children:"mismatchLabelKeys"})," to separating pods with different task UUIDs:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:'WOODPECKER_BACKEND_K8S_POD_AFFINITY: |\n  podAffinity:\n    requiredDuringSchedulingIgnoredDuringExecution:\n    - labelSelector: {}\n      matchLabelKeys:\n        - woodpecker-ci.org/task-uuid\n      topologyKey: "kubernetes.io/hostname"\n  podAntiAffinity:\n    requiredDuringSchedulingIgnoredDuringExecution:\n    - labelSelector: {}\n      mismatchLabelKeys:\n      - woodpecker-ci.org/task-uuid\n      topologyKey: "kubernetes.io/hostname"\n'})}),"\n",(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"matchLabelKeys"})," and ",(0,o.jsx)(n.code,{children:"mismatchLabelKeys"})," features require Kubernetes v1.29+ (alpha with feature gate ",(0,o.jsx)(n.code,{children:"MatchLabelKeysInPodAffinity"}),") or v1.33+ (beta, enabled by default). These fields allow the Kubernetes API server to dynamically populate label selectors at pod creation time, eliminating the need to hardcode values like ",(0,o.jsx)(n.code,{children:"$(WOODPECKER_TASK_UUID)"}),"."]})}),"\n",(0,o.jsx)(n.h4,{id:"example-node-affinity-for-gpu-workloads",children:"Example: Node affinity for GPU workloads"}),"\n",(0,o.jsx)(n.p,{children:"Ensure a step runs only on GPU-enabled nodes:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"steps:\n  - name: train-model\n    image: tensorflow/tensorflow:latest-gpu\n    backend_options:\n      kubernetes:\n        affinity:\n          nodeAffinity:\n            requiredDuringSchedulingIgnoredDuringExecution:\n              nodeSelectorTerms:\n                - matchExpressions:\n                    - key: accelerator\n                      operator: In\n                      values:\n                        - nvidia-tesla-v100\n"})}),"\n",(0,o.jsx)(n.h3,{id:"volumes",children:"Volumes"}),"\n",(0,o.jsxs)(n.p,{children:["To mount volumes a PersistentVolume (PV) and PersistentVolumeClaim (PVC) are needed on the cluster which can be referenced in steps via the ",(0,o.jsx)(n.code,{children:"volumes"})," option."]}),"\n",(0,o.jsxs)(n.p,{children:["Persistent volumes must be created manually. Use the Kubernetes ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/concepts/storage/persistent-volumes/",children:"Persistent Volumes"})," documentation as a reference."]}),"\n",(0,o.jsx)(n.p,{children:(0,o.jsxs)(n.em,{children:["If your PVC is not highly available or NFS-based, use the ",(0,o.jsx)(n.code,{children:"affinity"})," settings (documented above) to ensure that your steps are executed on the correct node."]})}),"\n",(0,o.jsxs)(n.p,{children:["NOTE: If you plan to use this volume in more than one workflow concurrently, make sure you have configured the PVC in ",(0,o.jsx)(n.code,{children:"RWX"})," mode. Keep in mind that this feature must be supported by the used CSI driver:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"accessModes:\n  - ReadWriteMany\n"})}),"\n",(0,o.jsxs)(n.p,{children:["Assuming a PVC named ",(0,o.jsx)(n.code,{children:"woodpecker-cache"})," exists, it can be referenced as follows in a plugin step:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:'steps:\n  - name: "Restore Cache"\n    image: meltwater/drone-cache\n    volumes:\n      - woodpecker-cache:/woodpecker/src/cache\n    settings:\n      mount:\n        - "woodpecker-cache"\n    [...]\n'})}),"\n",(0,o.jsx)(n.p,{children:"Or as follows when using a normal image:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:'steps:\n  - name: "Edit cache"\n    image: alpine:latest\n    volumes:\n      - woodpecker-cache:/woodpecker/src/cache\n    commands:\n      - echo "Hello World" > /woodpecker/src/cache/output.txt\n    [...]\n'})}),"\n",(0,o.jsx)(n.h3,{id:"security-context",children:"Security context"}),"\n",(0,o.jsxs)(n.p,{children:["Use the following configuration to set the ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/tasks/configure-pod-container/security-context/",children:"Security Context"})," for the Pod/container running a given pipeline step:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"steps:\n  - name: test\n    image: alpine\n    commands:\n      - echo Hello world\n    backend_options:\n      kubernetes:\n        securityContext:\n          runAsUser: 999\n          runAsGroup: 999\n          privileged: true\n    [...]\n"})}),"\n",(0,o.jsxs)(n.p,{children:["Note that the ",(0,o.jsx)(n.code,{children:"backend_options.kubernetes.securityContext"})," object allows you to set both Pod and container level security context options in one object.\nBy default, the properties will be set at the Pod level. Properties that are only supported on the container level will be set there instead. So, the\nconfiguration shown above will result in something like the following Pod spec:"]}),"\n","\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"kind: Pod\nspec:\n  securityContext:\n    runAsUser: 999\n    runAsGroup: 999\n  containers:\n    - name: wp-01hcd83q7be5ymh89k5accn3k6-0-step-0\n      image: alpine\n      securityContext:\n        privileged: true\n  [...]\n"})}),"\n","\n",(0,o.jsxs)(n.p,{children:["You can also restrict a syscalls of containers with ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/tutorials/security/seccomp/",children:"seccomp"})," profile."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"backend_options:\n  kubernetes:\n    securityContext:\n      seccompProfile:\n        type: Localhost\n        localhostProfile: profiles/audit.json\n"})}),"\n",(0,o.jsxs)(n.p,{children:["or restrict a container's access to resources by specifying ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/tutorials/security/apparmor/",children:"AppArmor"})," profile"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"backend_options:\n  kubernetes:\n    securityContext:\n      apparmorProfile:\n        type: Localhost\n        localhostProfile: k8s-apparmor-example-deny-write\n"})}),"\n",(0,o.jsxs)(n.p,{children:["or configure a specific ",(0,o.jsx)(n.code,{children:"fsGroupChangePolicy"})," (Kubernetes defaults to 'Always')"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"backend_options:\n  kubernetes:\n    securityContext:\n      fsGroupChangePolicy: OnRootMismatch\n"})}),"\n",(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsx)(n.p,{children:"The feature requires Kubernetes v1.30 or above."})}),"\n",(0,o.jsxs)(n.p,{children:["You can set ",(0,o.jsx)(n.code,{children:"allowPrivilegeEscalation"})," to ",(0,o.jsx)(n.code,{children:"false"})," to prevent a container from gaining more privileges than its parent process."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"backend_options:\n  kubernetes:\n    securityContext:\n      allowPrivilegeEscalation: false\n"})}),"\n",(0,o.jsxs)(n.p,{children:["You can also drop ",(0,o.jsx)(n.a,{href:"https://man7.org/linux/man-pages/man7/capabilities.7.html",children:"Linux capabilities"})," from a container. Adding capabilities is not allowed."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"backend_options:\n  kubernetes:\n    securityContext:\n      capabilities:\n        drop:\n          - ALL\n"})}),"\n",(0,o.jsx)(n.h3,{id:"annotations-and-labels",children:"Annotations and labels"}),"\n",(0,o.jsxs)(n.p,{children:["You can specify arbitrary ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/concepts/
1overview/working-with-objects/annotations/",children:"annotations"})," and ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/",children:"labels"})," to be set on the Pod definition for a given workflow step using the following configuration:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"backend_options:\n  kubernetes:\n    annotations:\n      workflow-group: alpha\n      io.kubernetes.cri-o.Devices: /dev/fuse\n    labels:\n      environment: ci\n      app.kubernetes.io/name: builder\n"})}),"\n",(0,o.jsxs)(n.p,{children:["In order to enable this configuration you need to set the appropriate environment variables to ",(0,o.jsx)(n.code,{children:"true"})," on the woodpecker agent:\n",(0,o.jsx)(n.a,{href:"#backend_k8s_pod_annotations_allow_from_step",children:"WOODPECKER_BACKEND_K8S_POD_ANNOTATIONS_ALLOW_FROM_STEP"})," and/or ",(0,o.jsx)(n.a,{href:"#backend_k8s_pod_labels_allow_from_step",children:"WOODPECKER_BACKEND_K8S_POD_LABELS_ALLOW_FROM_STEP"}),"."]}),"\n",(0,o.jsx)(n.h2,{id:"tips-and-tricks",children:"Tips and tricks"}),"\n",(0,o.jsx)(n.h3,{id:"cri-o",children:"CRI-O"}),"\n",(0,o.jsx)(n.p,{children:"CRI-O users currently need to configure the workspace for all workflows in order for them to run correctly. Add the following at the beginning of your configuration:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"workspace:\n  base: '/woodpecker'\n  path: '/'\n"})}),"\n",(0,o.jsxs)(n.p,{children:["See ",(0,o.jsx)(n.a,{href:"https://github.com/woodpecker-ci/woodpecker/issues/2510",children:"this issue"})," for more details."]}),"\n",(0,o.jsxs)(n.h3,{id:"kubernetes_service_host-environment-variable",children:[(0,o.jsx)(n.code,{children:"KUBERNETES_SERVICE_HOST"})," environment variable"]}),"\n",(0,o.jsx)(n.p,{children:"Like the below env vars used for configuration, this can be set in the environment for configuration of the agent.\nIt configures the address of the Kubernetes API server to connect to."}),"\n",(0,o.jsx)(n.p,{children:"If running the agent within Kubernetes, this will already be set and you don't have to add it manually."}),"\n",(0,o.jsx)(n.h3,{id:"headless-services",children:"Headless services"}),"\n",(0,o.jsxs)(n.p,{children:["For each workflow run a ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/concepts/services-networking/service/#headless-services",children:"headless services"})," is created,\nand all steps asigned the subdomain that matches the headless service, so any step can reach other steps via DNS by using the step name as hostname."]}),"\n",(0,o.jsx)(n.p,{children:"Using the headless services, the step pod is connected to directly, so any port on the other step pods can be reached."}),"\n",(0,o.jsx)(n.p,{children:"This is useful for some use-cases, like test-containers in a docker-in-docker setup, where the step needs to connect to many ports on the docker host service."}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"steps:\n  - name: test\n    image: docker:cli # use 'docker:<major-version>-cli' or similar in production\n    environment:\n      DOCKER_HOST: 'tcp://docker:2376'\n      DOCKER_CERT_PATH: '/woodpecker/dind-certs/client'\n      DOCKER_TLS_VERIFY: '1'\n    commands:\n      - docker run hello-world\n\n  - name: docker\n    image: docker:dind # use 'docker:<major-version>-dind' or similar in production\n    detach: true\n    privileged: true\n    environment:\n      DOCKER_TLS_CERTDIR: /woodpecker/dind-certs\n"})}),"\n",(0,o.jsx)(n.p,{children:"If ports are defined on a service, then woodpecker will create a normal service for the pod, which use hosts override using the services cluster IP."}),"\n",(0,o.jsx)(n.h2,{id:"environment-variables",children:"Environment variables"}),"\n",(0,o.jsxs)(n.p,{children:["These env vars can be set in the ",(0,o.jsx)(n.code,{children:"env:"})," sections of the agent."]}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_namespace",children:"BACKEND_K8S_NAMESPACE"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_NAMESPACE"})]}),"\n",(0,o.jsxs)(n.li,{children:["Default: ",(0,o.jsx)(n.code,{children:"woodpecker"})]}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"The namespace to create worker Pods in."}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_namespace_per_organization",children:"BACKEND_K8S_NAMESPACE_PER_ORGANIZATION"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_NAMESPACE_PER_ORGANIZATION"})]}),"\n",(0,o.jsxs)(n.li,{children:["Default: ",(0,o.jsx)(n.code,{children:"false"})]}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"Enables namespace isolation per Woodpecker organization. When enabled, each organization gets its own dedicated Kubernetes namespace for improved security and resource isolation."}),"\n",(0,o.jsxs)(n.p,{children:["With this feature enabled, Woodpecker creates separate Kubernetes namespaces for each organization using the format ",(0,o.jsx)(n.code,{children:"{WOODPECKER_BACKEND_K8S_NAMESPACE}-{organization-id}"}),". Namespaces are created automatically when needed, but they are not automatically deleted when organizations are removed from Woodpecker."]}
1),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_cluster_domain",children:"BACKEND_K8S_CLUSTER_DOMAIN"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_CLUSTER_DOMAIN"})]}),"\n",(0,o.jsxs)(n.li,{children:["Default: ",(0,o.jsx)(n.code,{children:"cluster.local"})]}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"The DNS domain of the Kubernetes cluster. It is used to build the DNS search entry that lets a step reach a service by its hostname."}),"\n",(0,o.jsx)(n.p,{children:"Set this value if your cluster was installed with a custom cluster domain."}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_volume_size",children:"BACKEND_K8S_VOLUME_SIZE"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_VOLUME_SIZE"})]}),"\n",(0,o.jsxs)(n.li,{children:["Default: ",(0,o.jsx)(n.code,{children:"10G"})]}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"The volume size of the pipeline volume."}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_storage_class",children:"BACKEND_K8S_STORAGE_CLASS"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_STORAGE_CLASS"})]}),"\n",(0,o.jsx)(n.li,{children:"Default: none"}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"The storage class to use for the pipeline volume."}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_storage_rwx",children:"BACKEND_K8S_STORAGE_RWX"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_STORAGE_RWX"})]}),"\n",(0,o.jsxs)(n.li,{children:["Default: ",(0,o.jsx)(n.code,{children:"true"})]}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["Determines if ",(0,o.jsx)(n.code,{children:"RWX"})," should be used for the pipeline volume's ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes",children:"access mode"}),". If false, ",(0,o.jsx)(n.code,{children:"RWO"})," is used instead."]}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_pod_labels",children:"BACKEND_K8S_POD_LABELS"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_POD_LABELS"})]}),"\n",(0,o.jsx)(n.li,{children:"Default: none"}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["Additional labels to apply to worker Pods. Must be a YAML object, e.g. ",(0,o.jsx)(n.code,{children:'{"example.com/test-label":"test-value"}'}),"."]}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_pod_labels_allow_from_step",children:"BACKEND_K8S_POD_LABELS_ALLOW_FROM_STEP"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_POD_LABELS_ALLOW_FROM_STEP"})]}),"\n",(0,o.jsxs)(n.li,{children:["Default: ",(0,o.jsx)(n.code,{children:"false"})]}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"Determines if additional Pod labels can be defined from a step's backend options."}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_pod_annotations",children:"BACKEND_K8S_POD_ANNOTATIONS"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_POD_ANNOTATIONS"})]}),"\n",(0,o.jsx)(n.li,{children:"Default: none"}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["Additional annotations to apply to worker Pods. Must be a YAML object, e.g. ",(0,o.jsx)(n.code,{children:'{"example.com/test-annotation":"test-value"}'}),"."]}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_pod_annotations_allow_from_step",children:"BACKEND_K8S_POD_ANNOTATIONS_ALLOW_FROM_STEP"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_POD_ANNOTATIONS_ALLOW_FROM_STEP"})]}),"\n",(0,o.jsxs)(n.li,{children:["Default: ",(0,o.jsx)(n.code,{children:"false"})]}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"Determines if Pod annotations can be defined from a step's backend options."}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_pod_tolerations",children:"BACKEND_K8S_POD_TOLERATIONS"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_POD_TOLERATIONS"})]}),"\n",(0,o.jsx)(n.li,{children:"Default: none"}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["Additional tolerations to apply to worker Pods. Must be a YAML object, e.g. ",(0,o.jsx)(n.code,{children:'[{"effect":"NoSchedule","key":"jobs","operator":"Exists"}]'}),"."]}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_pod_tolerations_allow_from_step",children:"BACKEND_K8S_POD_TOLERATIONS_ALLOW_FROM_STEP"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_POD_TOLERATIONS_ALLOW_FROM_STEP"})]}),"\n",(0,o.jsxs)(n.li,{children:["Default: ",(0,o.jsx)(n.code,{children:"true"})]}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"Determines if Pod tolerations can be defined from a step's backend options."}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_pod_node_selector",children:"BACKEND_K8S_POD_NODE_SELECTOR"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_POD_NODE_SELECTOR"})]}),"\n",(0,o.jsx)(n.li,{children:"Default: none"}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["Additional node selector to apply to worker pods. Must be a YAML object, e.g. ",(0,o.jsx)(n.code,{children:'{"topology.kubernetes.io/region":"eu-central-1"}'}),"."]}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_pod_node_selector_allow_from_step",children:"BACKEND_K8S_POD_NODE_SELECTOR_ALLOW_FROM_STEP"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_POD_NODE_SELECTOR_ALLOW_FROM_STEP"})]}),"\n",(0,o.jsxs)(n.li,{children:["Default: ",(0,o.jsx)(n.code,{children:"false"})]}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["Determines if the Pod ",(0,o.jsx)(n.code,{children:"nodeSelector"})," can be defined from a step's backend options. Disabled by default, as it would otherwise let any user with push access pin pipeline pods onto chosen nodes."]}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_secctx_nonroot-",children:"BACKEND_K8S_SECCTX_NONROOT "}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_SECCTX_NONROOT"})]}),"\n",(0,o.jsxs)(n.li,{children:["Default: ",(0,o.jsx)(n.code,{children:"false"})]}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"Determines if containers must be required to run as non-root users."}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_user_namespaces",children:"BACKEND_K8S_USER_NAMESPACES"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_USER_NAMESPACES"})]}),"\n",(0,o.jsxs)(n.li,{children:["Default: ",(0,o.jsx)(n.code,{children:"false"})]}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["Default to running containers in a dedicated user namespace (i.e. ",(0,o.jsx)(n.code,{children:"hostUsers: false"}),") where ",(0,o.jsx)(n.code,{children:"root"})," is mapped to a ",(0,o.jsx)(n.code,{children:"non-root"})," user on the host."]}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_user_namespaces_override_secctx_nonroot-",children:"BACKEND_K8S_USER_NAMESPACES_OVERRIDE_SECCTX_NONROOT "}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_USER_NAMESPACES_OVERRIDE_SECCTX_NONROOT"})]}),"\n",(0,o.jsxs)(n.li,{children:["Default: ",(0,o.jsx)(n.code,{children:"false"})]}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["Allow containers running in a custom user namespace to override ",(0,o.jsx)(n.code,{children:"BACKEND_K8S_SECCTX_NONROOT"})," and run as ",(0,o.jsx)(n.code,{children:"root"}),"."]}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_pull_secret_names",children:"BACKEND_K8S_PULL_SECRET_NAMES"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_PULL_SECRET_NAMES"})]}),"\n",(0,o.jsx)(n.li,{children:"Default: none"}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["Secret names to pull images from private repositories. See, how to ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/",children:"Pull an Image from a Private Registry"}),"."]}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_priority_class",children:"BACKEND_K8S_PRIORITY_CLASS"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_PRIORITY_CLASS"})]}),"\n",(0,o.jsx)(n.li,{children:"Default: none, which will use the default priority class configured in Kubernetes"}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["Which ",(0,o.jsx)(n.a,{href:"https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/priority-class-v1/",children:"Kubernetes PriorityClass"})," to assign to created job pods."]}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_permission_init_image",children:"BACKEND_K8S_PERMISSION_INIT_IMAGE"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_PERMISSION_INIT_IMAGE"})]}),"\n",(0,o.jsx)(n.li,{children:"Default: 'busybox:stable-musl'"}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"Container image used for the workspace permission init container, which is used to create the workspace directory and ensure correct permissions when running steps as non-root users."}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_service_account_name_allow_from_step",children:"BACKEND_K8S_SERVICE_ACCOUNT_NAME_ALLOW_FROM_STEP"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_SERVICE_ACCOUNT_NAME_ALLOW_FROM_STEP"})]}),"\n",(0,o.jsxs)(n.li,{children:["Default: ",(0,o.jsx)(n.code,{children:"false"})]}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["Determines if the Pod ",(0,o.jsx)(n.code,{children:"serviceAccountName"})," can be defined from a step's backend options. Disabled by default, as it would otherwise allow any user with push access to run pods under an arbitrary service account and escalate privileges."]}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h3,{id:"backend_k8s_runtime_class_allow_from_step",children:"BACKEND_K8S_RUNTIME_CLASS_ALLOW_FROM_STEP"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Name: ",(0,o.jsx)(n.code,{children:"WOODPECKER_BACKEND_K8S_RUNTIME_CLASS_ALLOW_FROM_STEP"})]}),"\n",(0,o.jsxs)(n.li,{children:["Default: ",(0,o.jsx)(n.code,{children:"false"})]}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["Determines if the Pod ",(0,o.jsx)(n.code,{children:"runtimeClassName"})," can be defined from a step's backend options. Disabled by default, as it would otherwise allow any user with push access to run pods under an arbitrary runtime class."]})]})}function h(e={}){let{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},23191(e,n,s){s.d(n,{R:()=>t,x:()=>a});var i=s(92990);let o={},r=i.createContext(o);function t(e){let n=i.useContext(r);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:t(e.components),i.createElement(r.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.