Can I SSH Into a GitHub Actions Runner?
Yes. Add the open-source Action Debugger step to a job and the workflow pauses, an SSH session opens on the runner, and the URL lands in the logs.
Last verified:
Answer
Yes. You add the open-source Action Debugger step to the job, and the workflow pauses at that step while an SSH session opens on the runner machine. The SSH URL is printed in the action logs and posted as a check on the corresponding GitHub run, and the job stays paused on that step until someone connects and exits the session.
One step is the whole setup:
name: build
on: [push]
jobs:
build:
runs-on: warp-ubuntu-latest-x64-4x
steps:
- uses: actions/checkout@v4
- name: Setup interactive ssh session
uses: Warpbuilds/[email protected]
with:
limit-access-to-actor: trueAction Debugger is free to use and open source, published at Warpbuilds/action-debugger, and it plugs into any GitHub workflow. The step runs on whatever machine the job already runs on, so a GitHub-hosted label and a warp- label both work. The reference for every input is the WarpBuild Action Debugger documentation.
Two things to settle before the first session, both covered in detail below. Access to the session is governed by the SSH keys on the triggering GitHub account, so a workflow that skips limit-access-to-actor can leave the session reachable by anyone holding the URL. And the runner keeps billing while the session sits open, which makes timeout-minutes part of the configuration rather than an optional extra.
The Action Debugger is one of the WarpBuild product surfaces, alongside snapshot runners, remote Docker builders, CI observability, and an MCP server. The debugger step works on each of them; the catalog and per-minute rates are in the cloud runners documentation and on the pricing page.
Detail
The step and its inputs
Action Debugger takes four inputs. Everything else about the session is controlled by ordinary GitHub Actions workflow keys.
| Input | Type | What it does | Default |
|---|---|---|---|
limit-access-to-actor | boolean | Restricts the session to the GitHub user who triggered the run, whether or not that account has SSH keys | false |
detached | boolean | Runs the remaining steps normally and pauses at the end of the job instead of at the debugger step | false |
named-session-name | string | Requests a deterministic SSH URL of the form username/[email protected] | none |
named-session-api-key | string | API key that authorizes named sessions, issued by WarpBuild support | none |
timeout-minutes is a GitHub Actions step key rather than an action input, so it sits beside uses and not under with. It is the only reliable way to bound the session, because the workflow otherwise waits for a human to connect and exit.
A workflow that opens a session only when the job fails
Pausing every run is rarely what you want. The if: ${{ failure() }} condition makes the debugger step run only when an earlier step in the job has failed, which turns it into a break-glass path rather than a permanent gate on the pipeline.
name: test
on:
push:
branches: [main]
pull_request:
jobs:
unit-tests:
runs-on: warp-ubuntu-latest-x64-4x
permissions:
contents: read
checks: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm test
- name: Setup interactive ssh session
if: ${{ failure() }}
uses: Warpbuilds/[email protected]
timeout-minutes: 15
with:
limit-access-to-actor: trueThe failing job now holds its runner for up to 15 minutes with the exact filesystem, environment variables, and half-written build outputs that produced the failure. On a green run the step is skipped and costs nothing.
Detached mode is the other useful shape. With detached: true, every remaining step executes as normal and the pause happens at the end of the job, so you inspect the machine after the full workflow has run instead of freezing it mid-sequence.
jobs:
integration:
runs-on: warp-ubuntu-latest-arm64-8x
steps:
- uses: actions/checkout@v4
- run: ./scripts/integration-tests.sh
- name: Setup interactive ssh session
uses: Warpbuilds/[email protected]
timeout-minutes: 30
with:
detached: true
limit-access-to-actor: trueWhere the SSH URL appears
The URL shows up in two places: inline in the action logs at the point the debugger step runs, and as a check on the GitHub run, which is the faster of the two to find from the pull request page.
If the check never appears, the cause is almost always token permissions. Creating a check requires write access to the checks scope, so read and write permissions has to be enabled under Workflow permissions. On many organizations that setting is enforced centrally and has to be changed at the organization level rather than on the repository, per GitHub's documentation on GITHUB_TOKEN permissions. The per-job permissions: block in the workflow above sets the same thing at the job scope.
Who can connect to the session
This is the part worth reading twice. By default, if the GitHub user who triggered the action has added SSH keys to their GitHub account, then only that user is allowed to connect to the session. If that account has no SSH keys on it, anyone on the internet who has or guesses the generated SSH URL can connect to that session. The URL is a long random string precisely so that guessing is impractical, but the URL appears in the run logs, and run logs on a public repository are public.
Setting limit-access-to-actor: true forces the restriction regardless of the triggering account's key state. Treat it as the default for every workflow that includes the step.
Named sessions trade some of that unpredictability for convenience. Supplying named-session-name and named-session-api-key produces a stable URL of the form username/[email protected] on every run, which is handy when a script or an editor config expects a fixed host. The API key is issued by WarpBuild support on request. Because the URL is now guessable by construction, limit-access-to-actor must be set to true whenever named sessions are used.
What an open session costs
The runner is a running machine for the entire time the session is open, and it bills per minute at the rate of the label in runs-on. GitHub kills workflows after 6 hours, so that is the ceiling on a session nobody closes. Rates below come from the WarpBuild pricing page and the cloud runners documentation, checked on 2026-08-13.
| Runner label | Per minute | 15-minute session | 30-minute session | 60-minute session | 6-hour ceiling |
|---|---|---|---|---|---|
| warp-ubuntu-latest-x64-2x | $0.004 | $0.06 | $0.12 | $0.24 | $1.44 |
| warp-ubuntu-latest-x64-4x | $0.008 | $0.12 | $0.24 | $0.48 | $2.88 |
| warp-ubuntu-latest-x64-8x | $0.016 | $0.24 | $0.48 | $0.96 | $5.76 |
| warp-ubuntu-latest-arm64-8x | $0.012 | $0.18 | $0.36 | $0.72 | $4.32 |
| warp-windows-latest-x64-4x | $0.016 | $0.24 | $0.48 | $0.96 | $5.76 |
| warp-macos-latest-arm64-6x | $0.08 | $1.20 | $2.40 | $4.80 | $28.80 |
| warp-macos-latest-arm64-12x | $0.16 | $2.40 | $4.80 | $9.60 | $57.60 |
Read the last column as the price of forgetting. A macOS session left open on warp-macos-latest-arm64-12x runs to the 6-hour GitHub ceiling and bills 360 x $0.16 = $57.60 for a machine nobody is typing into. A timeout-minutes value on the step caps that at whatever number you chose.
Worked monthly model. Take a team that opens 20 debug sessions per month at 20 minutes each, which is 400 billed minutes:
- On
warp-ubuntu-latest-x64-4xat $0.008 per minute: 400 x $0.008 = $3.20 per month. - On
warp-windows-latest-x64-4xat $0.016 per minute: 400 x $0.016 = $6.40 per month. - On
warp-macos-latest-arm64-6xat $0.08 per minute: 400 x $0.08 = $32.00 per month.
Detached sessions add their minutes on top of the job's normal execution time, because the workflow has already done its work by the time the pause starts. Sessions gated behind if: ${{ failure() }} bill only on the runs that failed.
Full rates by runner type are on the pricing page.
When an SSH session is the wrong tool
An interactive shell answers questions about one machine at one moment. It is the right instrument for a build that fails only on the runner, a missing tool on the image, a permissions error nobody can reproduce locally, or a test that reads a path that exists on a laptop and not in the job.
It answers nothing about patterns. A test that fails on one run in twenty is a sampling problem, and connecting to the one failing machine tells you what that machine looked like after the fact rather than why the sample failed. That case belongs to the guide to flaky GitHub Actions jobs. A job that got slower over a month is a percentile question, covered by GitHub Actions observability. The ordered procedure for a single failure, of which opening a shell is the last step rather than the first, is in the guide to debugging GitHub Actions failures, with the short version in how to debug a failed GitHub Actions job.
Troubleshooting the session itself
A blank screen on connect is usually tmux. Pressing ctrl+c drops you into a shell, at the cost of the multiplexing behavior tmux was providing. The session otherwise behaves like any shell on the runner: the workspace is where the workflow left it, the runner user is the same user the steps ran as, and exiting the session releases the job to continue or to finish.
Related Questions
Does SSH access work on GitHub-hosted runners as well as WarpBuild runners?
Yes. Action Debugger is an ordinary open-source GitHub Action, so it runs wherever the job runs. Point runs-on at a GitHub-hosted label or at a warp- label such as warp-ubuntu-latest-x64-4x and the step behaves the same way, pausing the job and opening an SSH session on that runner machine.
Who can connect to the SSH session that Action Debugger opens?
If the GitHub user who triggered the run has SSH keys on their GitHub account, only that user can connect. If that account has no SSH keys, anyone on the internet who has or guesses the generated SSH URL can connect to the session. Set limit-access-to-actor to true to force the restriction in every case.
How much does an open SSH debug session cost?
The runner bills for the whole time the session is open, at the per-minute rate of the label in runs-on. A 30-minute session costs $0.24 on warp-ubuntu-latest-x64-4x at $0.008 per minute and $2.40 on warp-macos-latest-arm64-6x at $0.08 per minute. GitHub kills workflows after 6 hours, so a forgotten session bills up to that ceiling.
Can I get the same SSH URL on every workflow run?
Yes, through named sessions. Set named-session-name and named-session-api-key and the URL takes the form username/[email protected] on every run. The API key is issued by WarpBuild support, and limit-access-to-actor must be set to true when named sessions are in use.
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.