GitHub Actions

Continuous Integration with GitHub Actions

What are Job Containers

In this guide, you’ll learn how job containers in GitHub Actions can speed up your CI/CD workflows, improve reproducibility, and enhance security. By running jobs inside reusable Docker images, you avoid repeated setup steps, cut down execution time, and lower billing costs.

Why Default Runners Can Be Slow

When you use a GitHub-hosted runner (Ubuntu, Windows, or macOS), it comes with many preinstalled tools—but large language runtimes or packages still need to be configured at runtime. Consider this example workflow that installs Node.js v20 and runs unit tests on ubuntu-latest:

name: My Awesome App
on: push
jobs:
  unit-testing:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v3
      - name: Install Node.js v20
        run: |
          curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
          sudo apt-get install -y nodejs
      - name: Install Dependencies
        run: npm install
      - name: Install Testing Packages
        run: npm install --save-dev jest
      - name: Run Tests
        run: npm test

Typical Step Durations

StepDuration
Checkout Code2 s
Install Node.js v2010 m
Install Dependencies3 m
Install Testing Packages45 m
Run Tests5 m
Total63 m

Repeatedly installing Node.js and testing libraries can add over an hour to each run.

What Is a Job Container?

A job container runs all your job’s steps inside a Docker image on the GitHub-hosted runner. Key benefits include:

BenefitDescription
IsolationPrevents conflicts between jobs or with the host VM.
ReproducibilityEnsures identical environments across runs and machines.
SecurityRestricts container permissions, reducing risk from untrusted code.

Note

GitHub Actions also supports service containers for databases or caching layers.
See services for details.

How to Configure a Job Container

Add a container section under your job to pull a prebuilt Docker image:

name: My Awesome App
on: push
jobs:
  unit-testing:
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/node-and-packages:20
      credentials:
        username: alice
        password: ${{ secrets.PWD }}
    steps:
      - name: Checkout Code
        uses: actions/checkout@v3
      - name: Run Tests
        run: npm test

What happens under the hood:

  1. GitHub provisions an ubuntu-latest VM runner.
  2. It pulls ghcr.io/node-and-packages:20, which already includes Node.js v20 and testing dependencies.
  3. Every step executes inside that container—no extra installs required.

Speed Comparison

ConfigurationTotal Duration
Default runner63 m
Job container image~ 5 m

By eliminating repeated installs, job containers can reduce runtime by over 90%.

Warning

Avoid embedding production credentials in your container image. Use GitHub Secrets for sensitive data.

Best Practices

  • Build a custom image with all your project’s common dependencies.
  • Version your container image tags (e.g., :1.0.0 or :stable).
  • Store images in your GitHub Container Registry or Docker Hub.
  • Combine with caching strategies for even faster workflows.

Watch Video

Watch video content

Previous
Project Status Meeting 2