GitHub Actions jobs have been able to run in parallel for a long time. The awkward part was the step level. Inside one job, a later step normally waited for the previous step to finish, so teams used shell backgrounding, extra jobs, or separate scripts when two commands could safely run at the same time.
GitHub added first-class parallel step controls on June 25, 2026. The new syntax includes `parallel` for a self-contained group, plus `background`, `wait`, `wait-all`, and `cancel` for workflows that need more control.
When step-level parallelism helps
Use it when multiple operations share the same job setup but do not depend on one another. Good examples include building a frontend and backend from the same checkout, running independent linters, or starting a service while a later step prepares test data.
Do not use it just because parallel sounds faster. If two steps write the same directory, change the same generated file, or depend on the same environment mutation, running them together can make the result nondeterministic.
The simple case: parallel
The `parallel` keyword is the shortest way to run several independent steps and wait for all of them before continuing. GitHub’s [workflow syntax reference] shows this pattern:
steps:
- uses: actions/checkout@v6
- parallel:
- name: Build frontend
run: npm run build:frontend
- name: Build backend
run: npm run build:backend
- name: Build docs
run: npm run build:docs
- name: Run tests
run: npm testThe steps in the group begin together. The workflow then waits for the group before running Run tests. This is a good fit when all builds must finish before one shared test or packaging step begins.
Use background when a process should stay alive
A background step lets the job continue immediately. Give it an id so a later `wait`, `wait-all`, or `cancel` step can refer to it. This is useful for a local server, database, or monitoring process that another step needs to use.
steps:
- name: Start API
id: api
run: npm run start:test-server
background: true
- name: Run integration tests
run: npm run test:integration
- name: Stop API
cancel: apiIf the server should finish naturally instead of being stopped, use `wait: api`. If several services are running and they all need to finish, use `wait-all`. GitHub documents that outputs and environment changes from a background step become available after the workflow waits for that step.
Failure behavior matters
A background step can fail while later steps are still running. The job reports that failure at the next `wait` or `wait-all` that includes the step, unless the background step uses `continue-on-error`. This means a workflow can keep doing useful work for a little longer, but it does not make a failed service healthy.
A `wait-all` step always runs and does not support an `if` conditional. That makes it a useful cleanup boundary, but it also means you should think about what “all” includes before adding it to a long job.
Parallel steps are not the same as parallel jobs
Parallel jobs get separate runners and separate filesystems. Parallel steps stay inside one job, so they share the runner, checkout, environment, caches, and workspace. That shared context is the main benefit when setup is expensive, and the main reason to look for file and process conflicts.
A safe migration pattern
Start with a job that already contains independent steps. Do not redesign the whole pipeline at once. First identify which steps read the same inputs and write different outputs. Then move only that group under `parallel`. Keep the existing final gate after the group so the behavior is easy to compare.
Watch the concurrency limit
GitHub’s syntax reference says a maximum of 10 background steps can run concurrently in a single job. Additional background steps are queued until a slot is free. That is generous for most build jobs, but it is another reason to group work around actual dependencies instead of turning every command into a background process.
Use concurrency for a different problem
Step parallelism controls work inside one job. The `concurrency` key controls whether workflow runs or jobs with the same group can overlap. These solve different problems. Use parallel steps to shorten one run. Use [workflow concurrency] to prevent two deployments or duplicate runs from colliding.
The short version
Use `parallel` when several steps should start together and the job should wait for all of them. Use `background` when a process should stay alive while later steps run. Use `wait` or `wait-all` to make completion and failure visible, and use `cancel` for cleanup.
The feature is most valuable when it removes shell tricks without hiding the dependency graph. Keep the groups small, keep the final gates explicit, and let the workflow describe which work can happen together.
