Actions Runner Controller
Introduction
Section titled “Introduction”Actions Runner Controller (ARC) is a Kubernetes operator that orchestrates and scales self-hosted runners for Gitea Actions. For more information, see Operator pattern in the Kubernetes documentation.
With ARC, you can create runner scale sets that automatically scale based on the number of workflows running in your repository, organization, or enterprise. Because controlled runners can be ephemeral and based on containers, new runner instances can scale up or down rapidly and cleanly.
Prerequisites
Section titled “Prerequisites”In order to use ARC, ensure you have the following.
- Gitea Enterprise Edition version number greater than 23.8.0, activated
- A Kubernetes cluster
Local Persistent Volume
Section titled “Local Persistent Volume”kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.31/deploy/local-path-storage.yamlA successful output will look similar to the following.
namespace/local-path-storage createdserviceaccount/local-path-provisioner-service-account createdrole.rbac.authorization.k8s.io/local-path-provisioner-role createdWarning: resource clusterroles/local-path-provisioner-role is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.clusterrole.rbac.authorization.k8s.io/local-path-provisioner-role configuredrolebinding.rbac.authorization.k8s.io/local-path-provisioner-bind createdWarning: resource clusterrolebindings/local-path-provisioner-bind is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.clusterrolebinding.rbac.authorization.k8s.io/local-path-provisioner-bind configureddeployment.apps/local-path-provisioner createdWarning: resource storageclasses/local-path is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.storageclass.storage.k8s.io/local-path configuredconfigmap/local-path-config createdFor more information about local-path-provisioner, see local-path-provisioner README
Installing Actions Runner Controller
Section titled “Installing Actions Runner Controller”cat > install.yml << EOFapiVersion: v1kind: Namespacemetadata: labels: app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: runner-controller control-plane: controller-manager name: runner-controller-system---apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata: annotations: controller-gen.kubebuilder.io/version: v0.16.1 name: runners.gitea.comspec: group: gitea.com names: kind: Runner listKind: RunnerList plural: runners singular: runner scope: Namespaced versions: - name: v1 schema: openAPIV3Schema: description: Runner is the Schema for the runners API properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: RunnerSpec defines the desired state of Runner properties: affinity: description: Affinity is the affinity for the runner pods properties: nodeAffinity: description: Describes node affinity scheduling rules for the pod. properties: preferredDuringSchedulingIgnoredDuringExecution: description: |- The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding "weight" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred. items: description: |- An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). properties: preference: description: A node selector term, associated with the corresponding weight. properties: matchExpressions: description: A list of node selector requirements by node's labels. items: description: |- A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: description: |- Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: description: |- An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchFields: description: A list of node selector requirements by node's fields. items: description: |- A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: description: |- Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: description: |- An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic type: object x-kubernetes-map-type: atomic weight: description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. format: int32 type: integer required: - preference - weight type: object type: array x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: description: |- If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to an update), the system may or may not try to eventually evict the pod from its node. properties: nodeSelectorTerms: description: Required. A list of node selector terms. The terms are ORed. items: description: |- A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. properties: matchExpressions: description: A list of node selector requirements by node's labels. items: description: |- A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: description: |- Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: description: |- An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchFields: description: A list of node selector requirements by node's fields. items: description: |- A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: description: |- Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: description: |- An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic type: object x-kubernetes-map-type: atomic type: array x-kubernetes-list-type: atomic required: - nodeSelectorTerms type: object x-kubernetes-map-type: atomic type: object podAffinity: description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: description: |- The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) properties: podAffinityTerm: description: Required. A pod affinity term, associated with the corresponding weight. properties: labelSelector: description: |- A label query over a set of resources, in this case pods. If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic matchLabelKeys: description: |- MatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both matchLabelKeys and labelSelector. Also, matchLabelKeys cannot be set when labelSelector isn't set. This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array x-kubernetes-list-type: atomic mismatchLabelKeys: description: |- MismatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. Also, mismatchLabelKeys cannot be set when labelSelector isn't set. This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array x-kubernetes-list-type: atomic namespaceSelector: description: |- A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: description: |- namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array x-kubernetes-list-type: atomic topologyKey: description: |- This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: description: |- weight associated with matching the corresponding podAffinityTerm, in the range 1-100. format: int32 type: integer required: - podAffinityTerm - weight type: object type: array x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: description: |- If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied. items: description: |- Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key <topologyKey> matches that of any node on which a pod of the set of pods is running properties: labelSelector: description: |- A label query over a set of resources, in this case pods. If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic matchLabelKeys: description: |- MatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both matchLabelKeys and labelSelector. Also, matchLabelKeys cannot be set when labelSelector isn't set. This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array x-kubernetes-list-type: atomic mismatchLabelKeys: description: |- MismatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. Also, mismatchLabelKeys cannot be set when labelSelector isn't set. This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array x-kubernetes-list-type: atomic namespaceSelector: description: |- A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: description: |- namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array x-kubernetes-list-type: atomic topologyKey: description: |- This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed. type: string required: - topologyKey type: object type: array x-kubernetes-list-type: atomic type: object podAntiAffinity: description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: description: |- The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) properties: podAffinityTerm: description: Required. A pod affinity term, associated with the corresponding weight. properties: labelSelector: description: |- A label query over a set of resources, in this case pods. If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic matchLabelKeys: description: |- MatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both matchLabelKeys and labelSelector. Also, matchLabelKeys cannot be set when labelSelector isn't set. This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array x-kubernetes-list-type: atomic mismatchLabelKeys: description: |- MismatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. Also, mismatchLabelKeys cannot be set when labelSelector isn't set. This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array x-kubernetes-list-type: atomic namespaceSelector: description: |- A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: description: |- namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array x-kubernetes-list-type: atomic topologyKey: description: |- This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: description: |- weight associated with matching the corresponding podAffinityTerm, in the range 1-100. format: int32 type: integer required: - podAffinityTerm - weight type: object type: array x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: description: |- If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied. items: description: |- Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key <topologyKey> matches that of any node on which a pod of the set of pods is running properties: labelSelector: description: |- A label query over a set of resources, in this case pods. If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic matchLabelKeys: description: |- MatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both matchLabelKeys and labelSelector. Also, matchLabelKeys cannot be set when labelSelector isn't set. This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array x-kubernetes-list-type: atomic mismatchLabelKeys: description: |- MismatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. Also, mismatchLabelKeys cannot be set when labelSelector isn't set. This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array x-kubernetes-list-type: atomic namespaceSelector: description: |- A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: description: |- namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array x-kubernetes-list-type: atomic topologyKey: description: |- This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed. type: string required: - topologyKey type: object type: array x-kubernetes-list-type: atomic type: object type: object cacheServer: description: |- CacheServer is the cache server spec. NOTE: If runnerConfigMapName is set, this cacheServer config is ignored and no built-in cache server is created. properties: externalSecret: description: |- ExternalSecret is the shared secret between the runner and the external cache server. Required by gitea-runner when external_server is in use: the runner pre-registers each job's ACTIONS_RUNTIME_TOKEN with the cache-server, which enforces bearer auth + per-repo cache isolation. Ignored when ExternalServerURL is empty. type: string externalServerURL: description: |- ExternalServerURL is the external cache server URL. If it's set, the controller will use this external cache server and won't create a cache-server. Host and Port will be ignored. type: string host: description: Host is the host for the cache server type: string port: default: 80 description: |- Port is the port for the cache server NOTE: act_runner's built-in cache server does not support TLS, so 443 is not recommended. format: int32 type: integer type: object gitea: description: Gitea is the Gitea spec properties: instanceUrl: description: InstanceUrl is the URL of the Gitea instance format: url type: string registrationToken: description: RegistrationToken is the registration token of the runner type: string skipTlsVerify: default: false description: SkipTlsVerify is the skip TLS verify flag type: boolean required: - instanceUrl - registrationToken type: object imagePullSecrets: description: ImagePullSecrets are the image pull secrets for the runner pods items: description: |- LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace. properties: name: default: "" description: |- Name of the referent. This field is effectively required, but due to backwards compatibility is allowed to be empty. Instances of this type with an empty value here are almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic type: array nodeSelector: additionalProperties: type: string description: NodeSelector is the node selector for the runner pods type: object runtime: default: image: gitea/runner:nightly-dind labels: ubuntu-20.04: docker://gitea/runner-images:ubuntu-20.04 ubuntu-22.04: docker://gitea/runner-images:ubuntu-22.04 ubuntu-latest: docker://gitea/runner-images:ubuntu-latest description: Runtime is the runtime spec properties: configMapMounts: description: ConfigMapMounts are additional ConfigMap mounts for runner pods. items: properties: configMapName: description: ConfigMapName is the name of the ConfigMap to mount. type: string defaultMode: description: 'DefaultMode is optional: mode bits used to set permissions on created files by default.' format: int32 type: integer items: description: Items is a list of key-to-path mappings to project from the config map. items: description: Maps a string key to a path within a volume. properties: key: description: key is the key to project. type: string mode: description: |- mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: description: |- path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'. type: string required: - key - path type: object type: array mountPath: description: MountPath is the path to mount the config map. type: string optional: description: Optional specifies whether the ConfigMap or its keys must be defined. type: boolean readOnly: description: ReadOnly indicates if the volume should be mounted read-only. type: boolean volumeName: description: VolumeName is the volume name for the config map mount. type: string required: - configMapName - mountPath - volumeName type: object type: array envs: description: Envs are additional environment variables written into runner config (runner.envs in config.yaml). items: properties: name: description: Name is the environment variable name. type: string value: description: Value is the environment variable value. type: string required: - name - value type: object type: array image: default: gitea/runner:nightly-dind description: Image is the image of the runner type: string imagePullPolicy: description: |- ImagePullPolicy controls when the runner image is pulled. Leave unset to use the kubelet default. enum: - Always - IfNotPresent - Never type: string labels: additionalProperties: type: string default: ubuntu-20.04: docker://gitea/runner-images:ubuntu-20.04 ubuntu-22.04: docker://gitea/runner-images:ubuntu-22.04 ubuntu-latest: docker://gitea/runner-images:ubuntu-latest description: Labels is the labels of the runner type: object pvcMounts: description: |- PVCMounts mount existing PVCs into runner worker pods. When runnerConfigMapName is not set, their mount paths are also added to container.valid_volumes so workflows can explicitly use them. items: properties: claimName: description: ClaimName is the name of an existing PVC in the same namespace as the runner worker pod. type: string mountPath: description: |- MountPath is the absolute container path to mount the PVC in the runner worker pod. The same path is added to container.valid_volumes when config.yaml is auto-generated. type: string readOnly: default: true description: ReadOnly controls whether the PVC mount is read-only. Defaults to true. type: boolean volumeName: description: VolumeName is the volume name for the PVC mount. type: string required: - claimName - mountPath - volumeName type: object x-kubernetes-validations: - message: mountPath must be an absolute path rule: self.mountPath.startsWith('/') type: array runnerConfigMapName: description: |- RunnerConfigMapName specifies an existing ConfigMap to mount as the runner config. If set, the controller will NOT generate a ConfigMap and any auto-generated settings (Labels, Gitea SkipTlsVerify/insecure, CacheServer URL, etc.) will be ignored, and the built-in cache server will not be created. type: string secretMounts: description: SecretMounts are additional secret mounts for runner pods. items: properties: defaultMode: description: |- defaultMode is Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer items: description: |- items If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'. items: description: Maps a string key to a path within a volume. properties: key: description: key is the key to project. type: string mode: description: |- mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: description: |- path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'. type: string required: - key - path type: object type: array x-kubernetes-list-type: atomic mountPath: description: MountPath is the path to mount the secret. type: string optional: description: optional field specify whether the Secret or its keys must be defined type: boolean readOnly: description: ReadOnly indicates if the volume should be mounted read-only. type: boolean secretName: description: |- secretName is the name of the secret in the pod's namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret type: string volumeName: description: VolumeName is the volume name for the secret mount. type: string required: - mountPath - volumeName type: object x-kubernetes-validations: - message: secretName is required rule: has(self.secretName) type: array required: - labels type: object scalability: default: cpuRequest: "2" maxSize: 10 memoryRequest: 4Gi description: Scalability is the scalability spec properties: cpuRequest: anyOf: - type: integer - type: string default: "2" description: CpuRequest is the CPU request of each runner pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true maxSize: default: 10 description: MaxSize is the maximum size of the runner maximum: 9999 minimum: 1 type: integer memoryRequest: anyOf: - type: integer - type: string default: 4Gi description: MemoryRequest is the memory request of each runner pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: object storage: default: cacheServerVolumeSize: 500Gi cacheVolumeSize: 100Gi className: standard dataVolumeSize: 100Mi description: Storage is the storage spec properties: cacheServerVolumeSize: anyOf: - type: integer - type: string default: 500Gi description: CacheServerVolumeSize is the size of the cache server volume pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true cacheVolumeSize: anyOf: - type: integer - type: string default: 100Gi description: CacheVolumeSize is the size of the cache volume pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true className: default: standard description: ClassName is the storage class name type: string dataVolumeSize: anyOf: - type: integer - type: string default: 100Mi description: DataVolumeSize is the size of the data volume pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: object required: - gitea type: object status: description: RunnerStatus defines the observed state of Runner properties: targetSize: type: integer targetSizeUpdatedAt: format: date-time type: string required: - targetSize - targetSizeUpdatedAt type: object type: object served: true storage: true subresources: status: {}---apiVersion: v1kind: ServiceAccountmetadata: labels: app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: runner-controller name: runner-controller-controller-manager namespace: runner-controller-system---apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: labels: app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: runner-controller name: runner-controller-leader-election-role namespace: runner-controller-systemrules:- apiGroups: - "" resources: - configmaps verbs: - get - list - watch - create - update - patch - delete- apiGroups: - coordination.k8s.io resources: - leases verbs: - get - list - watch - create - update - patch - delete- apiGroups: - "" resources: - events verbs: - create - patch---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: runner-controller-manager-rolerules:- apiGroups: - "" resources: - configmaps - events - persistentvolumeclaims - persistentvolumes - pods - secrets - services verbs: - create - delete - get - list - patch - update - watch- apiGroups: - gitea.com resources: - runners verbs: - create - delete - get - list - patch - update - watch- apiGroups: - gitea.com resources: - runners/finalizers verbs: - update- apiGroups: - gitea.com resources: - runners/status verbs: - get - patch - update---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: runner-controller-metrics-auth-rolerules:- apiGroups: - authentication.k8s.io resources: - tokenreviews verbs: - create- apiGroups: - authorization.k8s.io resources: - subjectaccessreviews verbs: - create---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: runner-controller-metrics-readerrules:- nonResourceURLs: - /metrics verbs: - get---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: labels: app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: runner-controller name: runner-controller-runner-editor-rolerules:- apiGroups: - gitea.com resources: - runners verbs: - create - delete - get - list - patch - update - watch- apiGroups: - gitea.com resources: - runners/status verbs: - get---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: labels: app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: runner-controller name: runner-controller-runner-viewer-rolerules:- apiGroups: - gitea.com resources: - runners verbs: - get - list - watch- apiGroups: - gitea.com resources: - runners/status verbs: - get---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: labels: app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: runner-controller name: runner-controller-leader-election-rolebinding namespace: runner-controller-systemroleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: runner-controller-leader-election-rolesubjects:- kind: ServiceAccount name: runner-controller-controller-manager namespace: runner-controller-system---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: labels: app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: runner-controller name: runner-controller-manager-rolebindingroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: runner-controller-manager-rolesubjects:- kind: ServiceAccount name: runner-controller-controller-manager namespace: runner-controller-system---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: runner-controller-metrics-auth-rolebindingroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: runner-controller-metrics-auth-rolesubjects:- kind: ServiceAccount name: runner-controller-controller-manager namespace: runner-controller-system---apiVersion: v1kind: Servicemetadata: labels: app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: runner-controller control-plane: controller-manager name: runner-controller-controller-manager-metrics-service namespace: runner-controller-systemspec: ports: - name: https port: 8443 protocol: TCP targetPort: 8443 selector: control-plane: controller-manager---apiVersion: apps/v1kind: Deploymentmetadata: labels: app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: runner-controller control-plane: controller-manager name: runner-controller-controller-manager namespace: runner-controller-systemspec: replicas: 1 selector: matchLabels: control-plane: controller-manager template: metadata: annotations: kubectl.kubernetes.io/default-container: manager labels: control-plane: controller-manager spec: containers: - args: - --metrics-bind-address=:8443 - --leader-elect - --health-probe-bind-address=:8081 command: - /manager image: commitgo/runner-controller:v0.6.0 livenessProbe: httpGet: path: /healthz port: 8081 initialDelaySeconds: 15 periodSeconds: 20 name: manager readinessProbe: httpGet: path: /readyz port: 8081 initialDelaySeconds: 5 periodSeconds: 10 resources: limits: cpu: 500m memory: 128Mi requests: cpu: 10m memory: 64Mi securityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL securityContext: runAsNonRoot: true serviceAccountName: runner-controller-controller-manager terminationGracePeriodSeconds: 10EOFkubectl apply -f install.ymlA successful output will look similar to the following.
namespace/runner-controller-system createdcustomresourcedefinition.apiextensions.k8s.io/runners.gitea.com createdserviceaccount/runner-controller-controller-manager createdrole.rbac.authorization.k8s.io/runner-controller-leader-election-role createdclusterrole.rbac.authorization.k8s.io/runner-controller-manager-role createdclusterrole.rbac.authorization.k8s.io/runner-controller-metrics-auth-role createdclusterrole.rbac.authorization.k8s.io/runner-controller-metrics-reader createdclusterrole.rbac.authorization.k8s.io/runner-controller-runner-editor-role createdclusterrole.rbac.authorization.k8s.io/runner-controller-runner-viewer-role createdrolebinding.rbac.authorization.k8s.io/runner-controller-leader-election-rolebinding createdclusterrolebinding.rbac.authorization.k8s.io/runner-controller-manager-rolebinding createdclusterrolebinding.rbac.authorization.k8s.io/runner-controller-metrics-auth-rolebinding createdservice/runner-controller-controller-manager-metrics-service createddeployment.apps/runner-controller-controller-manager createdConfiguration
Section titled “Configuration”cat > runner.yml << EOFapiVersion: gitea.com/v1kind: Runnermetadata: labels: app.kubernetes.io/name: runner-controller app.kubernetes.io/managed-by: kustomize name: "gitea-runner" # TODO, like my-runnerspec: gitea: instanceUrl: "Your Gitea instanlce URL" # TODO, like https://mygitea.com/ registrationToken: "<Your registration token>" # TODO, it's the same token to register runners runtime: labels: ubuntu-latest: docker://gitea/runner-images:ubuntu-latest ubuntu-22.04: docker://gitea/runner-images:ubuntu-22.04 ubuntu-20.04: docker://gitea/runner-images:ubuntu-20.04 storage: className: "local-path" # TODO, it should be real storage class in your k8s cluster scalability: maxSize: 10 cpuRequest: 4 memoryRequest: 8GiEOFMounting existing PVCs into runner worker pods
Section titled “Mounting existing PVCs into runner worker pods”spec.runtime.pvcMounts mounts existing PersistentVolumeClaims (PVCs) into the runner worker pods so that workflow jobs can read or write data on shared storage.
apiVersion: gitea.com/v1kind: Runnermetadata: name: gitea-runnerspec: # ... runtime: pvcMounts: - volumeName: shared-data claimName: my-existing-pvc mountPath: /mnt/shared readOnly: trueEach entry has the following fields:
volumeName(required): the name used for the volume inside the worker pod spec. Must be unique withinpvcMounts.claimName(required): the name of an existing PVC. The PVC must already exist in the same namespace as the runner worker pod — the controller does not create it.mountPath(required): the absolute container path where the PVC is mounted inside the worker.readOnly(optional): defaults totrue. Set it tofalseto allow workflow jobs to write to the volume.
By default, each mountPath and its mountPath/** glob are also added to container.valid_volumes in the worker’s auto-generated config.yaml. This allows workflow jobs to mount the same path into their job containers via container.volumes, for example:
jobs: example: runs-on: ubuntu-latest container: image: ubuntu:latest volumes: - /mnt/shared:/data steps: - run: ls /dataUsing an external cache server
Section titled “Using an external cache server”By default, the controller provisions a built-in cache server (the *-cache-server pod) for each Runner. To reuse an existing cache server instead, configure spec.cacheServer.externalServerURL and spec.cacheServer.externalSecret.
apiVersion: gitea.com/v1kind: Runnermetadata: name: gitea-runnerspec: # ... cacheServer: externalServerURL: "http://my-cache-server.example.com/" externalSecret: "<shared bearer secret>"externalServerURL: when set, the controller will not create a built-in cache-server pod, and the worker will use this URL instead.externalSecret: the shared bearer secret used between the runner and the external cache server. The runner uses this secret to enforce bearer authentication. This field is ignored whenexternalServerURLis empty.
To apply the runner.yml, run:
kubectl apply -f runner.ymlA successful output will look similar to the following.
runner.gitea.com/gitea-runner created
- Get the runner opration pods
NAME READY STATUS RESTARTS AGErunner-controller-controller-manager-ff9cbc7b-kbxzh 1/1 Running 0 14m- Get the runner opration logs
kubectl -n runner-controller-system logs -f runner-controller-controller-manager-ff9cbc7b-kbxzh
Using runner scale sets
Section titled “Using runner scale sets”Now you will create and run a simple test workflow that uses the runner scale set runners.
In a repository, create a workflow similar to the following example. The runs-on value should match the runner runtime labels name you used when you installed the autoscaling runner set.
-
In a repository, create a workflow similar to the following example. The runs-on value should match the runner runtime labels name you used when you installed the autoscaling runner set.
For more information on adding workflows to a repository, see Quickstart for Gitea Actions.
name: Gitea Actions Demorun-name: ${{ gitea.actor }} is testing out Gitea Actionson: [push]jobs:Explore-Gitea-Actions:runs-on: ubuntu-lateststeps:- run: echo " The job was automatically triggered by a ${{ gitea.event_name }} event."- run: echo " This job is now running on a ${{ runner.os }} server hosted by Gitea!"- run: echo " The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."- name: Check out repository codeuses: actions/checkout@v4- run: echo " The ${{ gitea.repository }} repository has been cloned to the runner."- run: echo " The workflow is now ready to test your code on the runner."- name: List files in the repositoryrun: |ls ${{ gitea.workspace }}- run: echo " This job's status is ${{ job.status }}."


- To view the runner pods being created while the workflow is running, run the following command from your terminal.
kubectl get pods- A successful output will look similar to the following.
NAME READY STATUS RESTARTS AGEgitea-runner-0001 1/1 Running 0 56sgitea-runner-cache-server 1/1 Running 0 26s