GitLab Runner on Red Hat OpenShift

Deploy GitLab Runner on OpenShift with the GitLab Operator, scoped security context constraints, in-cluster autoscaling and version alignment with your self-managed GitLab.

On this page

When GitLab Runner on OpenShift fits your CI/CD estate

If your platform team already runs OpenShift as its standard container platform, running GitLab Runner inside the cluster is worth evaluating before you commission separate build VMs. The GitLab Operator can deploy runners into the cluster, so the work centers on three decisions: the security context constraints that let job pods run without weakening the cluster, the autoscaling behavior inside the cluster, and keeping runner versions aligned with your self-managed GitLab release. GitLab Runner is the application that executes CI/CD jobs. According to the GitLab Runner documentation, it is distributed under the MIT license and supports shell, Docker and autoscaling executors across clouds and hypervisors.

Running jobs as pods makes sense when your teams already deploy and monitor workloads on OpenShift, when build demand is bursty and you want capacity to follow the cluster's scheduler, and when you want job isolation without maintaining a separate VM fleet. It is less attractive when jobs need the whole host, attached devices, or a non-Linux operating system, because those needs point toward VM-based executors. Confirm with your own workloads which category most of your pipelines fall into before you decide.

Before you commit, ask your team to inventory the jobs that will run. List which need privileged container builds, which pull images from registries that require custom certificates, and which write to shared caches. Those answers determine how much the security context constraints must loosen, and whether the runner manager should sit in a namespace that only the platform team can administer. Check the current GitLab Runner and Operator compatibility notes with GitLab before you fix a version, since the docs advise keeping Runner major and minor versions aligned with GitLab. GitLab Runner documentation is the natural next read for the executor options.

Choosing between the Operator, the Helm chart and VM-based runners

The platform team owns the cluster, so it absorbs most of the cost of each option. The GitLab installation methods page lists the Linux package, the Helm chart, the GitLab Operator, Docker and source as ways to install GitLab, and the Operator sits among those methods as a supported route for the GitLab instance. For runners, the choice decides who edits cluster-scoped objects and how often the platform team is pulled into upgrades.

  • GitLab Operator: Runs GitLab components as Kubernetes custom resources, so the platform team must accept an operator's lifecycle alongside other operators in a shared cluster. Confirm which runner resources the Operator manages in the version you plan to install.
  • Helm chart: Installs the runner as a release inside a namespace. Binding a security context constraint (SCC) to the runner's service account usually needs someone with cluster-level rights, so plan that step as a cluster-admin task.
  • VM-based runners: Keep build workloads off the OpenShift nodes. Cluster-admin involvement drops, but the platform team takes on VM images, patching and capacity for the fleet.
  • Network egress: Every option needs outbound paths to the GitLab instance and to package and image registries. Cluster runners use the cluster egress path, while VM runners use the network path and firewall rules around the VMs.

Before choosing, ask Red Hat and GitLab in writing to confirm which Operator and OpenShift versions are compatible with your GitLab version.

Configuration options and when each one fits

This table compares the main choices for running GitLab Runner on OpenShift, from runner scope and executor to permissions, job resources, helper sizing and cache location. Read each row for the condition that favors an option and the cost it adds, then confirm the details with your cluster administrators before you commit.

