A Beginner’s Guide to GitLab CI/CD Pipelines, Runner Setup, and SSH Deployment
This article provides a comprehensive, step‑by‑step guide to setting up GitLab CI/CD pipelines, configuring .gitlab-ci.yml files, registering Docker‑based GitLab Runners, and deploying applications via SSH, covering stages, jobs, and practical code examples for DevOps practitioners.
The purpose of this tutorial is to give a friendly, hands‑on introduction to GitLab CI/CD using a sample application, so readers can get started without reading the entire GitLab documentation.
Continuous Integration works by pushing small code commits to a Git repository; each push triggers a pipeline that builds, tests, and validates the changes before merging into the main branch. Continuous Delivery/Deployment adds a further CI step that automatically deploys the application to a production environment on every push to the default branch.
One of the main benefits of GitLab CI/CD is that the entire workflow is defined in a single .gitlab-ci.yml file placed at the repository root, and the scripts are executed by a GitLab Runner.
Jobs are grouped into stages to form a pipeline . The tutorial defines three example pipelines:
Project Pipeline : installs dependencies, runs linters, and processes all code.
Continuous Integration Pipeline : runs automated tests and builds a distributable version.
Deployment Pipeline : deploys the code to a chosen cloud provider and environment.
The following is a basic .gitlab-ci.yml example that defines three stages (build, test, deploy) and several jobs:
stages:
- build
- test
- deploy
image: alpine
build_a:
stage: build
script:
- echo "This job builds something."
build_b:
stage: build
script:
- echo "This job builds something else."
test_a:
stage: test
script:
- echo "This job tests something. It will only run when all jobs in the"
- echo "build stage are complete."
test_b:
stage: test
script:
- echo "This job tests something else. It will only run when all jobs in the"
- echo "build stage are complete too. It will start at about the same time as test_a."
deploy_a:
stage: deploy
script:
- echo "This job deploys something. It will only run when all jobs in the"
- echo "test stage complete."
deploy_b:
stage: deploy
script:
- echo "This job deploys something else. It will only run when all jobs in the"
- echo "test stage complete. It will start at about the same time as deploy_a."Jobs are executed in the order defined by the stages keyword. The only directive can be used to run specific jobs (e.g., deploying to a staging server vs. production) when commits are pushed to particular branches.
deploy-production:
stage: deploy
script:
- ./deploy_prod.sh
only:
- masterPipeline names are customizable; you can rename deploy-production to any meaningful identifier.
Before adding the YAML file to the repository root, you can validate it with the CI Lint tool or start from one of the UI‑provided templates.
GitLab Runner
GitLab Runner is an open‑source project that executes jobs and reports results back to GitLab. It can run on GNU/Linux, macOS, FreeBSD, and Windows, and can be installed via Docker, binary download, or package manager.
To run a gitlab-runner inside a Docker container, ensure Docker is installed and use an Alpine image:
docker run --rm -it -v /srv/gitlab-runner/config:/etc/gitlab-runner gitlab/gitlab-runner registerDuring registration you will be prompted for the GitLab coordinator URL and a registration token (obtainable from Settings → CI/CD → Runners → Expand → Manual registration ).
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com)
https://gitlab.com
Please enter the gitlab-ci token for this runner
xxxAfter registration the configuration is written to the chosen volume (e.g., /srv/gitlab-runner/config ) and automatically loaded by the Runner.
Deployment via SSH
When CI/CD jobs run inside Docker containers and need to deploy to a private server, you must provide SSH access. Generate an SSH key pair (without a passphrase) and add the private key as a CI/CD variable named SSH_PRIVATE_KEY .
ssh-keygen -t rsa -b 4096 -C "example"
# Copy the content of public key to authorized_keys
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
cd .ssh && cat id_rsaAdd the private key value in Settings → CI/CD → Variables → Expand . Also add a deployment key (read‑only or read‑write) in Settings → Repository → Deploy Keys → Expand by pasting the public key ( id_rsa.pub ).
# Includes deployment pipeline only
image: alpine:3.7
stages:
- deploy
before_script:
## Optionally set Git user info if needed
#- git config --global user.email "[email protected]"
#- git config --global user.name "User name"
deploy_production:
stage: deploy
before_script:
- apk add openssh-client # Add SSH client for alpine
- eval $(ssh-agent -s) # Run the SSH client
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
script:
- ssh -o StrictHostKeyChecking=no username@host_ip_address "cd /project && git pull"
only:
- masterBecause Alpine does not include an SSH client by default, the pipeline installs it via apk add openssh-client . Adjust commands if you use a different base image.
About the Author
Ze Yang, a DevOps practitioner focusing on enterprise‑level DevOps operations and Linux administration, shares practical courses and tutorials.
DevOps Cloud Academy
Exploring industry DevOps practices and technical expertise.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.