Terminal dependencies for CircleCI workflows: Always run what matters
Staff Software Engineer
When a job fails, gets canceled, or never runs, the work that still needs to happen afterward (cleanup, notifications, teardown) has no clean way to trigger. There is no easy way to express “run this no matter what” in your pipeline config without duplicating jobs or adding fragile workaround branches.
Terminal jobs change that.
With terminal jobs in CircleCI, you can now express “run this when upstream work is done, no matter how it ended” without duplicating config or adding fragile workaround branches.
Why this matters
Most teams eventually hit a version of this problem:
- You need to always clean up temporary resources after tests.
- You need to always notify people when a pipeline reaches an end state.
Historically, these patterns forced teams into repetitive config: duplicate jobs per failure path, brittle dependency chains, and hard-to-read workflows that grow quickly with every new branch condition.
Terminal jobs reduce that complexity and make your workflow intent clearer.
What terminal jobs are
CircleCI now supports a terminal dependency state in requires that lets you define downstream behavior more naturally.
You can use terminal when you mean “any final state” in one concise expression. This gives you a direct way to introduce “terminal” behavior in pipeline configuration.
Common use cases
1) Always tear down ephemeral environments
When a setup job succeeds, teardown should run even if tests fail, are canceled, or never start.
workflows:
main:
jobs:
- setup-env
- integration-test:
requires:
- setup-env
- teardown-env:
requires:
- setup-env: success
- integration-test: terminal
Here, teardown-env requires setup-env to succeed but only needs integration-test to reach a terminal state. That means teardown runs whether integration tests pass, fail, get canceled, or never start.
2) Send one final notification without duplicating jobs
Instead of creating separate notify jobs for each outcome, run one notification job once upstream work reaches a final state.
workflows:
main:
jobs:
- deploy
- release:
requires:
- deploy
- notify:
requires:
- release: terminal
The notify job waits for release to finish, regardless of outcome. Without terminal, you would need separate notification jobs for each possible result of release, or a chain of conditional branches to cover every path.
Impact in practice
This feature helps teams:
- Write less repetitive config
- Express intent more clearly in workflows
- Reduce operational risk from missing cleanup paths
- Standardize reliable “finally” patterns across projects
In short: fewer config workarounds, more predictable automation.
Getting started
Start by identifying workflows where you currently duplicate downstream jobs to cover different outcomes. Those are usually the fastest wins.
Then:
- Replace long outcome lists with
terminalwhere the intent is “run when done.” - Validate in one representative pipeline, then roll out broadly.
You can find the complete release details in our changelog.