loke.dev
A protected deployment pipeline separating untrusted pull requests from a 1Password-backed production secret path

1Password in GitHub Actions: Protect the Bootstrap Token

Use 1Password load-secrets-action with a scoped service account, then isolate fork pull requests, job outputs, environments, logs, and token rotation.

Published 11 min read

Moving deployment credentials from GitHub repository secrets into 1Password reduces duplicated application secrets. It does not remove every long-lived credential from GitHub. The runner still needs one bootstrap credential, usually OP_SERVICE_ACCOUNT_TOKEN, before it can ask 1Password for anything.

If you are still choosing an account, compare 1Password Teams and Business first. Service-account limits and operational controls differ by plan.

The short answer

Use a narrowly scoped 1Password service account for trusted deployment jobs. Put its token in a GitHub environment secret, expose it only to the load-secrets step, and pass each resolved application secret only to the step that needs it.

Run ordinary pull-request checks without 1Password. Never change a fork workflow to pull_request_target merely to regain secret access and then execute the contributor's code. GitHub deliberately withholds Actions secrets from fork pull_request workflows.

The bootstrap token is the remaining trust anchor

A secret manager changes the shape of the risk. Instead of storing a database password, registry token, signing key, and deployment credential separately in GitHub, you store references to them and one credential that can resolve the allowed references.

That is valuable only when the service account has less access than a human account and the token reaches fewer steps. A service account allowed to read every vault turns one copied token into a broad incident.

1Password describes service accounts as nonhuman identities whose accessible vaults, Environments, and permitted actions can be controlled. The service account documentation is the source of truth for current account behavior.

  • GitHub stores OP_SERVICE_ACCOUNT_TOKEN and decides which job may receive it.
  • The 1Password service account decides which vaults and items that token can read.
  • The workflow decides which action and later process receive the resolved values.
  • The deployment target decides what each resolved credential can actually change.

All four boundaries matter. Tightening only the vault while leaving the deployment credential as an administrator token still leaves a high-impact path.

Create a CI-only service account

Do not use a developer's personal 1Password session in an unattended runner. Create a service account owned by the team or project, give it a descriptive name such as github-production-deploy, and record an operational owner.

  • Grant read access only to a dedicated deployment vault.
  • Put only the items needed by this pipeline in that vault.
  • Prefer a separate account for production and staging when their consequences differ.
  • Do not grant write access unless the workflow has a documented reason to modify 1Password.
  • Record where the token is stored, which workflows use it, and how to revoke it.

Create the token in 1Password, copy it once into GitHub, and then stop displaying it. The token is a secret in its own right. If it appears in a terminal, screenshot, issue, chat, or workflow log, revoke it and issue a replacement.

Store the token in a GitHub environment

A repository secret can work, and the 1Password setup guide uses that as its basic example. For a deployment path, a GitHub environment gives you a narrower place to attach the token and can add deployment-branch rules or required review, depending on repository visibility and plan.

  • Create a production environment in the repository settings.
  • Add OP_SERVICE_ACCOUNT_TOKEN as an environment secret.
  • Allow deployments only from the intended branch or tag pattern.
  • Add a required reviewer when the repository and GitHub plan support it.
  • Set permissions explicitly in the workflow instead of accepting broader defaults.

GitHub says environment secrets are released only to jobs that reference the environment and only after configured protection rules pass. Check the current GitHub environment documentation because protection-rule availability varies by plan and public or private visibility.

Keep pull-request CI and deployment separate

The cleanest design uses two workflows with different trust levels. Pull-request CI compiles and tests code but receives no deployment secrets. A deployment workflow runs only after trusted code reaches the protected branch or an approved manual dispatch.

Pull requests: run untrusted code without secrets

name: pull-request-ci

on:
  pull_request:

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
      - run: npm ci
      - run: npm test

This job can execute package scripts, tests, build tools, and configuration supplied by the pull request. That is precisely why it should not receive the 1Password service-account token.

