Stages: work order and stopping on failure
We split the pipeline into build and test, see how stages run in order, and why a failed build cancels everything after it.
We split the pipeline into build and test, see how stages run in order, and why a failed build cancels everything after it.
Why split into stages
One job is enough right up until there's more than one job to do. Build, check, deploy: these steps need to run in a specific order, and deploying only makes sense for something that passed the check.
Stages set the order. A stage is a named step of the pipeline that holds one or more jobs. The list of stages is declared once at the top of the file, and each job says which stage it belongs to.
stages:
- build
- test
build-site:
stage: build
script:
- echo "Building the site"
- test -f index.html
check-title:
stage: test
script:
- grep "<title>" index.html
Push this to your project in place of the old config. The pipeline page will show two columns instead of one, one column per stage.
How it runs
Stages run strictly in sequence: until all of build finishes, not a single job from test
starts.
Inside one stage it's the opposite: jobs run in parallel if runners are free. Two tests in the
test stage will run at the same time, and won't double the total time.
A pipeline with two stages takes longer than one job with the same commands. Each job waits for its own runner and gets its own container with a fresh clone of the repo; the container is never reused. So splitting work into a dozen tiny jobs for the sake of it isn't worth it: each one adds startup time.
A failure stops the pipeline
Check this key property of stages by hand. Break a command in build:
build-site:
stage: build
script:
- echo "Building"
- test -f nonexistent.html
Result on the pipeline page:
build-site failed
check-title skipped
skipped, not failed. The test stage never even started: GitLab didn't bother spending a
runner on checking something that didn't build.
Inside a stage, a neighbor's failure doesn't stop anything. If test has two jobs and one fails,
the other still finishes. Only the next stage stops.
Hence the rule for ordering stages: the cheaper and faster a check is, the earlier it should sit. A linter that fails in five seconds saves ten minutes of integration tests.
Stage names mean nothing
build, test and deploy are just strings. GitLab doesn't know that build means building, and
doesn't check what you actually do there. Only the order in the stages list matters.
If you don't declare stages at all, GitLab uses its own list: .pre, build, test, deploy,
.post. The built-in .pre and .post always come first and last, no matter what you write. A
job with no stage specified falls into test, which is why the pipeline from the previous lesson
worked without the word stages.
Try the config without breaking main
You don't have to run experiments on the main branch. A pipeline runs on any branch:
git checkout -b pipeline-test
git push -u origin pipeline-test
The pipeline list will show the branch name next to the commit. Done experimenting? Merge into
main or delete the branch.
Common mistakes
- A job with no
stage:. It silently falls intotest. If that wasdeploy-site, the deploy would run alongside the tests and not wait for their result. - A typo in a stage name.
stage: tsetwith abuild, testlist gives a config error, and the pipeline doesn't get created. Look for the error message in the pipeline list or in the Pipeline editor. - The order in
stagesis mixed up. For example,deploycomes beforetest: GitLab will deploy the site before the checks, since the stage names give it no hint. - Expensive checks before cheap ones. The pipeline still fails, just later, and burns runner minutes first.
What to remember
stages at the top of the file sets the order, stage: on a job says where it sits.
Stages run in sequence, jobs inside a stage run in parallel.
A failed stage cancels every stage after it: their jobs get status skipped.
Put cheap checks before expensive ones, so the pipeline fails faster and burns fewer minutes.
Next, we'll pass built files from one stage to another.
What you'll need for the tasks
The tasks are done locally, without gitlab.com. The lab environment creates a git repo work/,
and the training runner run-pipeline.sh executes the last commit: stages in order, each job in a
clean alpine:3.20.3 container. At the end it prints a summary per job: passed, failed or
skipped. Unlike GitLab, it runs jobs in one stage one after another, not in parallel. That
doesn't affect the result.
- the
stageskey andstage:on a job; git -C work commit -am "...",git -C work revert --no-edit HEAD;bash run-pipeline.shand reading the final status table.
Done reading?
Mark the lesson as read to move your track progress forward.
Lesson marked as done.
Go through it with an AI agent
The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.
Tell the agent: use MCP izi, give me lesson cicd-practice-03-stages
Progress saved in this browser
Clear your history or open this on your phone, and the marks will be gone. Leave your email and they'll move to your account. No password needed.
Done. moved to your account. You can see your progress on any device. My progress