CI/CD Commands Cheatsheet - CI/CD Pipeline Debugging Reference

When a CI job sits pending or fails for no clear reason, the reflex is to edit YAML and push again, which is both slow and rarely the cause. A better order exists: confirm the runner is online, that its labels match what the job requested, and that concurrency quota is not exhausted; then read the actual log of the failing step rather than its last line; then decide whether you can reproduce locally and eliminate environment differences one at a time. Low cache hit rates usually come from a key missing the lockfile hash or a path that does not cover the real dependency directory, both diagnosable from configuration alone without restructuring the pipeline. Commands for GitHub Actions, GitLab CI and Jenkins are listed in separate sections, alongside Docker multi-stage builds and general practices. Hung jobs point at infrastructure state; failed jobs point at logs. Most investigations end at one of three places, and naming which one you are in keeps the search short.

SysOps·33 commands·Last updated 2026-07-21
cicdgithub-actionsgitlab-cijenkinsrunner

GitHub Actions 6

gh run list --limit 10
List the 10 most recent runs to quickly find failed pipelines
gh run view <run-id> --log-failed
View the failed-step logs for a specific run
gh run rerun <run-id> --failed
Rerun only failed jobs, skipping successful ones
gh run cancel <run-id>
Cancel a running or stuck pipeline
gh workflow run deploy.yml --ref main
Manually trigger a specified workflow
gh api repos/:owner/:repo/actions/runners
Check self-hosted runner online status and labels

GitLab CI 6

gitlab-runner list
List runners registered on this machine
gitlab-runner verify
Verify the runner's connection to GitLab
gitlab-runner run
Start the runner in the foreground to pull jobs (debugging)
gitlab-ci-lint < .gitlab-ci.yml
Validate CI configuration syntax
glab ci list --per-page 20
List recent pipelines with the glab CLI
curl -H "PRIVATE-TOKEN: <token>" "https://gitlab/api/v4/projects/:id/pipelines"
Query pipeline status via the API

Jenkins 5

jenkins-cli build <job> -f -v
Trigger and follow build output (common for CLI debugging)
jenkins-cli console <job> <build>
Fetch the full console log for a build
systemctl status jenkins
Check Jenkins service status (stuck or OOM)
ls -lah /var/lib/jenkins/jobs/<job>/builds/
Inspect disk used by build history
jenkins-cli list-jobs
List all job names before batch scripting

Docker CI Builds 5

docker build -t app:ci --target build .
Multi-stage build targeting a specific stage
docker build --cache-from=app:base .
Speed up builds by reusing cache layers
docker tag app:ci registry/app:$(git rev-parse --short HEAD)
Tag with the short git commit hash
docker buildx build --platform linux/amd64,linux/arm64 .
Multi-arch build and push
docker history app:ci --no-trunc
Inspect each image layer to debug size

Runner & Cache Troubleshooting 5

docker ps --filter "name=runner"
Check if a containerized runner is running
df -h /var/lib/docker
A full CI disk often blocks runners from pulling images or writing logs
act -j <job> -W .github/workflows/ci.yml
Reproduce GitHub Actions locally with act
gitlab-runner exec docker <job>
Reproduce a GitLab CI job locally (older versions)
du -sh ~/.cache/pnpm /root/.cache
Check whether cache dirs are filling the disk

Common Patterns & Best Practices 6

echo "key=value" >> $GITHUB_OUTPUT
Set a step output in GitHub Actions (new syntax)
${{ secrets.NAME }} / $CI_NAME
Reference secrets and CI variables
needs: [build] / depends_on: [test]
Declare job dependencies to control execution order
workflow_dispatch: / schedule: / push:
GitHub Actions trigger configuration
rules: / only: / except:
GitLab CI conditional job execution
artifacts: paths: / expire_in: 1 week
GitLab CI artifact retention and expiry

Command Examples

Locate the latest failed pipeline and pull just the failing step logs

gh run list --limit 10 gh run view <run-id> --log-failed

Confirm the run ID and blast radius in the list first, then fetch only the failing step output — far quicker than opening each job, and it makes the offending command obvious.

Output

STATUS  NAME        WORKFLOW      BRANCH      EVENT  ID          ELAPSED  AGE
failure  CI / build   ci.yml        main        push   1287349921  4m12s    18m

===   Failed steps
===   Run  gh run view 1287349921 --log-failed
Run  webapp (build)
  4:  Run npm ci
  Error: Process completed with exit code 1.

Re-run only the failed jobs, leaving successful ones untouched

gh run rerun <run-id> --failed

Successful jobs keep their artefacts and cache layers, so the rerun is quick and the signal is clean: only the failed job changed, so the difference is your fix. If the failed job genuinely needs freshly generated upstream output, fall back to a full re-run.