GitHub documents that, except for a read-only GITHUB_TOKEN, secrets are not passed to workflows triggered from forked repositories. Dependabot pull requests receive similar restrictions. See events that trigger workflows.

Deployment: run trusted code with narrow secrets

name: production-deploy

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read

jobs:
  deploy:
    environment: production
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4

      - name: Load deployment token
        id: op
        uses: 1password/load-secrets-action@7ce42673ba9ed69053d678faeba29ea36bd25755 # v4.0.0
        with:
          export-env: false
        env:
          OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
          DEPLOY_TOKEN: op://production-ci/deployment/token

      - name: Deploy
        env:
          DEPLOY_TOKEN: ${{ steps.op.outputs.DEPLOY_TOKEN }}
        run: ./scripts/deploy.sh

The example pins 1Password load-secrets-action v4.0.0 to the commit referenced by its public tag on 24 July 2026. The moving v4 tag pointed at a different commit on that date. A full commit SHA is immutable, while a major-version tag can move.

GitHub's secure use reference recommends full-length commit SHAs for third-party actions and says to verify that a SHA belongs to the action repository.

Limit the token to the loading step

1Password documents both a configure action and direct step-level token use. The configure action can make the service-account token available to subsequent steps. That is convenient when many steps resolve secrets, but it also increases the number of steps that can read the bootstrap credential.

For a small deployment job, pass OP_SERVICE_ACCOUNT_TOKEN only in the env map of the load-secrets step. Later steps receive only the resolved value they need. This makes the boundary visible in the workflow file.

The current 1Password GitHub Actions guide explicitly notes that using the configure step can make the service-account token available to every subsequent step, and shows step-level env as the narrower alternative.

Prefer outputs when only one later step needs a value

load-secrets-action v4 supports step outputs and exported environment variables. Its repository recommends outputs. With export-env set to false, DEPLOY_TOKEN is referenced through the named step and then placed only in the deploy step's environment.

Use export-env: true only when several later steps genuinely require the same secret. An environment variable is not harmless: code, dependencies, debugging flags, crash handlers, and child processes in those steps may read it.

Do not print a secret to prove masking works. Masking is a last-resort log control, not an authorization boundary. Review both successful and failing logs because command-line tools often include request details or environment-derived values in error output.

Do not use pull_request_target as a secret bypass

A common failure begins when tests from forked pull requests cannot reach a private registry or external service. Someone changes pull_request to pull_request_target, checks out the contributor's commit, and runs its build. The job now combines untrusted code with base-repository secrets.

The dangerous part is not checkout by itself. The next npm install, test script, Makefile, container build, or generated command executes files controlled by the pull request. Those files can read the job environment or contact the network.

GitHub calls this a privileged-workflow risk and warns against checking out and executing untrusted pull-request code in pull_request_target or workflow_run jobs. Read Securely using pull_request_target before using that event.

  • Keep secret-free tests on pull_request.
  • Move deployment to push on a protected branch or an approved workflow_dispatch.
  • Use a separate unprivileged workflow for labels or comments if pull_request_target is necessary.
  • Treat artifacts produced by an untrusted workflow as untrusted data in any later privileged workflow.

Reduce the blast radius after 1Password

The 1Password vault is only one layer. Each retrieved credential should also be scoped at its destination. A deployment token should target one project and environment, not an entire cloud account. A registry credential should publish only the intended package or repository.

  • Set GITHUB_TOKEN permissions to contents: read unless a job needs more.
  • Use cloud-provider OpenID Connect when the target supports short-lived GitHub identities, instead of retrieving a long-lived cloud key from any secret manager.
  • Separate staging and production vaults, service accounts, GitHub environments, and target credentials.
  • Protect .github/workflows with CODEOWNERS or an equivalent review rule.
  • Avoid persistent self-hosted runners for untrusted pull-request code.

GitHub's security guidance recommends minimum GITHUB_TOKEN permissions and OIDC for supported cloud providers. OIDC may remove a cloud access key, but it does not automatically replace every application secret or the 1Password token used for other values.

Plan for rate limits and parallel jobs

