How do I share secrets and configuration across CI pipelines?

Define each secret or variable once in Depot CI, then use scoped variants for repositories, branches, workflows, and environments. The workflow keeps one stable name while Depot selects the most specific matching value.

Last updated 2026-09-10

Share secrets and configuration across CI pipelines by giving each value one stable name, storing it once in Depot CI, and adding scoped variants only where the value needs to differ.

For example, every workflow can use the same expression:

env:
  API_BASE_URL: ${{ vars.API_BASE_URL }}
  DATABASE_URL: ${{ secrets.DATABASE_URL }}

Depot selects the matching value for the repository, branch, workflow file, and job environment before the workflow starts. The YAML doesn't need a copy of every environment-specific credential.

Use secrets for credentials and variables for configuration

Depot CI secrets are encrypted, decrypted on demand, sent to the isolated job sandbox, and masked in logs. Their values can't be read back after creation.

Variables are plain text. Their values are visible in the dashboard and CLI output. Use them for configuration that's not sensitive, such as service names, regions, feature modes, and public endpoints.

Don't put a credential in a variable because it's convenient to inspect. Don't put ordinary configuration in a secret if engineers need to audit or compare it.

Organization owners can manage secrets and variables in the Depot dashboard. Existing GitHub Actions values can be imported with:

depot ci migrate secrets-and-vars

That command creates a one-shot GitHub Actions workflow to import the values. It's a migration mechanism, not a continuous synchronization path.

Start with a fallback, then add variants

Create an unscoped fallback only when the value is safe and correct for every matching job. Add variants for narrower contexts.

A common DATABASE_URL setup is:

  • Default variant for development or test jobs
  • Repository variant for a service with its own database
  • Branch variant for a release branch
  • Environment variant for staging or production

Every workflow still reads ${{ secrets.DATABASE_URL }}. Depot resolves the value from least to most specific.

When more than one variant matches, attribute priority is:

  1. Environment
  2. Repository
  3. Branch
  4. Workflow
  5. No attributes

Combining different attribute types makes a variant more specific. A variant scoped to both repository=api and environment=production beats a variant scoped only to environment=production.

Within branch or workflow patterns, a narrow match beats a broad match. release/v2 is more specific than release/*.

Select environment values explicitly

An environment-scoped variant matches the job's environment field:

jobs:
  deploy:
    runs-on: depot-ubuntu-24.04
    environment: production
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/deploy.sh
        env:
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
          API_BASE_URL: ${{ vars.API_BASE_URL }}

A job without environment: production can't receive a variant limited to that environment, even if it runs on the default branch or lives in the deployment workflow.

The environment field selects the matching Depot secret and variable variants. It doesn't, by itself, create a human approval gate. Keep deployment approval in the system that owns the release policy, or use a separately dispatched deployment workflow.

Limit credentials to the job that needs them

Scope a production credential across more than one dimension when possible:

  • Repository: only the service that deploys
  • Branch: only main or release/*
  • Workflow: only deploy.yml
  • Environment: only production

This prevents a pull request workflow from receiving the credential simply because it uses the same secret name.

Also keep workflow token permissions narrow:

permissions:
  contents: read

Add write scopes only to the job that needs them. Don't give validation jobs release, package, or infrastructure credentials.

Know the built-in token exceptions

Depot CI injects a short-lived DEPOT_TOKEN into every job. It has access to the organization's Depot projects and registry for that job. You don't need to store another Depot token for ordinary container builds or Depot Registry pushes.

If you create your own DEPOT_TOKEN secret, it overrides the injected token. A migrated project token may be scoped to one container-build project and fail when the workflow tries to access another registry repository. Remove the custom secret when the built-in token is the correct identity.

The built-in job token can't dispatch another Depot CI workflow. Store an organization or user token under a different name, such as DEPOT_ORG_TOKEN, when a workflow must call depot ci dispatch.

Depot CI also exposes secrets.GITHUB_TOKEN as a GitHub App token. GitHub Packages doesn't accept GitHub App tokens for package authentication. Use Depot Registry, another package registry, or a separately managed GitHub personal access token if a job must push to or pull from GitHub Packages.

Verify resolution before running production

Use the filters in Depot CI settings to select a repository, branch, workflow, and environment. The dashboard shows which variant wins and greys out the other matches.

Check that resolution whenever you add a broad glob or a more specific variant. The safest secret system is still dangerous if production resolves to the test credential or a pull request job unexpectedly matches a release branch.

Keep the names stable, keep the values out of YAML, and make the scope narrow enough that the wrong job can't receive them.

Start building with Depot
in minutes