Secure Github Workflows with Infisical Github Secrets Action and OIDC
Secure github workflows with Infisical Github Secrets Action and OIDC
Looking to improve your secret management processes?Talk to an expert
Fetching Secrets from Infisical in GitHub Actions with OIDC
Anytime you run CI/CD, you eventually need secrets: database URLs, API keys, tokens. The real question is how you manage them. This post walks through how to fetch secrets from Infisical directly into a GitHub Actions workflow, instead of hard-coding them or storing them long-term in GitHub.
A secrets manager like Infisical gives you a single place to manage, rotate, and control access to secrets across all your environments. That centralization starts to matter once you have more than a handful of workflows. The approach here uses OIDC so GitHub Actions can authenticate to Infisical and request secrets at runtime.
By the end, you'll have a simple, production-ready pattern for using Infisical in GitHub Actions: secrets fetched dynamically at runtime, with access based on verified identity rather than stored credentials.
The problem with secrets sprawl in GitHub Actions
Before getting into how to authenticate securely, it helps to look at how secrets usually get managed in CI/CD and what tends to be missing.
In many setups, secrets end up scattered across pipelines. They're stored directly in GitHub, copied between repositories, duplicated across environments, and updated by hand whenever something changes. This works fine at first, when you have a small number of pipelines. But as teams grow and CI/CD expands across dozens or even hundreds of workflows, pipelines, repositories, and environments, it starts to break down.
Secrets get copied from repo to repo. Environments drift out of sync. The same credentials end up reused far more widely than intended. At that point, every secret carries a much larger blast radius. A single leaked credential can affect multiple services, environments, or pipelines, not because it was designed that way, but because it has been reused everywhere.
Rotation becomes risky and time-consuming. Instead of updating one place, you're hunting through workflows asking which pipelines depend on this value, which environments are still using the old version, and what might break if you rotate it. Because rotation is painful, secrets tend to stick around far longer than they should, simply because changing them feels dangerous.
Over time it becomes hard to answer a basic question: who had access to this secret, and when? There's no clear audit trail, no single place to reason about access, and no easy way to understand how secrets flow through your CI/CD system.
This pattern, where secrets are spread across repositories, pipelines, and environments with a growing blast radius and shrinking visibility, is what people refer to as secret sprawl.
How a secrets manager fixes CI/CD secret sprawl
A secrets manager is designed to solve exactly this problem. Instead of scattering secrets across pipelines, everything lives in one centralized place with clear environment scoping, fine-grained access control, and auditability built in.
Infisical provides that centralized foundation. Just as importantly, it gives CI/CD pipelines a secure way to fetch secrets at runtime.
Once you decide to use a secrets manager, the next question becomes obvious: how does a CI/CD workflow actually authenticate to fetch those secrets?
The "secret zero" problem with static API keys
In many setups, the answer is a static API key or token. You generate a credential in your secrets manager, store it in GitHub secrets, and use it to fetch everything else at runtime.
This is where teams recreate a familiar problem. That first credential, the one you stored in GitHub, becomes your secret zero. It's the root secret that everything else depends on. If it leaks, is mis-scoped, or expires, every downstream system is affected. And because it's long-lived, it tends to stick around far longer than it should.
So even though you centralized your secrets in a manager, you've still anchored trust to a static credential sitting at rest in GitHub. That's the pitfall we want to avoid.
Instead of authenticating with a stored secret, we want workflows to authenticate based on their workload identity.
Authenticating CI/CD securely with OIDC
This is where OIDC authentication comes in, and it's something Infisical supports natively. With OIDC, GitHub can prove who it is at runtime: which repository it's running in, which workflow is executing, and what context it's running under. There's no long-lived credential to store and nothing permanent to rotate.
That identity-based model is what lets you fetch secrets securely without reintroducing secret zero. Now that we've covered the why, let's walk through how to set this up in Infisical.
Setting up secrets and machine identities in Infisical
First, open a project inside Infisical. This is where your secrets will live. Within that project, I have a dev environment with a realistic set of secrets that a CI pipeline would actually need: Docker registry credentials, specifically a Docker username and Docker password, which we'll use to authenticate and push an image as part of the workflow.
Next, GitHub Actions needs a way to authenticate to Infisical, and this is where machine identities come in. A machine identity in Infisical is a service account designed for workloads like CI/CD, servers, or jobs. It defines what an automated system is allowed to access without tying that access to a user account.
Create one under Access Control > Machine Identities. I've scoped this machine identity with a rule that only allows access to the specific secrets it needs.
By default, it's set up to use universal auth, which relies on static credentials like a client ID and secret. That works, but for CI/CD we want to avoid the long-lived credentials problem covered earlier. So remove that and switch the identity to use OIDC with GitHub.
Here's where you scope exactly what this identity is allowed to trust. First, define the subject. This tells Infisical which GitHub workflow is allowed to authenticate. For GitHub Actions, the subject is derived from the repository and where the workflow is running, such as a specific branch or environment. The full subject and audience formats are documented, so you can copy them exactly when setting this up.
So instead of saying "any workflow from GitHub," you're saying specifically this repository, running in this context, is allowed to request these secrets.
Next, define the audience. This is who the token is intended for. By default it's set to the GitHub organization that owns the repository. Infisical checks this against the audience claim in the token to make sure it hasn't been reused somewhere it shouldn't be.
Together, these settings make the trust relationship explicit. Only workflows that match the repository context and audience defined here will be able to authenticate and fetch secrets.
Once that's set up, Infisical gives you an identity ID. This is what you'll reference inside the GitHub Actions workflow. One important note: the identity ID is not a secret. It's just an identifier, safe to commit directly into your workflow YAML.
Configuring GitHub Actions with OIDC (no stored secrets)
At this point Infisical is ready. We've defined what secrets exist and who or what is allowed to request them, without creating any static credentials. Now let's switch over to the GitHub Actions workflow itself.
Inside the repository, I have a simple workflow under .github/workflows. This workflow is manually triggered so we can run it on demand while testing.
At the top of the workflow, there's one important permissions block: id-token: write. We allow this explicitly because it's what lets GitHub issue an OIDC token for the run. Without it, OIDC authentication won't work.
The core of the workflow is the Infisical secrets action. This step authenticates to Infisical and fetches secrets at runtime. Tell it to use the OIDC method, pass in the identity ID from Infisical, and specify which project and environment you want secrets from.
What's important here is what you don't see. There are no GitHub secrets configured, no API key, and no long-lived credentials stored anywhere in this repository. This workflow is trusted purely based on its identity: which workflow it's running in, which repo it is, and which claims are attached to that run.
Once this step completes, any secrets fetched from Infisical are injected as environment variables and made available to the steps that follow.
How secrets are fetched and injected at runtime
So what actually happens when this Infisical step runs?
When the workflow starts, GitHub generates a short-lived OIDC token just for this specific job. That token contains claims about where it's running: the repository, the workflow, and the organization. The Infisical secrets action takes that token and sends it to Infisical as proof of identity.
Infisical verifies those claims against the machine identity you configured earlier. If everything matches, the workflow, the repo, and the permissions, Infisical issues a short-lived access token. The Infisical secrets action then uses that token to securely fetch the secrets requested for the specific project and environment.
One subtle detail: if identity and permissions don't match, Infisical intentionally returns a 404 instead of a 403, so you don't leak project existence.
At that point, the secrets are injected directly into the workflow as environment variables. No file is written unless you explicitly ask for one, and nothing is persisted beyond this job execution.
This is the key difference from traditional CI setups. Instead of pulling secrets using a static credential that lives forever, you fetch them dynamically based on a verified identity that only exists for the duration of the run. Once the job finishes, the token expires, the environment is torn down, and the secrets are gone automatically.
Now that the secrets are available, you can use them in the workflow. In the next step, I reference the Docker username and Docker password like any other environment variables. Those credentials are fetched from Infisical at runtime and injected directly into the job, so the workflow can authenticate to the Docker registry and push an image.
Because those secrets only exist for the duration of the job, there's no risk of them sticking around afterward. Once the workflow finishes, the environment is torn down and the secrets disappear with it. No cleanup required. That's what makes this pattern powerful: you get the convenience of environment variables without relying on long-lived credentials stored in your CI system.
Running the workflow end-to-end (Docker push demo)
Let's run this workflow and see it end to end. Because it uses a manual trigger, I can run it directly from the GitHub Actions tab.
Once it starts, you can watch each step execute in order, including the Infisical secrets action authenticating via OIDC. Notice there are no static credentials in the logs; authentication is handled through identity instead. When the job reaches the Docker authentication step, the login succeeds, and shortly after, the image is built and pushed.
In Docker Hub, you can see the new image was pushed and built successfully, tagged with the number of the most recent GitHub workflow run.
That's the full flow. GitHub proves its identity, Infisical verifies it, secrets are fetched just in time, and everything is cleaned up automatically at the end of the run.
Final recap: identity-based secrets at scale
To zoom out, the problem we started with is that secrets tend to end up scattered across repositories, pipelines, and environments, making it hard to understand who owns them, how they're rotated, and who or what actually had access and when.
This pattern flips that around. Instead of managing secrets separately in your CI system, everything lives centrally in one place with clear scoping, auditability, and access control, and workflows only get what they need when they need it. Access is no longer based on static credentials but on identity: which repository is running, which workflow is executing, and what exactly it's allowed to do. That makes permissions much easier to reason about and much easier to scale across teams and environments.
It also means far less operational overhead. You're no longer chasing down secrets across pipelines, manually rotating credentials, or worrying about cleanup after a job finishes. Everything is issued just in time and expires automatically.
While we focused on GitHub Actions here, the same model applies anywhere you can use OIDC. Infisical also supports other authentication methods like AWS IAM or universal auth, depending on where your workloads run.
If you're building or managing CI/CD pipelines today, this is a great foundation to start from: centralized, secure by default, and flexible as your setup grows. If you want to try it yourself, see the resources below for the full setup guide and example workflow.
Resources
- Infisical GitHub Actions integration guide: the full step-by-step setup, including the example workflow YAML.
- Infisical Secrets Action on GitHub: the action source, inputs, and OIDC, AWS IAM, and universal auth examples.
- OIDC Auth for GitHub: exact subject, audience, and claims formats for GitHub OIDC tokens.
- OIDC Auth overview: how OIDC authentication works across providers.
- Machine identities: creating and scoping non-human identities in Infisical.
- AWS IAM Auth: authenticating from EC2, Lambda, and other IAM principals.
- Universal Auth: platform-agnostic client ID and secret authentication.
Starting with Infisical is simple, fast, and free.

PRODUCT
CONTACT