Skipping CircleCI Pipelines: [ci skip], Conditional Workflows, and Pipeline Parameters
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.
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
GotchaCircleCIWhen 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?
easyBoth 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?