GitLab CI/CD is GitLab’s built-in continuous integration and delivery system that automatically runs pipelines when code is pushed to a repository. Unlike Jenkins (which requires a separate server and complex plugin configuration), GitLab CI/CD is fully integrated with the GitLab platform — pipelines are defined in a .gitlab-ci.yml file in the repository root and run automatically on GitLab Runners (agents that can be installed on any server). GitLab CI/CD provides an auto-cancellation of redundant pipelines, parallel job execution, Docker-based job isolation, caching, artifacts, and environments for deployment tracking. This guide covers writing .gitlab-ci.yml pipelines on RHEL 9 for build, test, and Docker deployment workflows, and registering a GitLab Runner on RHEL 9.
Prerequisites
- GitLab CE or GitLab.com account
- RHEL 9 server for the GitLab Runner
Step 1 — Install and Register a GitLab Runner
# Install GitLab Runner on RHEL 9
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | bash
dnf install -y gitlab-runner
# Register the runner with your GitLab instance
# Get the registration token from: GitLab → Project → Settings → CI/CD → Runners
gitlab-runner register
--non-interactive
--url https://gitlab.example.com
--registration-token YOUR_TOKEN
--executor docker
--docker-image alpine:latest
--description "RHEL9 Docker Runner"
--tag-list "docker,rhel9"
systemctl enable --now gitlab-runner
Step 2 — Basic .gitlab-ci.yml
# .gitlab-ci.yml — placed in repository root
stages:
- install
- test
- build
- deploy
variables:
NODE_ENV: test
DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
install:
stage: install
image: node:20-alpine
script:
- npm ci
cache:
key: $CI_COMMIT_REF_SLUG
paths:
- node_modules/
test:
stage: test
image: node:20-alpine
script:
- npm test
artifacts:
reports:
junit: test-results/*.xml
when: always
build-image:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $DOCKER_IMAGE .
- docker push $DOCKER_IMAGE
only:
- main
deploy:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/myapp myapp=$DOCKER_IMAGE -n production
- kubectl rollout status deployment/myapp -n production
environment:
name: production
url: https://myapp.example.com
only:
- main
Step 3 — Using GitLab CI/CD Variables (Secrets)
# Store secrets in: Project → Settings → CI/CD → Variables
# Examples: DOCKER_PASSWORD, KUBECONFIG, SSH_PRIVATE_KEY
# Access in .gitlab-ci.yml:
deploy:
script:
- echo "$KUBECONFIG" | base64 -d > /tmp/kubeconfig
- kubectl --kubeconfig=/tmp/kubeconfig apply -f k8s/
- rm /tmp/kubeconfig # Clean up credentials
Step 4 — Pipeline Rules and Triggers
# Conditional job execution with 'rules':
deploy:
rules:
- if: '$CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"'
when: on_success
- if: '$CI_COMMIT_TAG =~ /^vd+.d+.d+$/' # Trigger on version tags
when: manual # Require manual approval for production
Conclusion
GitLab CI/CD on RHEL 9 provides a tightly integrated pipeline system that eliminates the need for a separate CI server. The built-in GitLab Container Registry ($CI_REGISTRY) with pre-populated $CI_REGISTRY_USER and $CI_REGISTRY_PASSWORD variables means Docker image push pipelines require zero credential configuration — GitLab handles authentication automatically. The cache configuration for node_modules/ is the single biggest pipeline speed improvement for Node.js projects, reducing install stage time from 60+ seconds to under 5 seconds for subsequent pipeline runs.
Next steps: How to Install GitLab CE on RHEL 9, How to Install Jenkins on RHEL 9, and How to Set Up a Git Server with Gitea on RHEL 9.