GitHub-Hosted Runners
Every GitHub Actions workflow runs on a runner, which can be a GitHub-hosted virtual machine or a self-hosted server. A typical configuration looks like this:ubuntu-latest runner includes many common tools—Node.js, Python, Docker, browsers, and package managers—so you can start building and testing right away. However, installing additional dependencies at runtime can slow down your jobs and increase billing minutes.
Introducing Job Containers
A job container is a Docker image in which all steps of a job execute. By shipping a container that already contains everything you need, you:| Benefit | Description |
|---|---|
| Isolation | Each job runs in its own container, preventing conflicts with the host or other jobs. |
| Reproducibility | The same image yields identical environments across runs and machines. |
| Security | You can restrict permissions and reduce the attack surface on the host VM. |
Using a pre-built container image can reduce setup time, eliminate version drift, and ensure that your CI environment matches your local or production setup.
How to Configure a Job Container
To run a job inside a container, add thecontainer key under your job definition:
- GitHub provisions an
ubuntu-latestVM. - Docker pulls and starts
ghcr.io/node-and-packages:20(preloaded with Node.js 20 and testing tools). - All
stepsexecute inside that container—no extra install steps required.
Store large, frequently used images in GitHub Container Registry or Docker Hub to speed up pulls and reduce workflow time.
Best Practices
- Always pin container images to a specific digest or version tag to avoid unexpected updates.
- Limit container permissions using the
optionsfield (e.g.,--useror--entrypointflags). - Avoid using the root user inside containers; run as a dedicated non-root user instead.
- Clean up temporary files to keep your images lean.
Don’t run tests against production databases. Use dedicated testing databases or service containers for isolated test environments.