DecisionOption and when it fitsTrade-off to weigh
Runner scopeInstance runners when many projects share one trust level. Group or project runners when a team or single repository needs isolation or its own capacity.Instance runners are simpler to operate but widen the blast radius of a compromised job. Narrower scopes multiply registrations and configuration to maintain.
ExecutorKubernetes executor when jobs should run as pods inside the cluster you already operate. Docker or shell on separate VMs when jobs need the host, privileged container builds or devices that OpenShift will not grant.The Kubernetes executor keeps capacity in one platform but ties job runtime to cluster policy. Separate VMs add a second platform to patch and monitor.
Service account and RBACA dedicated service account for the runner manager, with rights limited to creating pods, secrets and logs in its job namespace. Broader cluster roles suit only tightly controlled platform teams.Least privilege takes design time and per-namespace bindings. Broad grants simplify early setup but let any job that reaches the manager affect other workloads.
Security context constraintsThe restricted SCC is the default target for job pods. A custom SCC is needed only when a job must run as a specific UID or use a volume type the default does not allow.Each relaxation widens what a CI job can do on the node. Confirm the SCC binding with your OpenShift administrators before granting exceptions.
Job resource requests and limitsSet defaults for typical jobs and allow per-job overrides for heavy builds, limited to values the namespace quota can hold.Low limits cause out-of-memory failures that look like flaky tests. High defaults reduce capacity for other teams sharing the quota.
Helper image sizingKeep the default helper unless jobs show artifact, cache or log handling bottlenecks, then size its requests separately from the build container.Undersized helpers slow artifact uploads and cache restores. Oversized helpers consume quota on every job pod.
Cache locationCluster-local cache volumes suit a small runner pool on one cluster. Object storage suits many runners, multiple clusters or fresh pods that must restore dependencies.Cluster volumes are simpler but depend on storage class behavior. Object storage adds credentials, network paths and cost to manage.
AutoscalingIn-cluster autoscaling through pod creation per job suits variable demand. Fixed runner pods suit steady workloads with predictable concurrency.Per-job pods start slower and depend on quota headroom. Fixed pods waste capacity off-peak but are easier to size and monitor.
Version alignmentMatch runner major and minor versions to GitLab, updating both in the same change window whenever you move through an upgrade stop.Operator and runner images must follow GitLab's release cadence, so each GitLab upgrade adds a runner update to the plan.

Deploying the runner Operator on OpenShift step by step

This sequence takes a cluster from a version check to a verified runner that executes pipeline jobs as pods, with each stage confirmed before the next begins. The result is a registered runner on your OpenShift cluster that has completed one test pipeline under the security context you approved.

  1. Confirm GitLab and runner version alignment

    Your platform engineer records the GitLab version of your self-managed instance and selects a GitLab Runner version with the same major and minor version, as GitLab's Runner documentation advises. Check the GitLab maintenance policy and upgrade paths before choosing, because the instance may be on a version that is approaching a required upgrade stop. Confirm the Kubernetes and OpenShift versions your cluster runs are supported by the Operator release you plan to use. The step is done when you have a written version pair for GitLab, Runner and OpenShift that the change record approves.

  2. Install the GitLab Operator from OperatorHub

    A cluster administrator installs the GitLab Operator from the OperatorHub catalog in the OpenShift console, choosing the channel and install scope that match your governance model. Mirror the Operator images into your internal registry first if the cluster has no outbound access to public registries. Confirm the Operator's ClusterServiceVersion reports a Succeeded phase and that the controller pod is running in the Operator namespace. The step is done when the Operator appears as installed and healthy in the console and in the command line output of your cluster tooling.

  3. Create the namespace and runner service account

    Create a dedicated namespace for runner infrastructure, separate from application workloads, and a service account that the runner manager uses to create job pods. Keep the namespace free of unrelated secrets and apply resource quotas and network policies your security team has specified. Avoid giving the service account cluster-wide rights; scope permissions to the namespace where job pods run. The step is done when oc get serviceaccount shows the account in the expected namespace and oc auth can-i returns the permissions you have approved and no others.

  4. Grant the minimum security context constraint

    OpenShift applies security context constraints (SCCs) that decide which user IDs and capabilities pods may use, so the runner's job pods may fail to start under the default restricted constraint. Review the user ID, volume and capability settings the job images need, then bind the narrowest SCC that allows them to the job service account. Avoid binding a privileged constraint unless your threat model accepts it, and record the reason in the change record. The step is done when a test pod in the namespace is admitted under the expected constraint, which oc get pod -o yaml shows in its security context annotation.

  5. Register the runner with an authentication token

    In the GitLab UI or API, create a runner at the scope you have chosen (instance, group or project) and copy the authentication token that begins with glrt-. Store the token in an OpenShift secret, not in the Operator custom resource or a values file in source control, and reference that secret from the runner definition. Legacy registration tokens are being phased out, so do not build new automation on them. The step is done when the runner appears in the GitLab runner list with a status that indicates it has contacted the instance.

  6. Configure the Kubernetes executor

    Set the runner custom resource so the Kubernetes executor creates job pods in the namespace you prepared, with the service account, security context constraint binding, default images and resource requests and limits you have agreed. Set the concurrency and pod limits to match cluster capacity, and confirm which registry and pull secrets the job images need. Apply the configuration through your normal change process so it can be reviewed and reverted. The step is done when the runner pod is Ready, its logs show the configuration loaded without errors, and GitLab shows the runner online.

  7. Run a test pipeline and check the job pod

    Add a small test pipeline to a non-production project that targets the runner by its tag and prints the effective user ID with id -u and the hostname. Watch the job pod appear in the runner namespace and confirm it runs under the user ID permitted by the SCC you bound, not root unless you approved that. Check that the job reaches success in GitLab and that the pod is removed after completion. The step is done when the job log shows the expected user ID and the pod lifecycle matches your expectations, which closes the build and becomes the baseline for later upgrades.

