Files
helm-actions/docs/resources.md
Arnault_LPC 6537bdbd33 feat: add per-container resource configuration for runner and DinD #160 (#168)
This a correction that is forf from original work of [anders.eficode](https://gitea.com/anders.eficode) on [pull request #160](https://gitea.com/gitea/helm-actions/pulls/160)
Description of the change
Adds statefulset.dind.resources and statefulset.runner.resources as optional per-container resource overrides. When set, each takes precedence over the shared statefulset.resources for that container. When unset (default {}), statefulset.resources is used as before.

Benefits
The DinD sidecar and the runner container have very different resource profiles — DinD is memory-hungry (image layer cache, concurrent builds, image pulls) while the runner is a lightweight coordinator that is mostly idle between jobs. Separate resource limits allow right-sizing each container independently, avoiding the choice between over-provisioning the runner or under-provisioning DinD.

Possible drawbacks
None. Fully backward-compatible — both new values default to {}, causing the shared statefulset.resources fallback to apply exactly as before.

Checklist
- [x] Parameters are documented in the `values.yaml` and added to the `README.md` using [readme-generator-for-helm](https://github.com/bitnami-labs/readme-generator-for-helm)
- [x] Breaking changes are documented in the `README.md`
- [x] Helm templating unittests are added (required when changing anything in `templates` folder)
- [x] Bash unittests are added (required when changing anything in `scripts` folder)
- [x] All added template resources MUST render a namespace in metadata

---------

Co-authored-by: Le Prévost-Corvellec Arnault <arnault.le.prevost.corvellec@carbon-it.com>
Co-authored-by: Anders <lantzanders@gmail.com>
Reviewed-on: https://gitea.com/gitea/helm-actions/pulls/168
Reviewed-by: DaanSelen <135789+daanselen@noreply.gitea.com>
Co-authored-by: Arnault_LPC <194310+arnault_lpc@noreply.gitea.com>
2026-07-21 06:41:06 +00:00

5.3 KiB
Raw Permalink Blame History

Resource limits and capacity

By default, all resource values are empty ({}). Without explicit limits, runner pods can consume unbounded CPU and memory on a node. This guide explains how to configure resource usage properly.

Pod architecture

Each runner pod contains two resource-consuming containers:

Container Role Helm value
runner Polls Gitea and orchestrates CI jobs statefulset.runner.resources
dind Docker-in-Docker daemon; executes job containers statefulset.dind.resources

The DinD container runs as a native sidecar (initContainer with restartPolicy: Always).

Helm resource keys

Three values control Kubernetes resource requests and limits:

Key Purpose
statefulset.resources Shared fallback applied to both containers when no override is set
statefulset.runner.resources Override for the runner container only
statefulset.dind.resources Override for the dind container only

Precedence:

statefulset.runner.resources  →  else statefulset.resources
statefulset.dind.resources    →  else statefulset.resources

Recommendation: set statefulset.runner.resources and statefulset.dind.resources explicitly instead of relying on the shared fallback. The runner process is lightweight; DinD and CI workloads need most of the budget.

Example: separate runner and DinD limits

enabled: true
giteaRootURL: https://gitea.example.com
existingSecret: runner-secret
existingSecretKey: runner-token

statefulset:
  replicas: 1

  runner:
    resources:
      requests:
        cpu: 100m
        memory: 256Mi
      limits:
        cpu: 500m
        memory: 512Mi
    config: |
      log:
        level: info
      cache:
        enabled: false
      runner:
        capacity: 1
      container:
        require_docker: true
        docker_timeout: 300s

  dind:
    resources:
      requests:
        cpu: 500m
        memory: 2Gi
      limits:
        cpu: 2
        memory: 4Gi

Two layers of limiting

Kubernetes limits and act-runner job limits serve different purposes. Use both for a robust setup.

1. Kubernetes limits (Helm values)

These apply to the runner and dind containers in the pod.

  • dind limits cap the Docker daemon and everything it runs inside the pod (images, build caches, job containers).
  • runner limits cap the act-runner process itself.

If neither is set, a runaway build can exhaust the entire node.

2. Per-job Docker limits (container.options)

CI jobs run as Docker containers spawned by act-runner through the Docker socket. They are not separate Kubernetes containers. Configure per-job limits in statefulset.runner.config — see act-runner configuration and config.example.yaml.

act-runner settings that affect resource usage

Settings such as runner.capacity and container.options live in statefulset.runner.config, not in the Helm resource values. Refer to the runner documentation for details:

When concurrent jobs or per-job Docker limits increase expected load, size statefulset.dind.resources accordingly on the Helm side.

Capacity planning

These formulas apply to the Helm resource values (statefulset.runner.resources, statefulset.dind.resources, statefulset.replicas):

Per pod

pod budget ≈ runner.limits + dind.limits

Per node

node budget ≈ (runner.limits + dind.limits) × statefulset.replicas + system overhead

Example with the configuration above and replicas: 3:

  • runner: 512Mi × 3 = 1.5Gi
  • dind: 4Gi × 3 = 12Gi
  • total: ~13.5Gi minimum, excluding other workloads on the node

Use statefulset.nodeSelector and statefulset.tolerations to place runners on dedicated nodes when needed.

Why not only set statefulset.resources?

Before chart 0.1.2, one value was copied to both containers. Requests are where it hurts most: the scheduler reserves capacity per container, and both inherit the same numbers.

# Only statefulset.resources — requests copied to runner AND dind:
statefulset:
  resources:
    requests:
      cpu: 500m
      memory: 2Gi      # what DinD needs…
  # runner also requests 500m + 2Gi → pod ~1 CPU + ~4Gi reserved (mostly wasted)
  # dind requests 500m + 2Gi ✓
statefulset:
  resources:
    requests:
      cpu: 100m
      memory: 256Mi    # what the runner actually needs…
  # runner requests 100m + 256Mi ✓
  # dind requests 100m + 256Mi too → tiny slot, builds starve ✗
# Separate overrides — scheduler sees the real footprint:
statefulset:
  resources: {}
  runner:
    resources:
      requests:
        cpu: 100m
        memory: 256Mi
  dind:
    resources:
      requests:
        cpu: 500m
        memory: 2Gi
  # pod requests ~600m CPU + ~2.25Gi RAM — not 1 CPU + 4Gi, nor 200m + 512Mi

Limits follow the same split. Unset overrides ({}) still fall back to statefulset.resources.