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 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 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

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.

Last updated

Was this helpful?