Skip to content

Jobs and Steps in GitHub Actions

In our journey through GitHub Actions, we’ve covered the basics and explored the main events that trigger workflows. Now, it’s time to get into the nuts and bolts of workflows: jobs and steps.

The building blocks of GitHub Actions

A GitHub Actions workflow is composed of one or more jobs. Each job contains a set of steps that execute commands or actions. Think of jobs as individual tasks in your workflow, and steps as the specific instructions needed to complete those tasks.


Jobs run in parallel by default, but they can be configured to run sequentially if there are dependencies between them. Each job runs in its own fresh instance of a virtual environment, allowing you to run jobs on different operating systems or environments within the same workflow.

Why jobs matter:

  • Isolation: since each job runs in a separate environment, you can test your application across different OSs without interference.
  • Parallelization: speed up your workflow by running your tests, builds, or deployments in parallel.
  • Dependency management: control the order of operations by specifying job dependencies, ensuring that certain jobs run only after others have completed successfully.


Steps are the smallest unit of work within a job and can either run a script or an action. A step to run a script could execute shell commands, while a step that uses an action could perform more complex tasks like setting up a Node environment or deploying to AWS.

Why steps matter:

  • granularity: steps allow you to break down your jobs into manageable, discrete tasks, making your workflows easier to understand and maintain.
  • reusability: by using actions in your steps, you can leverage community-built solutions to common problems, making your workflows more powerful without extra code.
  • flexibility: customize each step to fit your specific needs, whether it’s running tests, linting code, or anything else your project requires.

Putting it all together

Let’s create a workflow that demonstrates how jobs and steps work together. We’ll set up a workflow for a simple Node.js project that installs dependencies, runs tests, and builds the project.

  1. Create a workflow file: start by creating a new workflow file in your repository under .github/workflows/ci.yml.

  2. Define the workflow:

name: Node.js CI
on: [push]
runs-on: ubuntu-latest
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
node-version: "20"
- name: Install Dependencies
run: npm install
- name: Run Tests
run: npm test
- name: Build
run: npm run build

In this workflow:

  • We trigger the workflow on every push event.
  • We define a single job called build that runs on the latest Ubuntu environment.
  • Within the build job, we have a series of steps that check out the code, set up Node.js, install dependencies, run tests, and build the project.
  • The first 2 steps rely on external GitHub Actions (more on that later), while the following steps execute shell commands.


This workflow demonstrates how jobs and steps work hand in hand to automate your development process. Each step performs a specific task within the job, leading to a (hopefully) successful build of your project.

Note that this workflow will unnecessarily reinstall your Node.js dependencies on every push. We’ll learn how to cache dependencies to make this process more efficient in the next article.