This article describes how to install and configure Gitlab Runner in OpenShift Platform.
Note: The
GitLab Runner documentation provides the basic
docker run command for a container installation. But to run this in OpenShift, you need to adapt it to work with the platform's security and operational model.
In This Article:
This process to install and configure Gitlab Runner in OpenShift Platform involves the following steps, which must be performed in order. Use the links below to jump to a specific step.
Instructions
Step 1: Get access to OpenShift Cloudapps Platform
Step 2: Create service account, roles, and role-bindings
oc login -u <onyen> https://api.cloudapps.unc.edu:6443
oc project <gitlab-runner-project-name>
oc create serviceaccount <gitlab-runner-sa> -n <gitlab-runner-project-name>
vi gitlab-runner-rbac.yml # Use the below yaml with updated names and namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: gitlab-runner-role
namespace: <gitlab-runner-project-name>
rules:
- apiGroups: [""]
resources: ["pods", "pods/exec", "pods/log", "pods/attach", "secrets", "events"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: gitlab-runner-binding
namespace: <gitlab-runner-project-name>
subjects:
- kind: ServiceAccount
name: gitlab-runner-sa
namespace: <gitlab-runner-project-name>
roleRef:
kind: Role
name: gitlab-runner-role
apiGroup: rbac.authorization.k8s.io
oc apply -f gitlab-runner-rbac.yml
Step 3: Create ImageStream
To download the Docker Image in Openshift, create an ImageStream. For the list of available version tags, see GitLab Runner tags. You don't need a BuildConfig for this. You can directly create an ImageStream that imports the image from Docker Hub.
NOTE: You can use buildConfig, if you are building image out of a Dockerfile instead. All the necessary docker files need to be stored in a repository(like gitlab).
Using the command line (oc)
oc import-image gitlab-runner-docker:latest --from=docker.io/gitlab/gitlab-runner:bleeding--confirm -n <gitlab-runner-project-name>
Using a YAML definition (imagestream-import.yaml)
Either update in UI(Administrator>Builds>Imagestream>Create ImageStream)
OR
oc apply -f imagestream-import.yaml
apiVersion: image.openshift.io/v1
kind: ImageStream
metadata:
name: gitlab-runner-docker-hub
namespace: <gitlab-runner-project-name>
spec:
tags:
- name: latest
from:
kind: DockerImage
name: 'docker.io/gitlab/gitlab-runner:bleeding'
importPolicy:
scheduled: true
referencePolicy:
type: Local
Step 4: Get the runner and token info
Get your token info from GitLab
- Go to your GitLab instance at https://sc.unc.edu
- Navigate to Settings → CI/CD → Runners
- Find or create a runner and copy the token


NOTE: It doesn’t matter what OS is selected, the token displayed would remain same for that particular runner.
Step 5: Create ConfigMap
The Advanced Configuration stores the config.toml configuration file for registering runner.
In your OpenShift project:
- Go to Workloads → ConfigMaps
- Create ConfigMap
- Replace the token line with:
token = "your-actual-token-here" (sample config.toml below)
- Click Save

Sample config.toml
concurrent = 4
check_interval = 0
log_level = "info"
listen_address = ":9252" # Optional: for metrics
[session_server]
session_timeout = 1800
[[runners]]
name = "ocp-test-runner" # Use the runner name created in Gitlab
url = "https://sc.unc.edu"
token = "<redacted>" # Use the token generated in Gitlab
executor = "kubernetes"
request_concurrency = 4
environment = ["FF_USE_ADAPTIVE_REQUEST_CONCURRENCY=true"] # Auto-adjust concurrency
[runners.kubernetes]
namespace = "<openshift_name_space>"
image = "docker.io/gitlab/gitlab-runner-helper:x86_64-bleeding"
# CORRECTED helper image tag (no extra hyphen)
helper_image = "gitlab/gitlab-runner-helper:x86_64-bleeding"
service_account = <gitlab-runner-sa>
privileged = false
pull_policy = "if-not-present"
[runners.kubernetes.volumes]
[[runners.kubernetes.volumes.empty_dir]]
name = "runner-home"
mount_path = "/home/gitlab-runner"
medium = "Memory"
# THIS FIXES THE GIT PERMISSION ISSUE
[[runners.kubernetes.env]]
name = "HOME"
value = "/tmp"
# Also set a writable GIT_CONFIG path
[[runners.kubernetes.env]]
name = "GIT_CONFIG"
value = "/tmp/.gitconfig"
# Method 1: Using env_variables (preferred)
[[runners.kubernetes.env_variables]]
name = "HOME"
value = "/tmp"
[[runners.kubernetes.env_variables]]
name = "GIT_CONFIG"
value = "/tmp/.gitconfig"
Step 6: Create deployment

Your deployment will need writable home directory, so add volumes and add ConfigMap so the gitlab-runner will register. That should get the pod up and running. deployment.yml should look something like this:
kind: Deployment
apiVersion: apps/v1
metadata:
name: gitlab-runner-docker-hub
namespace: <openshiftnamespace>
spec:
replicas: 1
selector:
matchLabels:
app: gitlab-runner-docker-hub
template:
metadata:
creationTimestamp: null
labels:
app: gitlab-runner-docker-hub
spec:
restartPolicy: Always
serviceAccountName: <gitlab-runner-sa>
schedulerName: default-scheduler
terminationGracePeriodSeconds: 30
securityContext: {}
containers:
- resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
terminationMessagePath: /dev/termination-log
name: container
command:
- /bin/sh
env:
- name: CI_SERVER_URL
value: 'https://sc.unc.edu'
- name: CI_SERVER_TOKEN
valueFrom:
secretKeyRef:
name: pc-docker-test-runner-secret
key: CI_SERVER_TOKEN
- name: HOME
value: /tmp/runner-home
securityContext:
capabilities:
drop:
- ALL
allowPrivilegeEscalation: false
imagePullPolicy: IfNotPresent
volumeMounts:
- name: runner-home
mountPath: /tmp/runner-home
- name: config
mountPath: /tmp/runner-home/.gitlab-runner
terminationMessagePolicy: File
image: 'image-registry.openshift-image-registry.svc:5000/<namespace>/gitlab-runner-docker-hub@sha256:c4209fddcacdd77d8d228aea57f91486d059f6952befaf266e30d22a487407e5'
args:
- '-c'
- |
echo "Using config from ConfigMap at /tmp/runner-home/.gitlab-runner/config.toml"
echo "Starting runner..."
exec gitlab-runner run --user=gitlab-runner --working-directory=/tmp/runner-home
serviceAccount: pc-gitlab-runner-sa
volumes:
- name: runner-home
emptyDir: {}
- name: config
configMap:
name: pc-test-gitlab-runner-config #configmap name
defaultMode: 420
dnsPolicy: ClusterFirst
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
revisionHistoryLimit: 10
progressDeadlineSeconds: 600
Restart the deployment
- Go to Workloads → Deployments
- Click
gitlab-runner-docker-hub
- Click Actions → Restart Rollout
Verify the logs for the running pods

OR via CLI
oc logs -f deployment/gitlab-runner-docker-hub -n <projectname>
At this point, the status of the runner in Gitlab UI should be online

Step 7: Create pipeline
Create .gitlab-ci.yml file in your Gitlab project’s repository and can be updated by going to Build > PipelineEditor.
Here is an working example of CI file
variables:
HOME: "/tmp"
before_script:
- echo "===== PREPARING ENVIRONMENT ====="
- echo "===== INSTALLING GIT ====="
- git version
- echo "===== CONFIGURING GIT ====="
- git config --global --add safe.directory "$CI_PROJECT_DIR"
- git config --global user.email "runner@example.com"
- git config --global user.name "GitLab Runner"
- echo "===== DEBUG INFO ====="
- whoami
- id
- pwd
- ls -la
- echo "HOME=$HOME"
- echo "CI_PROJECT_DIR=$CI_PROJECT_DIR"
- echo "===== READY ====="
test-job:
script:
- echo "Running test job..."
- echo "System info:- $(uname -a)"
- echo "Current directory:- $(pwd)"
- echo "Git status:"
- git status
- echo "Creating a test file"
- echo "Test content $(date)" > test-file.txt
- echo "Adding file to git"
- git add test-file.txt
- echo "Committing file"
- git commit -m "Add test file from CI job [skip ci]"
- echo "Git log:"
- git log --oneline -1
- echo "All tests passed successfully!"
after_script:
- echo "Job completed with exit code:- $?"
Step 8: Run pipeline
- Trigger the pipeline or run it manually or rerun existing job.
- You can view the status of the pipeline from Build > Jobs

- Click on the job to see the pipeline output
Running with gitlab-runner 18.10.0~pre.781.gae674a65 (ae674a65)
on ocp-test-runner QtDNHcjuV, system ID: r_xxxxxx
Preparing the "kubernetes" executor 00:00
Using default image
Using Kubernetes namespace: <openshit-namespace>
Using Kubernetes executor with image docker.io/gitlab/gitlab-runner-helper:x86_64-bleeding ...
Using attach strategy to execute scripts...
Using effective pull policy of [IfNotPresent] for container build
Using effective pull policy of [IfNotPresent] for container helper
Using effective pull policy of [IfNotPresent] for container init-permissions
Preparing environment 00:14
Using FF_USE_POD_ACTIVE_DEADLINE_SECONDS, the Pod activeDeadlineSeconds will be set to the job timeout: 1h0m0s...
Waiting for pod <namespace>/runner-qtdnhcjuv-project-5051-concurrent-0-ur08v3s9 to be running, status is Pending
Running on runner-qtdnhcjuv-project-5051-concurrent-0-ur08v3s9 via gitlab-runner-docker-hub-7f7d5d4f78-zgsbc...
Getting source from Git repository 00:01
Gitaly correlation ID: 01KKX5V5HMW266V2P3BT5S45T4
Fetching changes with git depth set to 20...
Initialized empty Git repository in /builds/<namespace>/ocp-testapp/.git/
Created fresh repository.
Checking out f57fb498 as detached HEAD (ref is main)...
Skipping Git submodules setup
Executing "step_script" stage of the job script 00:01
$ echo "===== PREPARING ENVIRONMENT ====="
===== PREPARING ENVIRONMENT =====
$ echo "===== INSTALLING GIT ====="
===== INSTALLING GIT =====
$ git version
git version 2.47.3
$ echo "===== CONFIGURING GIT ====="
===== CONFIGURING GIT =====
$ git config --global --add safe.directory "$CI_PROJECT_DIR"
$ git config --global user.email "runner@example.com"
$ git config --global user.name "GitLab Runner"
$ echo "===== DEBUG INFO ====="
===== DEBUG INFO =====
$ whoami
1014910000
$ id
uid=1014910000(1014910000) gid=0(root) groups=0(root),1014910000
$ pwd
/builds/<namespace>ocp-testapp
$ ls -la
total 16
drwxrwsrwx 3 1014910000 1014910000 74 Mar 17 05:56 .
drwxrwsrwx 4 1014910000 1014910000 48 Mar 17 05:56 ..
drwxrwsrwx 6 1014910000 1014910000 113 Mar 17 05:56 .git
-rw-rw-rw- 1 1014910000 1014910000 2329 Mar 17 05:56 .gitlab-ci.yml
-rw-rw-rw- 1 1014910000 1014910000 6175 Mar 17 05:56 README.md
-rw-rw-rw- 1 1014910000 1014910000 137 Mar 17 05:56 index.php
$ echo "HOME=$HOME"
HOME=/tmp
$ echo "CI_PROJECT_DIR=$CI_PROJECT_DIR"
CI_PROJECT_DIR=/builds/<namespace>/ocp-testapp
$ echo "===== READY ====="
===== READY =====
$ echo "Running test job..."
Running test job...
$ echo "System info:- $(uname -a)"
System info:- Linux runner-qtdnhcjuv-project-5051-concurrent-0-ur08v3s9 5.14.0-427.109.1.el9_4.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Jan 28 17:07:26 EST 2026 x86_64 GNU/Linux
$ echo "Current directory:- $(pwd)"
Current directory:- /builds/<namespace>/ocp-testapp
$ echo "Git status:"
Git status:
$ git status
HEAD detached at f57fb49
nothing to commit, working tree clean
$ echo "Creating a test file"
Creating a test file
$ echo "Test content $(date)" > test-file.txt
$ echo "Adding file to git"
Adding file to git
$ git add test-file.txt
$ echo "Committing file"
Committing file
$ git commit -m "Add test file from CI job [skip ci]"
[detached HEAD d535b50] Add test file from CI job [skip ci]
1 file changed, 1 insertion(+)
create mode 100644 test-file.txt
$ echo "Git log:"
Git log:
$ git log --oneline -1
d535b50 Add test file from CI job [skip ci]
$ echo "All tests passed successfully!"
All tests passed successfully!
Running after_script 00:01
Running after script...
$ echo "Job completed with exit code:- $?"
Job completed with exit code:- 0
Cleaning up project directory and file based variables 00:00
Job succeeded