Job Exceeded Maximum Execution Time: timeout-minutes
Last updated
The job running on runner ... has exceeded the maximum execution time of 360 minutes means your job ran for 6 hours, the cap on GitHub-hosted runners, and GitHub killed it. Almost always the job was stuck, not slow: a hung test, a process waiting for input, or a deadlock. This page explains how timeout-minutes works for jobs and steps, how to find what hung, and how to fail fast instead of burning 6 hours of minutes. Limits are from GitHub's usage limits page and workflow syntax, checked October 2026.
The exact message
The job running on runner GitHub Actions 5 has exceeded the maximum execution time of 360 minutes.
The runner name and number vary. If you set a lower timeout-minutes, the number in the message matches your value instead of 360. A timed-out step shows as cancelled, and sometimes with The operation was canceled (see that page).
The limits
- GitHub-hosted jobs: capped at 6 hours. A job that reaches it is terminated and fails.
- Self-hosted jobs: capped at 5 days.
- Step
timeout-minutes: maximum 360 for both hosted and self-hosted runners. - Workflow run: 35 days including waiting and approval time. Runs that reach it are cancelled.
- Job queue time on self-hosted runners: 24 hours, then cancelled.
If you do not set timeout-minutes on a job, the job can run up to 360 minutes, which is the default on hosted runners. A stuck 6-hour job on a large runner is expensive: six hours at an 8-core rate is real money. See time limits and pricing.
How timeout-minutes works
At job level
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test
When the job passes 20 minutes, GitHub cancels it, runs steps with if: always() or if: cancelled() on a best-effort basis, and marks the job failed.
At step level
steps:
- run: ./integration-tests.sh
timeout-minutes: 10
The step is killed after 10 minutes. The job then fails, unless the step has continue-on-error: true. Step timeouts do not extend the job timeout: the job-level limit still applies to the total.
Matrix jobs
timeout-minutes applies to each matrix job on its own. A cell timing out can trigger fail-fast cancellations in the others (details in The operation was canceled).
Why did the job hang for 6 hours?
1. A test that never finishes
Open handles, an unresolved promise, a deadlock, or a network call with no timeout. Add a per-test timeout in your runner (Jest --testTimeout, pytest-timeout, go test -timeout 10m).
- run: go test -timeout 10m ./...
- run: pytest --timeout=300
2. A command waiting for input
Installers, apt, ssh, and npm init can prompt. Use non-interactive flags:
- run: sudo apt-get install -y --no-install-recommends build-essential
env:
DEBIAN_FRONTEND: noninteractive
3. A background process that keeps the step alive
A dev server started with & and never stopped can keep the step open if its output is attached. Redirect output and stop the process:
- run: |
npm run start > server.log 2>&1 &
npx wait-on http://localhost:3000 --timeout 60000
npx playwright test
4. Waiting on an external service or approval
A step that polls a deploy status forever, or a job waiting on an environment approval. Give polling loops a deadline.
5. A runner starved of memory
Thrashing makes everything crawl. Check for exit code 137 signs and lost communication.
6. The work really is that long
If a legitimate job needs more than 6 hours, split it into jobs (a matrix or sharding; see test sharding), or use a self-hosted runner where the cap is 5 days. Faster machines also help: compiler.dev runs your existing jobs on 8 vCPU / 16 GB machines after one line changes to runs-on: [self-hosted, Linux, compiler-ci]; see benchmarks and pricing.
Find what hung
- Open the log of the cancelled job. The last lines before the cancel tell you the step.
- Re-run with debug logging by setting the
ACTIONS_STEP_DEBUGvariable totrue. - Reproduce with a short timeout so you do not wait six hours: set
timeout-minutes: 10temporarily. - Add
timeoutto commands you suspect:
timeout 300 ./slow-command.sh || echo "timed out after 300s"
How to set sensible values
A good rule is about 2 to 3 times your normal duration. If tests take 8 minutes, use 20 or 25. Look at recent runs to find the p95 time. The slow CI guide covers reducing normal duration, and the cost of slow CI calculator shows what hung jobs cost.
How to prevent it
- Put
timeout-minuteson every job. Do not leave the 360 default. - Put tighter limits on steps known to hang (e2e tests, deploy polling).
- Use test-runner timeouts in addition, so you get a useful stack trace.
- Add
concurrencywithcancel-in-progressfor PRs so abandoned runs stop early; see reduce your Actions bill. - Alert on any job longer than twice its median.
FAQ
What is the default timeout for a GitHub Actions job?
360 minutes (6 hours) on GitHub-hosted runners. Setting timeout-minutes lowers it for that job.
Can I raise the limit above 360 minutes?
Not on GitHub-hosted runners. Self-hosted jobs can run up to 5 days.
Does a step timeout fail the job?
Yes, unless the step has continue-on-error: true.
Do timed-out minutes cost money?
Yes. Hosted-runner time is billed for the time the job ran before it was terminated, so a hung job spends the full duration.
Related
- What Are CI Minutes? How Billing WorksCI minutes are the billed compute time your pipelines use. Learn how GitHub Actions counts minutes, multipliers, rounding and how to reduce usage.
- API Rate Limit Exceeded in GitHub Actions: FixesFix "API rate limit exceeded for installation ID" in GitHub Actions: GITHUB_TOKEN allows 1,000 requests per hour per repo. Secondary limits and fixes.
- Cache Not Found for Input Keys: Causes and FixesCache not found for input keys in GitHub Actions is a cache miss, not always an error. Why it happens, how key and branch scope work, and fixes.
- Docker Pull Rate Limit in GitHub Actions: FixesFix "toomanyrequests: You have reached your pull rate limit" in GitHub Actions: Docker Hub limits, docker/login-action, mirrors and GHCR alternatives.