Intro
GitLab CI/CD basic commands with practical examples should move you from an observed problem to a verified fix. Start by identifying the installed version, deployment topology, prerequisites, and the exact component you are inspecting.
This guide is for developers, DevOps consultants, and technical startup teams who operate GitLab CI/CD daily. It connects basic commands, practical examples, and a cheat-sheet approach to operational safety: observe before changing, limit blast radius, use placeholders instead of secrets, verify every result, and document recovery paths.
Every procedure below follows the same pattern:
- Read-only observation first.
- Define expected result and failure signal before changing anything.
- Make the smallest justified change with a known rollback.
- Verify the outcome with a concrete command or API response.
No real credentials, tokens, private keys, or production identifiers appear in this article. Replace all placeholders with your own values.
Version and Environment Inventory
Before running any GitLab CI/CD command, know what you are working with. The version determines which flags, API endpoints, and configuration syntax are valid. The topology tells you where runners live, how they connect, and what logs you can reach.
1. Check GitLab server version
Use the version API or the Rails console on the GitLab instance.
# From the GitLab host (read-only)
sudo gitlab-rake gitlab:env:info
Expected output includes:
GitLab information
Version: 16.11.0
Revision: 1234567
Directory: /opt/gitlab/embedded/service/gitlab-rails
DB Adapter: PostgreSQL
URL: https://gitlab.example.com
HTTP Clone URL: https://gitlab.example.com/some-group/some-project.git
SSH Clone URL: [email protected]:some-group/some-project.git
Failure signals:
- "Permission denied" -> you need sudo or GitLab admin access.
- "rake aborted" -> GitLab installation may be broken; check
gitlab-ctl status.
2. Check GitLab Runner version
On each runner host:
gitlab-runner --version
Expected output:
Version: 16.11.0
Git revision: 4b5e6f1
Git branch: 16-11-stable
GO version: go1.21.5
Built: 2024-01-15T14:00:00Z
OS/Arch: linux/amd64
Why versions matter: Runner 16.x uses --registration-token for runner registration; newer versions may use an authentication token from the UI. Mixing versions can lead to "500 Internal Server Error" during job registration.
3. List registered runners (requires admin token)
The runners API shows all runners, their status, tags, and executors. Use a read-only GET request with an admin personal access token (PAT).
curl --header "PRIVATE-TOKEN: <your_admin_pat>" \
"https://gitlab.example.com/api/v4/runners?per_page=50"
A healthy runner response includes:
[
{
"id": 42,
"description": "docker-runner-prod",
"active": true,
"online": true,
"status": "online",
"runner_type": "group_type",
"tag_list": ["docker", "linux", "prod"]
}
]
Failure signals:
- HTTP 401 -> invalid PAT or missing
read_apiscope. - Empty array
[]-> no runners registered or token lacks permission.
4. Record current state before any change
Write down the output of the commands above, including timestamps. This is your rollback point. For example:
date -u +"%Y-%m-%dT%H:%M:%SZ"
sudo gitlab-rake gitlab:env:info > gitlab_env_$(date +%F_%T).log
gitlab-runner --version > runner_version_$(date +%F_%T).log
Store these logs in a secure location, not in the repository.
Safe Configuration Path
Configuration changes should be small, version-appropriate, and reversible. Never edit multiple files at once, and never put secrets in gitlab-ci.yml.
1. Edit gitlab-ci.yml safely
The pipeline definition is the most frequently changed file. Use the GitLab CI Lint API to validate it before pushing.
# Use a project access token or PAT with 'api' scope
curl --header "Content-Type: application/json" \
--header "PRIVATE-TOKEN: <your_pat>" \
--data '{"content": "include:\n - local: /templates/.gitlab-ci-template.yml\n\nstages:\n - test\n\ntest_job:\n stage: test\n script: echo \"Running tests\""}' \
"https://gitlab.example.com/api/v4/ci/lint"
Expected success response:
{
"valid": true,
"errors": [],
"warnings": [],
"merged_yaml": "---\ninclude:\n- local: /templates/.gitlab-ci-template.yml\nstages:\n- test\ntest_job:\n stage: test\n script: echo 'Running tests'\n"
}
Failure signals:
"valid": falsewith anerrorsarray -> fix syntax before committing.- HTTP 401 -> token lacks api scope.
2. Configure a runner for a project
If a project needs a dedicated runner, register it with a project-specific token (obtained from Settings > CI/CD > Runners). Use non-interactive registration to avoid prompts:
sudo gitlab-runner register \
--non-interactive \
--url "https://gitlab.example.com" \
--registration-token "<project_registration_token>" \
--executor "docker" \
--docker-image "alpine:3.19" \
--description "project-x-runner" \
--tag-list "docker,linux"
Then verify registration:
sudo gitlab-runner verify
Expected output:
Runtime platform arch=amd64 os=linux pid=1234 revision=4b5e6f1 version=16.11.0
Running in system-mode.
Verifying runner... is alive runner=abc123
Recovery: If the runner does not appear in the GitLab UI, check /etc/gitlab-runner/config.toml for the correct URL and token, then run gitlab-runner restart.
3. Move a runner between groups/projects safely
Changing a runner's scope requires de-registering and re-registering. This disrupts running jobs on that runner. Plan maintenance windows accordingly.
- Pause the runner in GitLab UI (Admin > Runners > click runner > Pause).
- Wait for any active jobs to finish.
- Unregister the runner from the old scope via API or UI.
- Re-register with the new scope token.
- Unpause and verify.
4. Use CI/CD variables securely
Protect secrets using masked and protected variables. In GitLab UI: Settings > CI/CD > Variables > Add variable.
- Key:
AWS_SECRET_ACCESS_KEY - Value:
dummy-value-123(replace with real secret from your vault) - Mask variable: yes
- Protect variable: yes (only on protected branches/tags)
In .gitlab-ci.yml, reference it without echoing:
deploy:
script:
- aws s3 cp build s3://my-bucket --region us-east-1
Never use echo $AWS_SECRET_ACCESS_KEY in logs. GitLab masks variables that match certain patterns, but it is not bulletproof.
Verification and Diagnostics
After making a change, verify that the system behaves as expected. Use read-only commands and API queries.
1. View pipeline status for a commit
Use the pipelines API to get the status of a pipeline for a specific commit SHA.
curl --header "PRIVATE-TOKEN: <your_pat>" \
"https://gitlab.example.com/api/v4/projects/123/pipelines?sha=<commit_sha>"
Expected response (simplified):
[
{
"id": 7654,
"status": "success",
"ref": "main",
"sha": "abcdef123456",
"web_url": "https://gitlab.example.com/group/project/-/pipelines/7654"
}
]
2. Fetch job logs for a failed job
When a pipeline fails, identify the failed job and pull logs via API:
# First list jobs for pipeline 7654
curl --header "PRIVATE-TOKEN: <your_pat>" \
"https://gitlab.example.com/api/v4/projects/123/pipelines/7654/jobs"
# Then get the trace for a specific job id (e.g., 98765)
curl --header "PRIVATE-TOKEN: <your_pat>" \
"https://gitlab.example.com/api/v4/projects/123/jobs/98765/trace" > job_98765.log
Inspect the log:
grep -n "ERROR" job_98765.log
Common error patterns:
fatal: unable to access 'https://gitlab.example.com/...': The requested URL returned error: 403-> check runner network or credentials.ERROR: Job failed (system failure): Error response from daemon: pull access denied-> Docker image private or wrong credentials.
3. Monitor runner health
Check runner concurrency and offline runners from the API:
curl --header "PRIVATE-TOKEN: <your_admin_pat>" \
"https://gitlab.example.com/api/v4/runners/all?status=online"
Compare with your expected runner pool. If a runner is offline, check the runner host:
sudo gitlab-runner status
sudo gitlab-runner list
4. Verify configuration with CI Lint
Always lint the entire pipeline after changes. The API accepts include paths and returns the merged YAML.
curl --header "Content-Type: application/json" \
--header "PRIVATE-TOKEN: <your_pat>" \
--data '{"content": "include:\n - project: group/templates\n ref: main\n file: /ci/base.yml\n\nstages:\n - build\n\nbuild_job:\n stage: build\n script: echo hello"}' \
"https://gitlab.example.com/api/v4/ci/lint"
Failure Modes and Recovery
Even careful changes can go wrong. Know the common failure modes and how to recover without making things worse.
Failure: Authentication failure when registering runner
Symptom: ERROR: Registering runner... forbidden (check registration token)
Why: Registration token expired, revoked, or you are using a personal access token instead of a runner registration token.
Recovery:
- Obtain a fresh registration token from GitLab UI (Settings > CI/CD > Runners > Register a group runner or project runner).
- Ensure the token is copied exactly, without trailing spaces.
- If using an authentication token (newer method), create it via API:
curl --request POST --header "PRIVATE-TOKEN: <your_pat>" \
"https://gitlab.example.com/api/v4/user/runners" \
--data "runner_type=project_type&project_id=123&description=my-runner"
The response contains a token field. Use that token with gitlab-runner register --token <token>.
Failure: Pipeline stuck in pending
Symptom: Pipeline shows pending for a long time, no runner picks it up.
Possible causes:
- No active runner with matching tags.
- Runner is offline or paused.
- Runner concurrency limit reached.
- Runner not assigned to the project/group.
Diagnostic steps:
- List runners for the project:
curl --header "PRIVATE-TOKEN: <your_pat>" \
"https://gitlab.example.com/api/v4/projects/123/runners"
Check active: true and online: true.
- Check runner tags. If job has
tags: [docker, linux], ensure a runner has both tags. - Check runner concurrency in
/etc/gitlab-runner/config.toml:
concurrent = 2
Increase if needed, then restart runner.
- If no runner available, register a new one or enable a shared runner.
Failure: Secret leaked in job logs
Symptom: A masked variable appears unmasked or a secret is accidentally echoed.
Why: The variable did not meet masking requirements (no spaces, length >8, not a known pattern) or you explicitly printed it.
Recovery:
- Immediately revoke the secret (e.g., rotate AWS key, reset password).
- Edit the job to remove the echo statement.
- Purge job logs if necessary via API (admin only):
curl --request DELETE --header "PRIVATE-TOKEN: <your_admin_pat>" \
"https://gitlab.example.com/api/v4/projects/123/jobs/98765/artifacts"
- Review CI/CD variables: remove unused secrets, enforce masking.
Operations Checklist
Use this checklist before and after any CI/CD change. Assign an owner to each item and review it monthly.
| # | Check | Command / API | Expected | Owner | Frequency |
|---|---|---|---|---|---|
| 1 | GitLab version supported | sudo gitlab-rake gitlab:env:info | Version matches supported matrix | DevOps lead | Monthly |
| 2 | Runner versions match GitLab | gitlab-runner --version on each runner | Runner version within one major of GitLab | CI admin | Monthly |
| 3 | All runners online | curl --header "PRIVATE-TOKEN: <pat>" "https://gitlab.example.com/api/v4/runners/all?status=online" | No unexpected offline runners | CI admin | Weekly |
| 4 | CI config valid | curl ... /ci/lint with latest config | "valid": true | Pipeline owner | Before every merge |
| 5 | Secrets are masked | Review CI/CD variables in UI | All secrets masked and protected | Security officer | Quarterly |
| 6 | Pipeline success rate | Project CI/CD analytics | >90% success on main | Project owner | Monthly |
| 7 | Runner concurrency sufficient | grep concurrent /etc/gitlab-runner/config.toml | No jobs waiting >5 min | CI admin | Monthly |
| 8 | Backup of gitlab-ci.yml and runner configs | Version control + config backup | Config can be restored in under 15 min | Configuration manager | Weekly |
Owner note: For each item, one named individual is accountable, not a team. They should receive alerts and review the check on the stated frequency. Update this checklist as your environment evolves.
Common Pitfalls and How to Avoid Them
Pitfall 1: Using the wrong token type
Why it happens: GitLab has many token types: personal access, project access, group access, runner registration, runner authentication, deploy tokens. Mixing them leads to authentication failures.
How to avoid: Always check the API docs for required token type and scopes. Use dedicated service accounts with least privilege.
Recovery: If you receive 401/403, verify token scope and expiration. Create a new token with correct permissions.
Pitfall 2: Hardcoding secrets in .gitlab-ci.yml
Why it happens: Developers want quick fixes and sometimes embed passwords directly.
How to avoid: Use CI/CD variables (Settings > CI/CD > Variables) and never commit secrets. Enable secret detection in pipeline.
Recovery: If secret is committed, treat it as compromised: rotate immediately, remove from git history, and re-encrypt any affected data.
Pitfall 3: Editing runner config while jobs are running
Why it happens: Trying to fix a runner without pausing it first.
How to avoid: Pause the runner in UI, wait for jobs to finish, then modify config.
Recovery: If a runner becomes inconsistent, re-register it from scratch after unregistering.
Pitfall 4: Not pinning Docker image versions
Why it happens: Using image: latest or floating tags leads to unpredictable builds.
How to avoid: Pin to a specific version like image: alpine:3.19.1. Use Dependabot or scheduled pipelines to update pinned versions.
Recovery: Roll back to previous known-good image tag in the pipeline definition.
Conclusion
GitLab CI/CD basic commands are only useful when they are version-scoped, observable, and reversible. Copying a command without checking prerequisites and expected output is not an operations procedure; it is gambling.
As a next step, choose one low-risk verification from this guide: check your GitLab and runner versions, lint your CI config, or list your runners. Record the current state, run the documented check, compare the result with the expected signal, and note any gaps in your recovery plan.
A reliable technical workflow makes failure visible, protects sensitive values, limits changes to the intended resource, and defines recovery verification before an incident forces the decision. Use this guide as your day-to-day cheat sheet, and revisit it whenever your environment or GitLab version changes.