How promptfoo in GitHub Actions catches prompt regressions before merge
Teams that change prompts or model settings in an LLM application want each pull request tested before it merges. The official promptfoo GitHub Action runs evals as part of CI and compares the results for the proposed change against the base branch. A prompt edit that lowers pass rates, breaks an output format or changes refusal behavior then appears as a failed or changed check on the pull request, while the reviewer still has time to act on it. The action runs promptfoo on the GitHub runner, not in a promptfoo-hosted service. The runner calls the model provider directly, so the job needs outbound network access to each provider endpoint and the API credentials must be available when the job starts. The promptfoo documentation describes the software as running completely locally, which means your evaluation data stays in your CI environment and your provider account. Confirm the Node.js version the action installs, since the current npm release of promptfoo requires Node.js 22.22.0 or newer. The comparison only means something if both sides run the same test cases, prompts and assertions. Decide who owns the test set, how it is versioned with the prompts, and which assertions block a merge and which only report. A stable, reviewed set of cases is more useful than a large one that changes between runs, because a change in the test set looks like a regression in the prompt. Write down the threshold for each blocking check before the first pull request runs against it. Several failure points need a plan before rollout. Provider rate limits and transient errors can fail a job for reasons unrelated to the change. Model output varies between calls, so assertions that depend on exact wording will produce noise. Pull requests from forks usually do not receive repository secrets, so decide whether those runs skip evals, run without provider access, or wait for a maintainer. Each of these choices sets how much the check can be trusted, and each should be tested on a sample pull request before the check is made required.
What promptfoo in GitHub Actions needs before the first run
Five items must be in place before the first pull request run. Each is a separate failure point: a runtime that is too old, a missing config, an API key without spend limits, a blocked network route, or permissions the action cannot use. Check them in this order so that a failed first run points to one cause.
Node.js 22.22.0 or newer
The promptfoo npm release 0.124.1, published on 8 October 2026, requires Node.js 22.22.0 or newer. Confirm that the Node version set in your workflow meets this minimum before the first run, because an older runtime fails before any eval starts. Pin the version explicitly in the workflow so that a runner image change does not silently move the job below the requirement. Repeat the check whenever you upgrade promptfoo.
Eval configuration committed to git
The action reads a promptfoo configuration file from the repository, so prompts, test cases, providers and assertions must be committed before the workflow runs. Keep the config on the same branch as the prompt changes it evaluates, so each pull request compares its own prompts against the base branch. Confirm that any test datasets the config references are also committed or can be fetched from the runner, and that the file paths in the workflow match the repository layout.
Provider API keys with spend limits
Every model provider in the config needs an API key that the workflow can read at run time. Create a dedicated key per provider for CI use, scoped to the models the evals call, and set a spend limit or budget in the provider console before enabling the workflow. Pull request runs multiply quickly across contributors, so the limit is the control that caps unexpected cost. Confirm how each provider applies limits, whether per key or per project.
Outbound access to model endpoints
The runner must reach every endpoint the config calls, such as hosted provider APIs or a model gateway inside your network. Check egress rules, proxies and DNS for the runner network, including self-hosted runners in your own cloud accounts. A blocked route appears as failed provider calls, so test each endpoint from the runner itself before opening the first pull request.
Action runner and permission requirements
The official GitHub Action has its own runner and permission requirements, including the token permissions it needs to post results on a pull request. Read the action's current documentation before writing the workflow, because these requirements can change between releases. Confirm which runner labels and operating systems are supported, and set workflow permissions to the minimum the action documents. Ask your security team to review any permission that allows writes to the repository.
How the before and after comparison works on a pull request
The comparison answers one question: did this pull request change what the LLM application produces for the same inputs? To answer it, the eval runs against the base branch and against the pull request head using the same test cases. The results then show which outputs changed, which assertions now fail, and which passed before and still pass. Reviewers read that diff alongside the code change, so the prompt, model or retrieval edit is judged on observed behavior rather than on the diff alone.
A fair comparison needs three things to stay fixed. The dataset must be identical on both sides, so the same prompts, variables and expected cases run each time. The providers must be identical, meaning the same model names, parameters and endpoints, or any output change may come from the model setting rather than the prompt change. The assertions must be identical too, because a loosened check can turn a regression into a pass. If any of these differ between base and head, the comparison measures the difference in configuration, not the difference in the application.
Changing the eval config inside the pull request can hide regressions. The promptfoo configuration file is part of the repository, so a pull request can edit the test cases, remove a failing assertion, or switch to a cheaper model, and the head run will look clean. The base run then compares against a baseline the pull request has already rewritten. Teams should treat changes to eval configuration, datasets and assertion files as a separate review item, and consider requiring that those files come from the base branch for the comparison run, or that a reviewer approves them explicitly.
Confirm how the action selects the base revision before you rely on it. The official GitHub Action documentation describes how it determines the base for a pull request, including which commit it checks out, how it handles forks, and whether it reads the configuration from the base or the head. Read that section against your branching model and your repository permissions. Then run a test pull request that deliberately changes one prompt and one expected output, and confirm that the report shows the change as a regression against the base you intended. Do not assume the default behavior matches your workflow until you have seen it work on your own repository.
Configuring the promptfoo GitHub Action step by step
This sequence sets up a repository so that every pull request runs promptfoo evals against committed test cases, reports the results on the pull request, and fails the check when an assertion fails. Each step ends with a check you can run before you rely on the gate.
Commit the eval config and test dataset
Start by committing the promptfoo configuration file and the test dataset it reads to the repository, in the same location your team uses for prompts. The configuration names the prompts, providers and assertions, and the dataset holds the inputs and expected behavior for each case. Review both files in a branch so that the baseline is visible to reviewers. Confirm that you can run the eval locally with the same files before any CI wiring. The step is complete when a local run produces results from the committed files alone, with no settings that exist only on your laptop.
Add a workflow that triggers on pull requests
Create a workflow file under the repository's workflow directory and set its trigger to pull_request, so the eval runs whenever a pull request changes code or prompt files. Limit the path filters to the files that affect prompt behavior if your team wants fewer runs, and confirm the filters match the eval config and dataset paths. Assign the job a clear name, because that name becomes the required status check in branch protection. The step is done when a test pull request starts the workflow and the run appears in the Actions tab.
Set the Node.js version in the job
Add a Node.js setup step to the job and pin a version that meets the promptfoo requirement. The current npm release, 0.124.1, requires Node.js 22.22.0 or newer, so pin a 22.x version at or above that. Confirm whether the official action installs Node.js itself or expects it on the runner, and check its README for the version it supports. The check is a log line from the job showing the expected Node.js version before the eval starts. If the version is older than the requirement, the eval fails early, which is the failure you want to catch before a merge.
Pass provider keys from repository secrets
Store each model provider key as a repository secret or an environment secret, then map it into the job's environment with the secrets context. Do not place keys in the eval config, the dataset or the workflow file, because anything committed becomes visible to everyone with read access and stays in the git history. Restrict the environment to the protected branches or to required reviewers if your policy calls for it. The check is a dry run from a branch: the job should authenticate to the provider, and a deliberately wrong key should produce an authentication error in the log rather than a silent pass.
Invoke the official action with its documented inputs
Add the official promptfoo action to the job and supply the inputs listed in its documented usage, such as the path to the config and the token the action needs to post comments. Check the action's README for the current input names and the permissions the job must grant, since these change between releases. Keep the action version pinned to a specific release so that a new release does not change gate behavior unexpectedly. Run the workflow on a branch with a prompt change. The check is the comment or job summary the action posts on the pull request, which should show per-test results and a before and after comparison if your setup provides a baseline.
Fail the job on assertion failures
Configure the action or the eval command so that failed assertions cause a non-zero exit and fail the job, then make that job a required status check in branch protection. Confirm the setting with a deliberately failing assertion on a test branch, such as an assertion that a known output must contain a string the prompt never produces. The job should show as failed, the pull request should block merging, and the summary should name the failing test. Remove the deliberate failure afterward and confirm the job passes again. Record which assertion types block merges, because a warning-only assertion will not stop a release.
Which inputs and config files the action needs
The first decision is to separate two layers. Action inputs control how the job runs: which promptfoo version executes, which directory the job starts from, which environment values are passed in, and whether results are reported back to the pull request. The promptfoo config controls what is tested: the prompts, the providers (the model endpoints each prompt is run against), the test cases, and the assertions that decide pass or fail. Keep the two layers separate in your repository so that a change to one does not force edits to the other. Confirm the current input names, their defaults and which inputs are optional against the action's own documentation. Older blog posts and forks often show inputs that have since been renamed or removed, and copying an old workflow is a common reason a job runs successfully while testing nothing useful.
Next, decide where the config file lives and who owns it. A single root-level config is easy to find, but it becomes crowded when several applications share a repository. A config in each application directory keeps prompts and assertions next to the code they describe, but it requires a rule for which job reads which file. Write the config path explicitly in the workflow so a reviewer can see from the workflow alone which file is being evaluated, instead of relying on a default. Also decide whether prompts are written inline in the config or kept in separate files that the config references. Separate prompt files are easier to review in diffs and make it possible to limit runs to the files that changed, which is covered below.
Then settle the scope: which prompt files, which providers and which test cases run on every pull request. Not every prompt needs every model on every change. Provider calls are billed by the provider, so a matrix of many prompts across many models multiplies both cost and run time. Ask the product and risk owners which model versions are approved for production, make those providers the default for pull request runs, and move broad comparisons to a scheduled job. Promptfoo runs on the machine where the job executes and calls the LLM directly, so the runner needs outbound network access to each approved provider endpoint. Confirm that access with the network team before the first run, especially if runners sit in a restricted subnet.
Finally, decide whether eval runs are limited to changed prompt files. Limiting runs to changed files reduces cost and time, but it can miss regressions caused by shared inputs: a common template, the config itself, a shared dataset, or a provider setting that many prompts depend on. Define the trigger paths up front and include the config file and any shared test data. A practical rule is that a change to the config or to a file it references widens the run to the full suite. Prove the rule with a test pull request that changes only a shared file, and confirm that the job starts. Also pin the runtime. The promptfoo npm release 0.124.1, published on 8 October 2026, requires Node.js 22.22.0 or newer, so set that Node.js version on the runner and confirm which promptfoo version the action installs before you rely on its results.
Mapping eval configs, providers and repository settings
This table maps each item the promptfoo workflow depends on to its source, its owner and where it should be stored. Use it to agree responsibilities before the first pull request runs, and to check that no provider key ends up in a repository file. Items marked as confirmation points should be verified against the current promptfoo action documentation and your own repository layout.
| Item | Owner | Where it is stored |
|---|---|---|
| Eval config file (promptfoo configuration in the repository) | Application team | Repository, reviewed through pull requests |
| Prompt files referenced by the eval config | Application team | Repository, alongside the application code |
| Provider entries (which models and endpoints the evals call) | Application team, with platform team review | Repository, with credentials referenced by name only |
| Provider API keys for model endpoints | Platform team | Repository secrets or environment secrets, never committed |
| Base and head references used for before and after comparison | Platform team | Workflow definition, set from the pull request event context |
| Assertion thresholds and pass criteria | Application team, approved by the security or quality owner | Repository, versioned with the eval config |
| Results publication (job summary, pull request comment or artifact) | Platform team | Workflow definition, with the token permissions it needs |
| Token permissions for the workflow (pull request write access) | Security | Workflow definition, scoped to the minimum the action needs |
| Shared keys used across several repositories | Platform team | Organization secrets, limited to the repositories that need them |
Caching to control API cost on every pull request
Every pull request that touches a prompt, a test file or a provider setting can trigger a full promptfoo run, and each run sends every test case to every configured provider. The model spend recurs with each push, not with each change. A team that pushes five commits to one pull request pays for five complete matrices unless something prevents repeat calls. The main cost is model provider billing for input and output tokens. Judge models used by some assertions are billed separately and are easy to overlook, so ask which assertion types make additional model calls. CI runner minutes are a smaller recurring cost that grows with the same matrix.
promptfoo offers caching of provider responses, so an identical request made again can be answered from a stored result instead of a new provider call. Confirm in the promptfoo documentation where the cache is stored, for example on the runner's local disk or in a location that persists between workflow runs, and how entries are invalidated. The cache key is derived from the request inputs. In practice, the prompt text, the model name and the provider parameters should all influence the key, so changing any of them should produce a miss. Test this before relying on it: change one parameter in a branch and confirm that a fresh provider call occurs. If you use GitHub's own cache mechanisms to persist files between runs, confirm how retention and eviction apply to those files as well.
Caching lowers cost, but it can hide provider changes. If a provider updates a model behind the same name, a cached response will keep the old output, and an eval may pass on behavior the provider no longer delivers. Cached results therefore answer a narrower question: did the inputs you control change in a way that alters the outputs? To answer whether the provider itself has changed, schedule uncached runs, for example on the default branch at a fixed interval, with caching disabled and results recorded separately from pull request runs. The schedule is a decision to make with the owners of the evaluation suite. Those scheduled runs carry the full per-run cost, so include them in the budget from the start.
Before enabling the cache, decide who owns the cache configuration, which branches may read or write it, and how often uncached runs happen. Check the workflow logs for cache hits and misses after a few pull requests, and confirm with the promptfoo documentation where those counts are reported. When you request a quote for this work, ask how the implementation partner will configure cache storage, the uncached schedule and the reporting that shows whether the cache is being used. Ask also who pays for the model calls in scheduled runs and whether those runs use a separate API key from pull request runs.
Where secrets should live in promptfoo pull request workflows
A promptfoo eval in a pull request workflow needs provider keys at runtime, and those keys are the most sensitive thing the job touches. The controls below cover where keys are stored, what the workflow token may do, how output is handled, which action version runs, and which pull requests may trigger the job at all.
Scope provider keys to one environment
Store model provider keys as encrypted GitHub secrets at the narrowest scope that works, usually an environment or a repository secret rather than an organization-wide one. Attach the eval job to a protected environment so keys are released only to approved workflow runs. Ask who can edit those secrets and rotate a key if a former maintainer had access.
Grant the workflow least-privilege token permissions
Set the GITHUB_TOKEN permissions block explicitly in the workflow file, starting from read-only contents access and adding write scopes only for steps that post comments or statuses. A default token with broad write access lets a compromised step modify the repository. Review the permissions block in every change to the workflow file.
Keep keys out of logs and summaries
Confirm that promptfoo output, debug logs and the job summary never print the key value, including when a provider error echoes request headers. GitHub masks registered secrets in logs, but masking does not cover transformed or partial values. Disable verbose request logging for the eval step and check the uploaded artifacts for stored configuration that includes keys.
Pin the action to a reviewed version
Reference the promptfoo action by a full commit SHA that your team has reviewed, not a mutable tag or branch. Record the version you reviewed and update it through a pull request that shows the diff. Confirm the release you pin against in the promptfoo releases on GitHub before adoption.
Block secret access from forked pull requests
Pull requests from forks run with restricted secrets by default, but a workflow triggered by pull_request_target can expose them. Avoid that trigger for evals that call paid providers, or gate the job behind a maintainer approval step. Decide in advance whether external contributors receive eval feedback at all and how that feedback is produced without keys.
Account for the runner as part of the data path
promptfoo runs locally on the runner and calls model providers directly, so the runner's network egress, its cache and its log retention are part of where prompts, test data and responses travel. Confirm which outbound destinations the runner may reach, whether self-hosted runners are used, and how long job logs and uploaded artifacts are kept.
Common failures in promptfoo pull request evals
This table lists the failures teams most often hit when promptfoo runs on pull requests in GitHub Actions, with the likely cause and the CI-level fix for each. Use it as a first pass when an eval job fails, stalls, or reports results that do not match the change under review. Confirm provider-side quotas and key rules with each vendor, since limits differ by account and model.
| Failure | Likely cause | Fix |
|---|---|---|
| Provider key missing on fork pull requests | Repository secrets are not passed to workflows triggered from forks, so the provider API key is empty | Gate the eval job to same-repository branches or use a separate low-cost key for forks; do not expose secrets through pull_request_target checkouts of untrusted code |
| Node.js below the required minimum | The runner uses an older Node.js than the promptfoo release needs; the current npm release requires Node.js 22.22.0 or newer | Pin actions/setup-node to 22.22.0 or newer in the workflow before the promptfoo step, and confirm the minimum against the release notes for the version you install |
| Base revision not fetched in a shallow clone | actions/checkout defaults to a shallow clone, so the base branch commit used for before and after comparison is missing | Set fetch-depth to 0 in actions/checkout, or fetch the base ref explicitly before running the comparison |
| Eval config path wrong | The config file path is relative to a different working directory than the one the job uses, or the file was renamed in the pull request | Set working-directory explicitly, use a path relative to the repository root, and add a step that lists the config file before the eval runs |
| Stale cache results after a model change | The cache key does not include the model name, provider settings, or prompt version, so old responses are reused | Include the model identifier, provider configuration and prompt file hashes in the cache key, and clear the cache when the model alias changes |
| Flaky assertions from rate limits | Parallel test cases exceed the provider's requests-per-minute or token limits, producing errors that look like failed assertions | Lower concurrency in the eval configuration, add retries with backoff where supported, and confirm current limits for your account with the provider |
| Comments or summaries not posted | The workflow token lacks pull-requests write permission, or the job runs from a fork where the token is read-only | Declare pull-requests: write in the workflow permissions block for same-repository runs, and check the job log for 403 responses from the GitHub API |
What does it cost and take to add promptfoo to GitHub Actions?
Is promptfoo free to run in GitHub Actions?
The promptfoo Community edition is free, and it includes all LLM evaluation features, all model providers and integrations, and red teaming with 10k probes per month, with community support. Licensing is MIT. Two costs remain outside the license: the API spend for the models your evals call, and the GitHub Actions minutes your workflow consumes. Both recur on every run, so they grow with pull request volume and dataset size. Confirm current Community limits on the promptfoo pricing page before you budget.
What recurring costs should I expect from promptfoo evals in CI?
Model API spend is usually the largest recurring cost. Every eval case that calls a model is billed by your provider, and an eval suite run on every pull request multiplies that cost. CI minutes are the second cost, since each run occupies a GitHub-hosted or self-hosted runner for as long as the suite takes. Ask your team to estimate runs per month, cases per suite, providers compared and average run time, then multiply those figures by your provider's published rates and your runner pricing. Caching, covered in another question, is the main lever for both.
What drives the effort to add promptfoo to a pipeline?
Effort scales with four things: the number of prompts and providers you compare, the size and quality of the eval dataset, the caching design that controls repeat model calls, and how secrets are governed across repositories and environments. A single prompt against one provider is a small job. A multi-prompt suite with before and after comparisons across several providers, plus fork pull request handling and a reviewed secrets model, takes materially more design and testing. Ask any partner to show how each driver changes its estimate.
What does a partner quote for a promptfoo GitHub Actions setup depend on?
DeployOpen agrees a fixed scope in writing before work starts, so a quote follows from that scope rather than from a list price. The inputs that matter are the repositories and branches in scope, the prompts, providers and assertions to evaluate, the dataset source and its size, the caching and cost controls you require, the runner model (hosted or self-hosted), the secrets store you use, and whether you want documentation and handover for your own engineers. Ask for each deliverable and each assumption to be listed separately so you can see what changes the price.
What must my team supply before a partner starts?
Plan to provide access to the repositories and CI configuration, a named owner for the prompts and assertions, representative eval datasets with any sensitive data removed, API keys or accounts for each model provider in scope, a decision on where secrets live, and someone who can approve changes to branch protection and required checks. Your team also needs to confirm the Node.js version your runners use, since current promptfoo releases require a recent Node.js version (confirm the exact minimum on the npm package page). Missing any of these items usually extends the timeline.
Can one internal engineer set up promptfoo in GitHub Actions alone?
A single engineer who knows your CI platform and can work with the team that owns the prompts can deliver a basic setup: one workflow, one suite, one provider, and a clear pass or fail rule. The gaps usually appear in secrets governance, cost control through caching, fork pull request behavior, and keeping the eval dataset current. Those areas need security and product input, not just pipeline skills. Decide upfront whether the engineer has time for ongoing maintenance, since prompt and model changes will keep the suite moving.
When do promptfoo Enterprise or On-Premise tiers change the picture?
Consider Enterprise when several teams need shared results, teams-based access control, SSO, custom roles, continuous monitoring or a central guardrail dashboard. Consider On-Premise when you need that functionality on your own infrastructure with complete data isolation and a dedicated deployment engineer. Both are custom priced, so the cost is not public and must be requested from promptfoo directly. Note that self-hosted open-source promptfoo stores data in a local SQLite database without RBAC, SSO or horizontal scaling, and the documentation does not recommend it for production, so a multi-team production rollout needs this decision made early.
How DeployOpen scopes the promptfoo GitHub Actions integration
DeployOpen plans, builds and documents promptfoo evaluations in GitHub Actions for your repositories and CI runners, so each pull request runs the evals you agreed on. Engagements are fixed-scope, with the scope, deliverables and timelines agreed in writing before work starts, and ongoing support is available as a retainer.
How we scope
- The written scope names the repositories, prompt files and test suites that trigger promptfoo evals.
- It records which model providers the workflow calls and which credentials each job needs.
- It sets the baseline comparison rule for before and after prompt changes on pull requests.
- It defines the caching approach and the API spend controls agreed with your team.
- Ask for the quote to list each of these items so that the price follows from the written scope.
How we build
- We write the workflow file, pin the promptfoo version and configure Node.js for your runners.
- We wire required inputs, output artifacts and pull request comments into the workflow.
- We store provider keys in your secrets manager or GitHub secrets, never in the repository.
- We test the workflow on sample pull requests, including a failing prompt change, before handover.
- We document each job, input and cache key so your engineers can change the workflow later.
How we hand over
- You receive the workflow files, configuration and a short runbook written for your engineering team.
- The runbook explains how to add a test case, update a baseline and rotate a provider key.
- We walk your maintainers through a live pull request so they can read the evaluation results.
- Documentation lists the promptfoo version in use and the steps to upgrade it safely.
- Ownership of repositories, runners and secrets stays with your organization throughout.
How we support
- Ongoing support, upgrades and continuous testing are available as a retainer after handover.
- A retainer can cover promptfoo version upgrades and checks that workflows still run on newer Node.js releases.
- Support can review API cost trends and tune cache settings if your eval volume changes.
- Ask in the quote whether a retainer covers runner changes, new providers and new test suites.
- We can also extend the red teaming checks in the pipeline under a separately written scope.