A matrix build can turn one workflow into dozens of 1Password reads. Repeatedly resolving the same references in every shard is both slower and easier to rate-limit. Load deployment secrets only in the job that deploys.

Current 1Password service-account rate limits are scoped per token and also per account. On 1Password Teams, the published limits are 1,000 reads per hour per token and 5,000 reads or writes per day per account. Business has higher published limits. Check the page before designing a high-volume pipeline.

Do not pass a resolved secret through an artifact to avoid another read. Artifacts cross a different storage and retention boundary. If a later job needs the same privilege, decide whether it should resolve the secret under its own identity or whether the jobs should be combined.

Rotate and test without exposing values

A useful test proves access and revocation without echoing the credential.

  • Start with a disposable target or a low-impact staging token.
  • Run the trusted workflow and confirm the target operation succeeds.
  • Confirm the pull-request workflow has no OP_SERVICE_ACCOUNT_TOKEN entry and cannot perform the target operation.
  • Remove the item or vault permission from the service account and confirm the deploy fails closed.
  • Restore access, replace the service-account token in GitHub, and revoke the old token.
  • Inspect successful and failed logs for values, prefixes, authorization headers, and unexpected command tracing.
  • Record the token owner, vault scope, GitHub secret location, last rotation, and emergency revocation steps.

If a value reaches an unredacted log, delete the affected log and rotate the credential. Redaction after the fact does not make a copied credential safe.

Choose service accounts or Connect deliberately

load-secrets-action supports a 1Password service account or a 1Password Connect server. For a typical GitHub-hosted deployment, 1Password's current secure-deployment guide recommends the GitHub integration with a service account.

Connect can make sense when you already operate the Connect service, need its network placement, or have an established token and availability model. It also adds infrastructure, availability, upgrades, and a Connect access-token boundary. Do not deploy it only because it sounds more enterprise.

Use the 1Password secure CI/CD guide to compare the current integration and authentication options.

Swedish and EU teams need an operational review too

Secret injection does not settle data location, support access, audit retention, employee monitoring, or incident-response questions. Review the 1Password account region, vault membership, administrator roles, service-account activity, subprocessors, and the GitHub runner location that applies to your organization.

Write down which credential classes may be resolved on GitHub-hosted runners and which require a controlled runner or another deployment path. Keep customer data and production database exports out of CI secrets entirely.

A practical acceptance checklist

  • The service account is nonhuman, named for one purpose, and limited to a dedicated vault.
  • OP_SERVICE_ACCOUNT_TOKEN is stored as an environment secret, not in YAML or repository files.
  • Pull-request workflows receive no 1Password credential.
  • No privileged workflow checks out and executes untrusted pull-request code.
  • Third-party actions are pinned to verified full commit SHAs.
  • The load step alone receives the bootstrap token.
  • Each resolved secret reaches only the step that needs it.
  • GITHUB_TOKEN and deployment-target permissions are explicitly narrow.
  • Logs have been checked on both success and failure without intentionally printing secrets.
  • Rotation, revocation, ownership, and rate-limit behavior are documented.

Decide from the boundary, not the YAML

This integration is a good fit when a team already uses 1Password, several deployment secrets need one managed source, and a narrowly scoped service account can be isolated from untrusted code.

Keep GitHub environment secrets alone when the pipeline has only one or two low-impact values and another secret manager would add more operational work than it removes. Prefer OIDC when the destination can issue a short-lived credential directly to the workflow.

For local agent-driven development rather than CI, use the separate 1Password MCP for Codex boundary guide. The desktop approval model and the unattended service-account model solve different problems.

If the trust model fits, check the current 1Password options. This direct tracked link currently earns no commission.

Primary sources checked

  • 1Password: Load secrets from 1Password into GitHub Actions
  • 1Password: Service accounts and rate limits
  • 1Password: Secure CI/CD and deployments
  • 1Password load-secrets-action repository and v4 tags
  • GitHub: Secure use reference for Actions
  • GitHub: Events that trigger workflows
  • GitHub: Securely using pull_request_target
  • GitHub: Managing deployment environments