Video Thumbnail for Lesson
3.4: Secrets and Next Steps

Secrets, variables, and environments

GitHub Actions provides a built-in mechanism for handling secrets and variables to be used within and across workflows.

Configuration that must stay private belongs in secrets, whereas non-sensitive values can live in variables. Both can be defined at the repository, organization, or environment level. Use environments when you need deployment gates, manual approvals, or differentiated credentials for staging vs. production.

The 03-core-features--07-secrets-and-variables.yaml workflow demonstrates injecting each tier into the runtime shell. GitHub automatically masks secret values in the logs—if you echo a secret, you will see *** instead of the raw value. Variables, on the other hand, are printed as-is, so treat them with care if they contain sensitive data.

When referencing secrets and variables in YAML you must use expression syntax (${{ secrets.MY_SECRET }} and ${{ vars.MY_VARIABLE }}) and typically assign them to environment variables for the command that needs them.

jobs:
  staging-environment:
    runs-on: ubuntu-24.04
    environment: staging
    env:
      # Inject repository-level secret & variable into the shell
      EXAMPLE_REPOSITORY_SECRET: ${{ secrets.EXAMPLE_REPOSITORY_SECRET }}
      EXAMPLE_REPOSITORY_VARIABLE: ${{ vars.EXAMPLE_REPOSITORY_VARIABLE }}
      # Inject environment-level items into the shell
      EXAMPLE_ENVIRONMENT_SECRET: ${{ secrets.EXAMPLE_ENVIRONMENT_SECRET }}
      EXAMPLE_ENVIRONMENT_VARIABLE: ${{ vars.EXAMPLE_ENVIRONMENT_VARIABLE }}
    steps:
      - name: Inspect values inside job
        run: |
          echo "Repo secret (masked):    $EXAMPLE_REPOSITORY_SECRET"
          echo "Repo variable:           $EXAMPLE_REPOSITORY_VARIABLE"
          echo "Env secret (masked):     $EXAMPLE_ENVIRONMENT_SECRET"
          echo "Env variable:            $EXAMPLE_ENVIRONMENT_VARIABLE"

  production-environment:
    runs-on: ubuntu-24.04
    environment: production
    env:
      # Inject repository-level secret & variable into the shell
      EXAMPLE_REPOSITORY_SECRET: ${{ secrets.EXAMPLE_REPOSITORY_SECRET }}
      EXAMPLE_REPOSITORY_VARIABLE: ${{ vars.EXAMPLE_REPOSITORY_VARIABLE }}
      # Inject environment-level items into the shell
      EXAMPLE_ENVIRONMENT_SECRET: ${{ secrets.EXAMPLE_ENVIRONMENT_SECRET }}
      EXAMPLE_ENVIRONMENT_VARIABLE: ${{ vars.EXAMPLE_ENVIRONMENT_VARIABLE }}
    steps:
      - name: Inspect values inside job
        run: |
          echo "Repo secret (masked):    $EXAMPLE_REPOSITORY_SECRET"
          echo "Repo variable:           $EXAMPLE_REPOSITORY_VARIABLE"
          echo "Env secret (masked):     $EXAMPLE_ENVIRONMENT_SECRET"
          echo "Env variable:            $EXAMPLE_ENVIRONMENT_VARIABLE"

Contexts

Many configuration values are exposed through contexts, structured data that you reference with the ${{ ... }} expression syntax. Common contexts include:

  • github: metadata about the repository, commit, event, and actor.
  • env: the environment variables available at the current scope.
  • secrets and vars: user-managed configuration.
  • needs: outputs from prerequisite jobs.

Expressions let you perform simple logic (if: ${{ github.ref == 'refs/heads/main' }}), interpolate values, and compose dynamic strings for commands or notifications.

The full set of contexts can be found here: Reference Docs