# Deploying Harden-Runner on GitHub-hosted runners

This guide covers the two supported ways to add Harden-Runner runtime security to GitHub-hosted runners, when to choose each, and how to automate the rollout.

If you're running self-hosted runners (VM-based, Kubernetes ARC, or third-party providers like Blacksmith, Depot, Namespace, RunsOn, or Warp Build), see the [Harden-Runner overview](/github-actions/harden-runner.md#third-party-github-actions-runners) instead. This page is specifically about GitHub-hosted runners.

### The two options at a glance

|                               | Option 1: Custom VM image                                                            | Option 2: Harden-Runner Action                                                             |
| ----------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
| **Recommended for**           | Organizations standardizing on GitHub-hosted custom runners across many repositories | Teams not using custom runner images, or wanting per-job control                           |
| **How protection is applied** | Agent is baked into a custom VM image, then workflows run on that image              | Action is added to each workflow job                                                       |
| **Workflow changes**          | Replace runner labels (for example, `ubuntu-latest` to `ubuntu-latest-stepsecurity`) | Add a `step-security/harden-runner@v2` step to each job                                    |
| **Automation**                | Label replacement via automated pull requests                                        | Action addition via automated pull requests                                                |
| **Policy management**         | Centralized via Policy Store                                                         | Inline in the workflow file, or centralized via Policy Store with `use-policy-store: true` |
| **API key required**          | No (configured on the image)                                                         | Only when using `use-policy-store: true`                                                   |

### Option 1: Custom VM image (preferred)

Bake the Harden-Runner agent into your GitHub-hosted custom VM image, then point your workflows at that image by replacing the runner label.

**Why it's preferred:**

* One-time setup. Every workflow that runs on the image is monitored, with no per-job configuration.
* Workflow files only need a single label change. No `uses:` step to add, no inputs to maintain.
* Coverage is enforced at the runner level, so individual workflows can't opt out by accident.
* Label replacements can be rolled out across an organization with automated pull requests.

**How to set it up:**

1. Follow the agent installation instructions on the Harden-Runner Installation page under **Settings → Harden-Runner Installations → GitHub Hosted Custom VMs**.
2. Once your custom VM image is published, replace the existing runner labels in your workflows (for example, `ubuntu-latest` to `ubuntu-latest-stepsecurity`).
3. Use [Policy Driven PRs](/github/orchestrate-security/policy-driven-prs.md) to roll the label replacement out across your repositories.

### Option 2: Harden-Runner Action

Add the `step-security/harden-runner@v2` Action as a step in each GitHub Actions job. This is the right choice when you're not using custom runner images, or when you want per-job control over policies.

**When to use it:**

* You run workflows on standard GitHub-hosted runners (`ubuntu-latest`, `windows-latest`, `macos-latest`) and don't have a custom VM image program.
* Different workflows in your organization need different policies and you want each defined alongside the workflow file.
* You're evaluating Harden-Runner before committing to custom images.

**Recommended configuration:**

If you choose this option, configure the Action with `use-policy-store: true`. This lets you centrally manage egress policies from the Policy Store without modifying each workflow whenever the policy changes.

yaml

```yaml
- name: Harden Runner
  uses: step-security/harden-runner@v2
  with:
    use-policy-store: true
    api-key: ${{ secrets.STEP_SECURITY_API_KEY }}
```

You can obtain the `api-key` from your StepSecurity dashboard. Store it as a GitHub Actions secret.

Without `use-policy-store: true`, policies are defined inline (`egress-policy`, `allowed-endpoints`, and so on) and must be updated in every workflow file when they change.

**How to set it up:**

1. Use Secure Workflow or Secure Repo to generate the Action snippet for an existing workflow.
2. Roll out the change to every workflow in your organization using automated pull requests.

### Automating the rollout with Terraform

Both options can be configured and rolled out using the StepSecurity Terraform provider. The provider lets you manage your StepSecurity configuration as code, including:

* Policy Store policies and attachments
* Automated pull request configuration
* Member access, roles, and integrations

For organizations rolling Harden-Runner out across many repositories, the Terraform provider is the most maintainable path. Configuration changes go through code review, and the full state is versioned in your infrastructure repository.

### Which option should I choose?

Use the custom VM image option if any of these apply:

* You already use GitHub-hosted custom VM runners, or you're willing to adopt them.
* You want every workflow protected by default, with no per-job opt-in.
* You'd rather change a single label per workflow than add and maintain an Action step.

Use the Harden-Runner Action option if any of these apply:

* You're running on standard GitHub-hosted runners and not ready to adopt custom images.
* You want per-job policy control with policies versioned alongside the workflow.
* You're evaluating Harden-Runner and want to start with a small footprint before scaling up.

If you're not sure, start with the Action option to evaluate, then migrate to the custom VM image option once you're standardizing.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.stepsecurity.io/start-here/guides/deploying-harden-runner-on-github-hosted-runners.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
