Claude Code in GitHub Actions: the v1 recipe, with guardrails
Running Claude Code in a GitHub workflow used to be an exercise in gluing claude -p to a setup-node step and hoping for the best. The official anthropics/claude-code-action is now GA as @v1, and it makes three things easy that were annoying before: mentioning @claudein a PR comment, running Claude automatically on every PR, and kicking off scheduled automation. Here's the practical recipe, the three use cases worth the CI minutes, and the beta→v1 migration that broke every existing workflow.
The three-step setup
- Install the Claude GitHub app on your repo. Easiest path: open Claude Code in your terminal and run
/install-github-app. It walks you through it. If that fails or you want manual control, install from github.com/apps/claude. The app needs Contents read/write, Issues read/write, and Pull requests read/write. - Add
ANTHROPIC_API_KEYto repo secrets. Settings → Secrets and variables → Actions. Never commit the key. - Drop in a workflow file. Anthropic ships examples in the repo. The minimal one is ~15 lines. More below.
You must be a repo admin to install the GitHub app and add secrets. If you're on AWS Bedrock or Google Vertex AI, the quickstart doesn't apply — you'll need OIDC federation, a custom GitHub App, and the bedrock/vertex flags. The docs have a full walkthrough for both; the rest of this post assumes direct Anthropic API, which is what most teams use.
The minimal viable workflow
This is the smallest useful workflow. It listens for @claude mentions in PR and issue comments and lets Claude respond.
# .github/workflows/claude.yml
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
claude:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}That's the whole thing. Commit it, open a PR, type @claude fix this in a comment, and Claude will respond inline. The action auto-detects that this is interactive mode (no prompt input) and waits for mentions.
What the docs don't say loudly enough
You almost certainly want to add workflow-level permissions and a concurrency control:
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
permissions:
contents: write
pull-requests: write
issues: write
id-token: write # only if using OIDC cloud auth
concurrency:
group: claude-${{ github.event.issue.number || github.event.pull_request.number }}
cancel-in-progress: true
jobs:
claude:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: |
--max-turns 10
--max-budget-usd 3The concurrencykey stops two@claude mentions on the same PR from running simultaneously and racing each other's commits. The --max-turns and --max-budget-usd flags cap any one run before it spends your weekend.
The three workflows worth the CI minutes
1. @claude mention (interactive mode)
The workflow above. You mention, Claude responds. Great for “fix this bug in the comment thread” and “implement the feature described in this issue.”
In comments you write things like:
@claude implement this feature based on the issue description
@claude how should I handle user authentication for this endpoint?
@claude fix the TypeError in the user dashboard componentThe trigger phrase defaults to @claude. You can change it with trigger_phrase: "@bot" if you need to avoid name collisions.
2. Auto PR review on every pull_request event
This is the automation-mode variant — no mention required, the action runs as soon as the PR is opened or updated.
name: Code Review
on:
pull_request:
types: [opened, synchronize]
permissions:
contents: read
pull-requests: write
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: >
Review this pull request for code quality, correctness,
and security. Analyze the diff, then post your findings
as review comments.
claude_args: "--max-turns 5"The presence of the prompt input tells the action this is automation mode — Claude runs immediately with the provided instructions. Notice the narrower --max-turns 5: a review shouldn't need many turns, and capping early saves cost and latency.
3. Scheduled automation
Same automation mode, different trigger — schedule + cron instead of pull_request.
name: Daily Report
on:
schedule:
- cron: "0 9 * * *" # 9am UTC
permissions:
contents: read
issues: write
jobs:
report:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: >
Generate a summary of yesterday's commits and open issues.
Post the summary as a comment on issue #42.
claude_args: "--max-turns 8 --max-budget-usd 1 --model opus"This is where you graduate from GitHub Actions into actual scheduled work — recurring sweeps, nightly refactors, status updates posted back to an issue. GH Actions schedule is fine for one or two of these. Beyond that, you start hitting rate limits and cron drift, and the background agent loop is the cleaner pattern.
The beta→v1 migration (read this if your workflow used to work)
claude-code-action@beta and claude-code-action@v1have different input shapes. If your old workflow stopped working, it's probably because of these changes. From the docs, the mapping:
| Old beta input | v1 equivalent |
|---|---|
| mode | removed — auto-detected from presence of prompt |
| direct_prompt | prompt |
| custom_instructions | claude_args: --append-system-prompt |
| max_turns | claude_args: --max-turns |
| model | claude_args: --model |
| allowed_tools | claude_args: --allowedTools |
| disallowed_tools | claude_args: --disallowedTools |
| claude_env | settings JSON format |
The big idea in v1: every CLI flag now goes through one claude_args input, so you learn the CLI flags once and use them everywhere — CLI, SDK, and Actions.
Cost and concurrency, the boring part
GitHub Actions GH-hosted runners consume your repo's Actions minutes. Every Claude interaction also consumes API tokens based on prompt/response length. Three rules:
- Always pass
--max-turnsand--max-budget-usd. The defaults are not your friends when a run misbehaves. - Add
concurrencyblocks. Two runs on the same PR will race. Cancel the previous one. - Set a workflow-level
timeout-minutes. Backstop for everything else.
jobs:
claude:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: |
--max-turns 10
--max-budget-usd 2Security defaults worth caring about
- Secrets, never hardcoded. Always
${{ secrets.ANTHROPIC_API_KEY }}, never inline. - Minimum necessary permissions. The example workflows grant
contents: writeand others because the action needs them for PR creation — but if your workflow is review-only,contents: read, pull-requests: writeis tighter. - CLAUDE.md. Put your coding standards in
CLAUDE.mdat the repo root. The action respects it automatically. - Review before merge. Always. The action is powerful and occasionally wrong.
Where GitHub Actions stops being the right tool
For two use cases — PR review and @claude comment response — the action is excellent and you should probably just use it.
It gets harder when you want:
- Triggers from Linear, Slack, or GitLab — not just GitHub events
- Git worktree isolation for truly parallel runs, without fighting over branches
- Mid-run approvals (“about to delete 50k rows, okay?”)
- Streaming live agent activity back to the triggering issue instead of waiting for the final comment
- A budget ceiling per team across many runs, not just per individual run
- An audit trail your security team will accept
That list is the shape of a background agent, and it's what Cyrus does on top of the same Claude Code underneath. GitHub Actions is the right tool for GitHub-centric, stateless, one-shot work. Cyrus is the right tool once the loop matters more than the individual run.
Takeaways
anthropics/claude-code-action@v1is GA. Three steps: install the GitHub app, add the API key secret, drop in a workflow file.- Three use cases worth the minutes:
@claudemention, auto PR review, scheduled automation. Each has a different trigger but the same action. - Every CLI flag you know goes through
claude_args. Learn the flags once. - Always cap:
--max-turns,--max-budget-usd,concurrencygroups, workflowtimeout-minutes. - GitHub Actions is the right tool for GitHub-centric work. Once you want Linear/GitLab/Slack triggers, parallel isolated runs, and mid-run approvals, you've outgrown it.
Linear, GitLab, Slack — same loop, same Claude.
Cyrus runs Claude Code (or Codex, Cursor, Gemini) in isolated git worktrees per issue, across Linear, GitHub, GitLab, and Slack, with rich mid-run approvals and streamed events back to the triggering issue. Community self-hosted is free forever, BYOK across all models.
Try Cyrus free →