Conditional Execution and Job Dependencies
Control when steps and jobs run using if conditions, the needs keyword, expressions, and status check functions in GitHub Actions workflows.
Conditional Execution and Job Dependencies is a free DevOps Bootcamp lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the DevOps Bootcamp learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Why Conditions Matter
Not every step should run every time. You might want a deploy step to run only on the main branch, or a notification step to run only when something failed.
GitHub Actions lets you attach an if condition to any step or job to control whether it executes.
The if Key
Add an if: key to a step. The step only runs when the expression evaluates to true.
Expressions inside if do not need the ${{ }} wrapper, though it is allowed.
steps:
- name: Deploy
if: github.ref == 'refs/heads/main'
run: ./deploy.shContext Objects
Conditions read from context objects that describe the run:
github— event, ref, actor, shaenv— environment variablesjobandsteps— status of prior workrunner— OS and architecture
For example, github.event_name tells you whether a push or pull_request triggered the run.
Status Check Functions
By default, a step is skipped if a previous step failed. To override this, use the status functions:
success()— true if no prior step failed (the default)failure()— true if any prior step failedalways()— runs no matter whatcancelled()— true if the run was cancelled
- name: Notify on failure
if: failure()
run: echo 'Build failed, sending alert'Always Run Cleanup
A common pattern is a cleanup step that must run even if earlier steps failed. Use if: always().
This guarantees temporary resources, containers, or test reports are handled regardless of the outcome.
- name: Upload logs
if: always()
uses: actions/upload-artifact@v4
with:
name: logs
path: ./logsJob Dependencies with needs
The needs keyword makes one job wait for others. Without it, jobs run in parallel.
A job listed in needs must finish successfully before the dependent job starts.
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: npm test
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- run: ./deploy.shMultiple Dependencies
A job can depend on several jobs by passing a list to needs. It waits for all of them.
This is how you build fan-in patterns, where a final job runs only after parallel jobs all succeed.
release:
needs: [lint, test, build]
runs-on: ubuntu-latest
steps:
- run: echo 'all checks passed'Conditional Jobs
The if key works on jobs too, not just steps. A job-level condition decides whether the entire job runs.
Here the deploy job runs only for pushes to main, while still depending on tests.
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- run: ./deploy.shUsing Job Outputs
Jobs can pass data downstream via outputs. A dependent job reads them through the needs context.
This lets a condition depend on a value computed earlier, such as whether a version changed.
check:
runs-on: ubuntu-latest
outputs:
changed: ${{ steps.diff.outputs.changed }}
steps:
- id: diff
run: echo 'changed=true' >> $GITHUB_OUTPUTCombining Conditions
You can combine expressions with && (and), || (or), and ! (not).
This example runs only on a successful push to main that is not a fork.
if: success() && github.ref == 'refs/heads/main' && github.event_name == 'push'if Always With needs
Careful: a job with needs is skipped if a dependency fails. To run a final reporting job regardless, combine needs with if: always().
Inside it you can inspect needs.<job>.result to decide what to report.
report:
needs: [test, build]
if: always()
runs-on: ubuntu-latest
steps:
- run: echo ${{ needs.test.result }}Quick Check
Test your understanding of conditional execution.
Recap
You learned to control workflow flow with conditions and dependencies.
ifgates steps and jobs on expressions- Status functions like
failure()andalways()override skip-on-failure needscreates ordering and fan-in patterns- Job
outputsfeed values into later conditions
These primitives let you build precise, efficient pipelines.
Frequently asked questions
Is the “Conditional Execution and Job Dependencies” lesson free?
Yes — the full text of “Conditional Execution and Job Dependencies” is free to read here on the web, and the DevOps Bootcamp course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the DevOps Bootcamp course, upgrade to CoddyKit PRO.
What will I learn in “Conditional Execution and Job Dependencies”?
Control when steps and jobs run using if conditions, the needs keyword, expressions, and status check functions in GitHub Actions workflows. You practise DevOps Bootcamp with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start DevOps Bootcamp?
No prior experience is required. DevOps Bootcamp on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Conditional Execution and Job Dependencies” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this DevOps Bootcamp lesson?
Yes. Every DevOps Bootcamp lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Matrix Builds for Multiple Environments
- Caching Dependencies for Speed
- Reusable Workflows and Actions
- Conditional Execution and Job Dependencies