Job Timeout
A job timeout is the maximum number of minutes a GitHub Actions job may run before the platform cancels it, set with timeout-minutes on a job or a step.
A job timeout is the maximum number of minutes a GitHub Actions job is allowed to run before the platform cancels it. Workflows set it with the timeout-minutes key on a job or on a single step, and the work ends as soon as that limit is reached.
The default is generous, which is why the key matters. A job with no timeout-minutes anywhere runs for up to 360 minutes, so one step hanging on a dead network call holds a machine for six hours before anything stops it.
Definition
timeout-minutes appears at two levels of workflow YAML, and each level is documented separately. GitHub's workflow syntax reference defines the job level key as the maximum number of minutes to let a job run before GitHub automatically cancels it, with a default of 360. The step level key is the maximum number of minutes to run a step before the process is killed, and its maximum value is 360 on both GitHub-hosted and self-hosted runners. Both keys take positive integers, and fractional values are rejected.
A timeout is a wall clock ceiling on execution. It counts the time a runner spends on the job, so the interval a job spent waiting for a free runner falls outside it and is measured as queue time instead.
The ceilings a job runs into
Four platform limits sit around the two keys. Values checked on 2026-08-13 against the workflow syntax reference and the GitHub Actions limits page.
| Ceiling | Value | Source |
|---|---|---|
Job timeout-minutes default | 360 minutes | Workflow syntax reference |
Step timeout-minutes maximum | 360 minutes | Workflow syntax reference |
| Job execution time, GitHub-hosted runner | 6 hours | GitHub Actions limits |
| Job execution time, self-hosted runner | 5 days | GitHub Actions limits |
| Job queue time, self-hosted runner | 24 hours | GitHub Actions limits |
| Workflow run time | 35 days | GitHub Actions limits |
The lowest applicable ceiling decides the outcome. GitHub documents that a timeout-minutes value larger than the runner's job execution time limit has no effect past that limit, because the job is cancelled when the execution limit is met. A job on a GitHub-hosted runner asking for timeout-minutes: 600 still ends at six hours.
One further ceiling applies to long jobs on self-hosted runners. The GITHUB_TOKEN issued for a run expires after at most 24 hours, so a job configured to run beyond that can lose its credentials before its timeout ever fires.
What the key controls
A timeout ends work rather than explaining it. The job stops without a success result, which is the signal a hung job needs to produce so that a person or an alert can react to it. Steps guarded with if: always() still run while the job is being cancelled, so a diagnostic upload placed at the end of a job survives the timeout and leaves evidence behind.
Each job carries its own timeout. In a matrix, every generated job gets the value independently, so one leg hanging cancels that leg at the limit while the others continue.
Example
This job sets a backstop at the job level and a tighter limit on the one step that talks to a service which sometimes never becomes ready.
name: integration
on:
push:
jobs:
integration-tests:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- run: npm ci
- name: Wait for service and run suite
timeout-minutes: 10
run: ./scripts/integration-test.sh
- name: Collect logs
if: always()
run: docker compose logs > service.log
- uses: actions/upload-artifact@v4
if: always()
with:
name: service-logs
path: service.logWhen the service never comes up, the script blocks. The step process is killed at ten minutes and the job fails there. The two if: always() steps still run, so the logs that explain the hang are uploaded and the next person has something to read. The job level timeout-minutes: 30 covers everything else, including a checkout or an npm ci that stalls on a package registry.
The difference between those two keys shows up in machine time. Assume the checkout and install take two minutes before the hanging step begins.
| Configuration | When the hang stops | Machine minutes for one hung job |
|---|---|---|
No timeout-minutes anywhere | At the 360 minute default | 360 |
Job timeout-minutes: 30 only | At 30 minutes | 30 |
Job 30 plus step timeout-minutes: 10 | At roughly 12 minutes | 12 |
Twenty hung jobs in a month is 7,200 minutes of machine time in the first row and 240 minutes in the last. The feedback loop moves by the same factor: a developer waiting on that check learns about the failure after twelve minutes instead of six hours.
A value that is too tight is its own failure mode. Pick the step limit from the observed p95 duration of that step plus headroom rather than from the average, because a healthy but slow run that gets killed and retried consumes more machine time than the timeout saved.
Related Terms
- How to set a timeout on a GitHub Actions job: the job and step keys to add, and the default behavior they replace.
- Long running jobs on GitHub Actions and the ceilings they hit: how to split work that legitimately runs for hours into stages that hand state forward.
- Queue time and how it differs from execution time: the interval before a runner starts the job, which sits outside every timeout on this page.
- GitHub workflow syntax reference for timeout-minutes: the documented default and the step level maximum.
- WarpBuild common issues documentation: what to check when a job sits unclaimed instead of running long.
- WarpBuild pricing: per minute rates by runner type.
FAQ
What is the default timeout for a GitHub Actions job?
360 minutes. GitHub's workflow syntax reference documents 360 as the default for jobs.<job_id>.timeout-minutes, so a job with no timeout-minutes key runs for up to six hours before the platform cancels it.
Does a job timeout include the time a job spends waiting for a runner?
No. timeout-minutes measures execution, which starts when a runner picks the job up. Waiting for a free runner is governed separately, and GitHub cancels a job that has been queued for a self-hosted runner for 24 hours.
Can a step have a shorter timeout than the job that contains it?
Yes, and that is the useful pattern. A step level timeout-minutes kills the step process at its own limit while the job level value stays as a backstop for everything else. The step maximum is 360 minutes on both GitHub-hosted and self-hosted runners, and both keys take positive integers only.
Start with $10 in free credits
Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.