Trigger a deploy on demand instead of waiting for the next push

gh workflow run deploy.yml --ref main

The workflow must declare the workflow_dispatch trigger or this command produces no run at all. Pass --ref to pin the branch so a multi-branch repository cannot pick the wrong one.

Runner triage: online state, disk headroom, container liveness

gh api repos/:owner/:repo/actions/runners docker ps --filter "name=runner" df -h /var/lib/docker

Together these three explain most unexplained pending states: an offline runner, a label mismatch, or a container host whose disk is full so image pulls fail. When df approaches 100 percent usage, clear images and stale build layers first.

Output

{
  "total_count": 2,
  "runners": [
    { "name": "ci-runner-01", "os": "Linux", "status": "online", "busy": false, "labels": [{"name": "self-hosted"}] },
    { "name": "ci-runner-02", "os": "Linux", "status": "offline", "busy": false, "labels": [{"name": "self-hosted"}] }
  ]
}
CONTAINER ID   IMAGE            STATUS         NAMES
3f9c1a2b7d10   ci-runner:2024   Up 6 hours     runner-controller
Filesystem      Size  Used Avail Use% Mounted on
/dev/vdb1       100G   98G  2.0G  99% /var/lib/docker

Common Pitfalls

  • CI variables come in protected and masked flavours; sensitive values need both an environment restriction and masking enabled. Protection alone still allows the value to appear in logs.
  • CI logs are retained for a long time, so any token echoed or printed is already leaked and deleting the log afterwards does nothing — revoke and rotate the credential instead.
  • When reproducing locally with act or gitlab-runner exec, your image, shell and toolchain versions differ from the hosted environment. A green local run is not evidence that the hosted run will pass, so do not merge on that basis.
  • A multi-platform docker buildx build only takes effect once the result is pushed to a registry; a local --load cannot hold a multi-arch image, so omitting the push flag wastes the build.
  • Declaring needs or depends_on controls both execution order and artefact availability. Leaving one out makes a downstream job silently miss upstream artefacts rather than fail loudly.

Tips

  • If a job stays pending, check runner online status and label matching first, then concurrency limits — not by repeatedly editing YAML.
  • When cache hit rate is low, check whether the key uses a lockfile hash and whether the path covers the real dependency directory.
  • CI logs may be retained long-term; never echo secrets or tokens while debugging.
  • Multi-stage Docker builds separate build dependencies from the runtime, greatly shrinking the final image.
  • CI variables come in protected and masked types; always mark sensitive values masked to prevent log leakage.
  • Reproduce CI locally with act (GitHub Actions) or gitlab-runner exec instead of pushing repeatedly.

FAQ

A job sits pending forever. Why is editing the YAML the wrong first move?

Pending means the scheduler never dispatched the job, so the YAML has not had a chance to run at all. Establish first that a runner is online and that its labels satisfy what the job requests, then check whether the concurrency allowance is exhausted; for containerised runners use docker ps --filter "name=runner" to confirm the container was not evicted. Infrastructure conditions like labels or quota will not improve no matter how many times you rewrite the pipeline file.

Only some jobs failed. Why prefer rerun --failed over a full re-run?

A full re-run throws away the part that already succeeded, which is usually the expensive half. gh run rerun <run-id> --failed re-runs only the failed jobs while successful ones keep their artefacts and cached layers, making the rerun faster and making it obvious whether your fix actually worked. The caveat is that nothing upstream is recomputed, so use this mode only when the successful jobs genuinely produced reusable output.

Why is the cache hit rate low?

Almost always it is a question of key granularity and path scope. Put the hash of a lockfile or manifest in the key rather than the branch name, otherwise any source edit invalidates the whole cache; and make sure the path list covers the real dependency directory — a correct key with a path pointing at a subdirectory still yields an empty hit. Verify by checking whether the lockfile actually changed between two runs, then confirm the configured path matches where dependencies really land.

The pipeline will not reproduce locally. What should I confirm first?

Confirm the local tool is reading the same configuration you think it is. act -j <job> -W .github/workflows/ci.yml names the workflow file explicitly, and gitlab-runner exec docker <job> demands that the local registration matches the hosted runner — mismatches usually fail loudly rather than drifting silently. Once the job passes locally but still fails hosted, the remaining difference is almost always environmental: OS-level packages baked into the image, environment variables, and reachability of downstream services.

Why must secrets never be echoed into CI logs?

CI logs have a far wider audience than most people assume: failed-step output is retained for a long time, anyone with repo or build access can read it, and once a value lands in a log it is already leaked — deleting the line afterwards does not undo it. Keep sensitive values in masked variables, and when you must debug, print the variable name or a few leading characters rather than the value.

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Jul 21, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us