Why security context constraints decide what jobs can do

On OpenShift, the security context constraint applied to a runner pod decides which identities, volumes and privileges its CI jobs can use. The six decisions below are linked: a permissive exception for one job can widen access for every team sharing the same namespace and runner. Confirm exact SCC names and permissions against the Red Hat and GitLab documentation for the versions you run.

Restricted SCC and arbitrary UIDs

OpenShift assigns pods a random high UID from a namespace range by default under the restricted profile, so images that expect root or a fixed UID may fail at startup. Test each job image under that assignment before rollout, and check that build caches, home directories and writable paths are group-writable for the assigned UID. Confirm the default SCC bound to the runner's service account in your cluster.

Explicit exceptions for privileged jobs

Jobs that need root, host paths or privileged containers should not run under the default runner identity. Grant a separate, narrowly scoped SCC to a dedicated runner or service account, and document which projects may use it and why. Treat each exception as a widened trust boundary that needs its own approval record.

Docker-in-Docker and build tools

Building container images inside jobs usually means Docker-in-Docker, Kaniko, Buildah or similar tools that need capabilities or nested runtimes that the restricted profile blocks. Decide per workload whether to rootless-build, use a dedicated build runner, or move image builds to a tool designed for unprivileged clusters. Confirm which option your security team accepts before writing pipelines that depend on it.

Namespace isolation between teams

Runner pods that share a namespace can read each other's secrets, service accounts and job artifacts if RBAC is broad. Plan one namespace per team or trust level, with resource quotas and role bindings that limit what a job's service account can list or modify. Verify isolation by attempting cross-namespace access from a test job.

Secrets exposure through runner configuration

Runner registration tokens, CI variables and Kubernetes Secrets mounted into job pods are visible to anyone who can read the runner's configuration or the namespace. Kubernetes stores Secrets unencrypted in etcd by default, so confirm encryption at rest is enabled and restrict list and watch access. Prefer short-lived credentials and external secret stores for production deployments.

Network egress policy for jobs

Jobs pull packages, images and dependencies, and a compromised pipeline can send data out just as easily. Define which destinations job pods may reach, such as your GitLab instance, internal registries and approved package mirrors, and enforce that with network policies or egress controls. Confirm how your OpenShift network plugin applies these rules to runner-created job pods.

How in-cluster autoscaling changes capacity planning

With the Kubernetes executor, the number of jobs that run at once is set by the runner configuration and by the room the cluster has to schedule job pods, not by an Auto Scaling group that adds virtual machines on demand. The runner's concurrency limit caps how many job pods one runner may start, and the cluster then has to find nodes with enough free CPU and memory for each pod. Node autoscaling is a separate cluster setting. On OpenShift, that means the machine sets and the cluster autoscaler decide whether new worker nodes appear, and whether they do depends on that cluster configuration rather than on anything GitLab Runner does. Before you size the runner, confirm which node autoscaling mechanism your OpenShift platform team operates, the minimum and maximum node counts it allows, and how long a new node takes to join in your environment.

Every job pod carries resource requests and limits, and those values decide how much of a node each job reserves. Requests drive scheduling, so a job that requests more memory than a node has free will wait even when the cluster looks idle in total. Limits decide what a job may burst to, and a job that exceeds its memory limit is terminated. The runner's pod_spec and resource overwrite settings let teams vary these values per job, but permissive overwrite rules let any pipeline ask for large requests. Agree on default requests, the largest request a project may set, and the namespace quota that caps the total. Your own job profiles will show the right numbers, so measure a representative set of builds before you publish any default.

