How to tune GKE-hosted runner memory and CPU settings for GitLab CI jobs¶
This guide explains how to configure resource requests and limits for GitLab CI jobs that run on GKE-hosted GitLab runners. These settings help optimize performance and control resource usage for CI pipelines.
Note
This guide is particularly useful when CI jobs are terminated due to
Out Of Memory (OOMKilled) errors (typically seen as exit code 137)
in jobs such as pre-commit or python:tox.
Supported variables¶
The following variables are available for fine-tuning resource allocation for jobs:
| Variable | Description | Example |
|---|---|---|
KUBERNETES_MEMORY_REQUEST |
Defines the amount of memory requested for the job’s build container. | 512Mi |
KUBERNETES_MEMORY_LIMIT |
Defines the maximum amount of memory the build container can use. | 1Gi |
KUBERNETES_CPU_REQUEST |
Defines the amount of CPU requested for the job’s build container. | 500m |
KUBERNETES_SERVICE_MEMORY_REQUEST |
Defines the amount of memory requested for each service container, such as docker:dind. |
1Gi |
KUBERNETES_SERVICE_MEMORY_LIMIT |
Defines the maximum amount of memory each service container can use. | 3Gi |
KUBERNETES_HELPER_MEMORY_REQUEST |
Defines the amount of memory requested for the helper container. | 256Mi |
KUBERNETES_HELPER_MEMORY_LIMIT |
Defines the maximum amount of memory the helper container can use. | 2Gi |
Each job pod has a build container, a helper container that clones the repository and handles
artifacts and the cache, and one container for each entry in services. The first three variables
only affect the build container. See
Resource requests and limits for job pods
for how the containers' values add up, and the
GKE GitLab Runners reference for the default
values.
For more details, see the official GitLab Runner documentation: Overwrite container resources
Note
The runner sets a ceiling for each overwrite, and a job cannot set a value above it. The request ceiling for a container is the same as its limit ceiling, because a request cannot be larger than its limit. As of October 2026 the production ceilings are 2000m CPU and 8Gi memory for the build container, 4Gi for the service containers and 4Gi for the helper container. The request and limit variables for the helper container and the service containers are new, and the platform team can change the ceilings per runner. Ask in the Platforms Team channel if a job genuinely needs more.
Warning
Use these variables only when necessary, and increase resource values
incrementally and responsibly. Setting excessively high limits (for example,
8Gi memory for every job) can cause inefficient cluster utilization, prevent autoscaling
from working correctly, and increase costs. Always profile and measure before raising limits.
When to use¶
Only use these variables if:
- A job is repeatedly failing due to
OOMKilled(e.g. exit code137). - A job runs significantly slower than expected due to CPU throttling.
- Profiling shows that the job requires more resources than the default configuration provides.
Jobs that use docker-in-docker¶
When a job uses the docker:dind service, everything you run with docker (test suites, image
builds, docker compose stacks) runs inside the service container, not the build container.
Set the KUBERNETES_SERVICE_MEMORY_* variables for such jobs, not KUBERNETES_MEMORY_*. See
Using docker in CI jobs for how to enable
dind.
The service container has its own memory limit. If the workload inside it goes over the limit, the
service container is OOMKilled and the job typically fails with Docker connection errors or exit
code 137. Only that job's pod is affected, and other jobs on the node carry on.
Tools that start one worker per CPU are a common cause, because the container sees every CPU of the node (16 on the production nodes) whatever its limits. Capping the number of workers in the tool's own configuration is usually a better first step than raising the limit.
build-image:
image: docker:24-git
services:
- docker:24-dind
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
DOCKER_TLS_VERIFY: 1
DOCKER_CERT_PATH: "/certs/client"
KUBERNETES_SERVICE_MEMORY_REQUEST: "1Gi"
KUBERNETES_SERVICE_MEMORY_LIMIT: "3Gi"
before_script:
- until docker info > /dev/null 2>&1; do sleep 1; done
script:
- docker build .
tags:
- $GKE_RUNNER_TAG
Estimating appropriate values¶
Before changing resource settings, profile the job locally or in a controlled environment to understand its actual requirements. An example profiling workflow is shown below:
-
Run the same job locally in a Docker container:
-
Monitor resource usage while the job runs:
docker container top <container_id> docker stats <container_id> -
Note the peak CPU and memory usage.
Steps¶
-
Define the resource tuning variables in a CI job configuration file. For example:
resource-tuned-job: stage: test script: - echo "Running with tuned Kubernetes resource settings" variables: KUBERNETES_MEMORY_REQUEST: "512Mi" KUBERNETES_MEMORY_LIMIT: "1Gi" KUBERNETES_CPU_REQUEST: "500m" tags: - $GKE_RUNNER_TAG -
Commit and push the configuration changes. Subsequent CI jobs will use the specified memory and CPU settings on the Kubernetes runner.
Summary¶
This guide described how to control memory and CPU resources for CI jobs running on a
GKE-hosted GitLab runner using the KUBERNETES_MEMORY_REQUEST, KUBERNETES_MEMORY_LIMIT, and
KUBERNETES_CPU_REQUEST variables, and the KUBERNETES_SERVICE_MEMORY_* and
KUBERNETES_HELPER_MEMORY_* variables for service and helper containers.
These settings should be used sparingly, only when default configurations are insufficient, and
should always be based on profiling data to maintain efficient use of cluster resources.