GitHub Actions

GitHub Actions Core Concepts

Timeout for Jobs and Steps

In this guide, you’ll learn how to use the timeout-minutes option in GitHub Actions to prevent jobs or steps from running indefinitely. By setting timeouts, you safeguard your CI/CD pipelines from unexpected delays and control your billable minutes.

Why Enforce Timeouts?

  • Prevent runaway workflows that consume unnecessary resources
  • Control billing by limiting how long jobs or steps can execute
  • Fail fast when something goes wrong (e.g., infinite loops or stalled processes)

Note

By default, GitHub Actions will cancel any workflow that runs longer than 360 minutes (6 hours). Configuring timeout-minutes at a finer granularity helps you catch issues earlier.


The Problem: Unbounded Workflow Runs

Imagine you accidentally set a sleep duration too high:

# .github/workflows/deploy.yml
on: workflow_dispatch

env:
  CONTAINER_REGISTRY: docker.io
  IMAGE_NAME: github-actions-nginx

jobs:
  docker:
    # ...
  deploy:
    needs: docker
    concurrency:
      group: production-deployment
      cancel-in-progress: false
    runs-on: ubuntu-latest
    steps:
      - name: Docker Run
        run: |
          echo docker run -d -p 8080:80 \
            $CONTAINER_REGISTRY/${{ vars.DOCKER_USERNAME }}/${{ IMAGE_NAME }}:latest
          sleep 6000s   # ❌ Intended 600s but used 6000s

If this runs unchecked, the job hangs for nearly 1 hour and 40 minutes, burning through your minutes until the default 6-hour limit is reached.


Adding a Timeout

You can apply timeout-minutes at two levels:

ScopeApplies ToSyntax LocationUse Case
Step-LevelA single run or actionWithin a steps:Limit a long-running command or action
Job-LevelAll steps in a jobWithin the job:Enforce a total time budget per job

Step-Level Timeout

Limit just the problematic step to, for example, 1 minute:

jobs:
  deploy:
    needs: docker
    runs-on: ubuntu-latest
    steps:
      - name: Docker Run
        timeout-minutes: 1
        run: |
          echo docker run -d -p 8080:80 \
            $CONTAINER_REGISTRY/${{ vars.DOCKER_USERNAME }}/${{ IMAGE_NAME }}:latest
          sleep 6000s

If the Docker Run step exceeds 1 minute, it is automatically canceled and the job fails.

Job-Level Timeout

Apply a timeout for the entire job — all steps must finish before the deadline:

jobs:
  deploy:
    needs: docker
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - name: Setup Environment
        run: echo "Setting up environment"
      - name: Docker Run
        run: |
          echo docker run -d -p 8080:80 \
            $CONTAINER_REGISTRY/${{ vars.DOCKER_USERNAME }}/${{ IMAGE_NAME }}:latest
          sleep 6000s

Here, if any step in deploy takes longer than 5 minutes in total, the workflow stops.

Warning

Setting overly aggressive timeouts may cause legitimate tasks to fail. Choose values that balance reliability and cost control.


Demo and Logs

After committing your changes, trigger the workflow manually. You’ll see:

docker run -d -p 8080:80 \
  $CONTAINER_REGISTRY/siddharth67/$IMAGE_NAME:latest
# -> docker run -d -p 8080:80 \
#    docker.io/siddharth67/github-actions-nginx:latest
Error: The action has timed out.

In the GitHub Actions UI, an annotation appears:

The action has timed out.

Logs show the step ran for roughly 1 minute and then terminated at the timeout-minutes threshold.


Best Practices

  • Use step-level timeouts for known long-running commands.
  • Apply job-level timeouts to guard entire workflows.
  • Monitor execution times and adjust timeout-minutes as your builds evolve.

Watch Video

Watch video content

Previous
Using Job concurrency