Jobs from different teams that share worker nodes can affect one another through CPU contention, memory pressure, disk I/O and the shared container image cache, even when every pod's requests are honest. A single heavy build can slow or evict neighboring pods, and noisy neighbors make failures hard to reproduce. Consider dedicated node pools for runners when jobs from different trust levels or sensitivity must not share a kernel and node, when deployment jobs need predictable capacity, or when a team runs very large builds. Node pools can be selected with node selectors or tolerations in the runner configuration. Shared pools remain reasonable for many routine workloads. Confirm the cost of extra pools with your platform team, because idle dedicated nodes are paid capacity.

Security and operational controls for cluster runners

A runner in an OpenShift cluster executes code from every project it serves, so the cluster is only as safe as the trust boundaries around it. The controls below cover credentials, permissions, untrusted code, visibility and how deployment work is separated from ordinary builds.

Runner token handling

Runner registration and authentication tokens grant the ability to attach a runner to your GitLab instance, so they belong in a Kubernetes Secret rather than in Helm values or Git. Plan who may create runners, how tokens rotate and how a compromised token is revoked. Confirm which registration method your GitLab version expects before you build the automation around it, and record the owner of each token.

Least-privilege service account RBAC

The Kubernetes executor needs a service account that can create pods, secrets and config maps in its job namespace, and nothing beyond that. Bind a Role to the namespace rather than a ClusterRole, avoid cluster-admin, and confirm that the runner cannot read secrets in other namespaces. Review the bindings after each runner chart or Operator upgrade, because defaults can widen between releases.

Isolating untrusted pipeline code

Merge requests from forks and unreviewed contributors can run arbitrary commands inside job pods. Keep those jobs on a separate runner with no access to deployment credentials, restrict privileged containers and host mounts through the security context constraints assigned to that runner, and block jobs from reaching the cluster API. Verify isolation with a test job that tries to list secrets and reach the API server.

Runner pod logging and monitoring

Job pods are short-lived, so logs disappear when a pod is cleaned up unless you collect them. Forward runner manager and job pod logs to your cluster logging stack, alert on repeated pod creation failures, unexpected privilege requests and jobs that exceed expected runtime. Ask your team which events must be retained and for how long, and confirm that log retention matches your audit requirements.

Separating deployment and build runners

Deployment jobs hold credentials for production clusters, registries and cloud accounts, so they should run on runners that only protected branches can reach. General build runners serve everyone else. Use distinct runner tags, a separate namespace and service account for each pool, and protected variables scoped to deployment jobs. Document which pool each project uses so ownership changes are visible during review.

Rolling out hardening gradually

GitLab describes its hardening guidance as a work in progress to be applied step by step. Treat each recommendation as a change with a test in a non-production environment, a rollback path and an owner. Start with CI/CD and runner isolation, then move to instance-wide settings. Keep a record of which controls are applied, which are deferred and why, so auditors and operators see the same picture.

Questions teams ask before running GitLab Runner on OpenShift

What does a GitLab Runner on OpenShift engagement cost, and what drives the price?

Cost follows from the written scope, and DeployOpen does not publish a price list. The main drivers are how many clusters and namespaces the runners span, how strict the cluster's security context constraints are, how many teams and runner trust levels you need, which workload types (Linux containers, builds that need privileges, deployments to production) must run, and how much compliance evidence you need to produce. Ask for a quote that lists each deliverable, what is one-time (design, build, initial cutover) and what recurs (upgrades, support or a retainer), and which items depend on your platform team's time. GitLab's own licensing is separate: the pricing page lists GitLab Free at 0 US dollars, Premium at 29 US dollars per user per month billed annually, and Ultimate as custom priced, with all three tiers available for self-managed installations.

How long does it take to get runners working on OpenShift?

The timeline depends on the number of clusters, how much policy work is needed for the runner service account and security context constraints, whether your GitLab instance and registry are already reachable from the cluster, and how many teams must be onboarded and tested. A narrow pilot on one cluster moves faster than a fleet shared across several teams with separate trust levels. Ask for a schedule that separates approval waits, such as SCC review by your security group, from build work, and confirm which dependencies sit with your team. DeployOpen does not quote a duration until the scope is agreed in writing.

What must our platform team provide before the work starts?

