Apr 22, 20262 min read

Terminal dependencies for CircleCI workflows: Always run what matters

Brice Nkengsa

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:

  1. Replace long outcome lists with terminal where the intent is “run when done.”
  2. Validate in one representative pipeline, then roll out broadly.

You can find the complete release details in our changelog.