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. ### login to openshift
oc project <gitlab-runner-project-name>. ### select your project space
oc create serviceaccount <gitlab-runner-sa> -n <gitlab-runner-project-name>. ### create service account for gitlab runner. Where <gitlab-runner-sa> could be any name you choose.
vi gitlab-runner-rbac.yml ### create a yaml file by using below code. Make sure the name and namespace are updated to reflect the names you chose in the previous steps. This applies necessary roles and permissions for the service account you created.
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"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"]
---
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. #### Apply the yaml file with above config
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