Plan to provide four things before the build begins:

  • Cluster access for the people doing the work, scoped to the namespaces and service accounts used for runners.
  • Approval for the security context constraints the runner pods need, reviewed by whoever owns cluster policy.
  • Access to a container registry that the cluster can pull runner and helper images from, including any mirror required in an air-gapped network.
  • GitLab administrator rights, or a named administrator, to create runners, register them with authentication tokens and configure the runner settings.

Confirm these in the scope document so that waiting on an approval does not stall the build.

Can our internal team deploy GitLab Runner on OpenShift without outside help?

Yes, a team with OpenShift and GitLab administration experience can do this alone, and the GitLab Runner documentation covers the installation and configuration. The harder parts are usually the policy decisions: which security context constraints are acceptable for build pods, how runner trust levels map to projects, and how the runner version stays aligned with your GitLab version. Ask whether your team has someone who will own those decisions after handover. An outside partner is most useful when the work must be finished on a fixed schedule, when evidence for an audit must be produced, or when the same platform must be repeated across several clusters.

How do we schedule runner upgrades alongside GitLab upgrades?

Keep the Runner major and minor versions aligned with your GitLab instance, as the GitLab Runner documentation advises. GitLab ships a minor release on the third Thursday of each month and patch releases twice a month, and it requires upgrades to pass through required upgrade stops. Since GitLab 17.5 these fall at versions x.2, x.5, x.8 and x.11, for example 19.2, 19.5, 19.8 and 19.11. Plan a runner upgrade as a step after each GitLab upgrade, test a job on a non-production runner first, and check the OpenShift cluster version because Kubernetes releases roughly three times a year. Ask your partner to define who watches release notes, who approves changes and how the runner image tag is pinned.

What compliance evidence should we expect from the runner setup?

Expect the work to prepare the controls and the evidence, not to produce an audit opinion. Typical evidence includes the runner configuration as code, the SCC and role bindings, records of which projects use which runner trust level, pipeline logs, and records of approved upgrades. Independent auditors and certification bodies issue reports and certifications, so ask the partner to list the evidence each deliverable produces and which control framework it is mapped to. GitLab's hardening guidance refers to NIST SP 800-53 and notes it should be rolled out gradually.

What happens to build pods when the cluster scales, and what should we check?

Runners on OpenShift can start job pods within the cluster, so autoscaling depends on cluster capacity, resource requests and limits, and how the Kubernetes executor is configured. Engineers should check that job pods have sensible resource requests, that the cluster autoscaler can add nodes in the time jobs expect, and that the security context constraints do not block the images your builds need. Test a burst of parallel jobs before onboarding teams, and confirm how the cluster behaves when nodes are drained during an OpenShift or Kubernetes upgrade.

How DeployOpen scopes and supports OpenShift runner deployments

DeployOpen plans and implements self-hosted GitLab inside your own infrastructure, and the OpenShift runner work is scoped the same way: the scope is agreed in writing before any delivery begins. The written scope names the OpenShift clusters and namespaces involved, the GitLab instance the runners will register against, the runner configuration, the security context constraints to be applied, the autoscaling limits, and the handover items. Your team confirms which OpenShift version, GitLab version and network path (connected, proxied or air-gapped) apply, because those facts change the build sequence and the effort involved. A quote follows from that written scope, and DeployOpen does not publish a price list.

DeployOpen hardens the runner setup and documents it so your platform team can operate it after handover. That includes the service accounts, SCC bindings and RBAC roles that the runner pods use, the secrets that hold runner authentication, and the runner version pinning that keeps the Runner aligned with your GitLab release. Documentation covers the configuration as deployed, the upgrade steps for each GitLab release and the checks to run after a change. Where you request it, runner security testing can be combined with the implementation, so the same scope can cover a review of the pod permissions and the isolation between projects. DeployOpen does not issue audit reports, attestations or certifications; if you need evidence for an assessment, your independent auditor or certification body makes that determination, and DeployOpen prepares the systems, controls and records they will review.

Ongoing work after handover can be covered by a retainer for upgrades, runner version alignment and support. Ask any partner, DeployOpen included, to state in the proposal which GitLab upgrade stops and runner releases fall inside the retainer, how Runner and GitLab version changes are tested before they reach production runners, and who responds when a job fails because of a cluster change. Confirm those answers against your own change process and your OpenShift support arrangements before you sign.

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.