Skipping CircleCI Pipelines: [ci skip], Conditional Workflows, and Pipeline Parameters

1 min readCloud Infrastructure

CircleCI triggers a pipeline on every push by default. [ci skip] or [skip ci] in the commit message prevents that run. For finer control — skipping based on changed files, branch, or tag — conditional workflows and pipeline parameters let you run only the jobs relevant to what changed.

ci-cdcircleci

Skipping a pipeline with commit message tags

Adding [ci skip] or [skip ci] anywhere in the first 250 characters of the latest commit message body or title skips the pipeline for that push:

git commit -m "chore: update README [ci skip]"
# or
git commit -m "[skip ci] fix typo in docs"

Both tags have identical behavior. CircleCI evaluates the latest commit in the push — if any of the skipped tags are present, no pipeline triggers for any commit in that push.

[ci skip] only works on the latest commit in a push — not on earlier commits in the same push

GotchaCircleCI

When you push multiple commits at once, CircleCI only checks the message of the latest (HEAD) commit for skip tags. If the latest commit doesn't contain [ci skip], a pipeline triggers even if an earlier commit in the push has it.

Prerequisites

  • CircleCI pipeline triggers
  • git push behavior

Key Points

  • [ci skip] and [skip ci] are equivalent — either works.
  • Tags must appear in the first 250 characters of the commit title or body.
  • Only the latest commit in a push is checked — not the entire push's commit list.
  • Doesn't apply to pipelines triggered manually or via the API.

Conditional workflows: skip based on branch or tag

For branch-based skipping without modifying commit messages, use workflow filters:

version: 2.1

workflows:
  build-and-test:
    jobs:
      - build:
          filters:
            branches:
              ignore:
                - gh-pages     # never run on this branch
                - /docs\/.*/   # regex: skip branches starting with docs/
      - deploy:
          filters:
            branches:
              only: main        # only run deploy on main
          requires:
            - build

Tag filters work the same way:

workflows:
  release:
    jobs:
      - publish:
          filters:
            tags:
              only: /^v\d+\.\d+\.\d+$/   # semver tags only
            branches:
              ignore: /.*/                # don't run on branches, only tags

Pipeline parameters: conditional jobs based on changed files

For monorepos or repositories where only specific directories changed, pipeline parameters enable skipping jobs when their relevant code wasn't modified:

version: 2.1

parameters:
  run-api-tests:
    type: boolean
    default: false
  run-frontend-tests:
    type: boolean
    default: false

workflows:
  ci:
    when: << pipeline.parameters.run-api-tests >>
    jobs:
      - test-api

  frontend-ci:
    when: << pipeline.parameters.run-frontend-tests >>
    jobs:
      - test-frontend

Trigger with parameters via API when pushing:

# Trigger only frontend tests
curl -X POST https://circleci.com/api/v2/project/gh/org/repo/pipeline \
  -H "Circle-Token: $CIRCLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "branch": "main",
    "parameters": {
      "run-api-tests": false,
      "run-frontend-tests": true
    }
  }'
📝Path filtering orb: skip jobs when files outside a path don't change

CircleCI's path-filtering orb dynamically sets pipeline parameters based on which files changed in a push. This automates the conditional logic without manual API calls:

version: 2.1

orbs:
  path-filtering: circleci/[email protected]

setup: true   # mark this as a setup workflow

workflows:
  setup:
    jobs:
      - path-filtering/filter:
          base-revision: main
          config-path: .circleci/continue_config.yml
          mapping: |
            api/.*        run-api-tests        true
            frontend/.*   run-frontend-tests   true
            shared/.*     run-api-tests         true
            shared/.*     run-frontend-tests    true

The mapping field maps path patterns to pipeline parameters. If api/ changes, run-api-tests is set to true. If only docs/ changes, neither parameter is set and no test jobs run.

This requires a separate continue_config.yml that contains the actual jobs — the setup workflow uses path-filtering/filter to generate a continuation with the computed parameters.

You push two commits to a feature branch. Commit A has '[ci skip]' in its message. Commit B (the latest) does not. A pipeline triggers and runs all jobs. Why wasn't the pipeline skipped?

easy

Both commits were pushed in the same git push. Commit A is the earlier commit, Commit B is HEAD.

  • A[ci skip] only works on the main branch — it's ignored on feature branches
    Incorrect.[ci skip] works on any branch. Branch filtering is a separate mechanism using workflow filters.
  • BCircleCI only checks the latest commit (HEAD) in a push for skip tags — Commit A's tag is ignored because Commit B is the latest commit and doesn't have [ci skip]
    Correct!CircleCI evaluates skip tags from the most recent (HEAD) commit only. When you push multiple commits, only the last one is checked. Commit B is HEAD and has no skip tag, so the pipeline runs normally. To skip this push, Commit B (HEAD) needs [ci skip] in its message, or you can amend it before pushing: git commit --amend -m '[ci skip] ...'
  • C[ci skip] must appear in the commit title, not the body
    Incorrect.[ci skip] is checked in both the title and the body, within the first 250 characters.
  • DThe tag is case-sensitive — [CI SKIP] would work but [ci skip] doesn't
    Incorrect.CircleCI's skip tag check is case-insensitive. [ci skip], [CI SKIP], and [Ci Skip] all work.

Hint:Which commit does CircleCI check for [ci skip] when multiple commits are pushed at once?