The most surprising thing about storing secrets in GitHub Actions is that they aren’t actually "stored" in GitHub Actions itself in the way you might expect; they’re managed by GitHub, and Actions just retrieves them.

Let’s see this in action. Imagine you have a deployment script that needs an AWS access key ID and secret access key. You don’t want these hardcoded in your repository. Instead, you’d add them as GitHub Secrets.

First, navigate to your GitHub repository’s "Settings" tab. On the left-hand sidebar, find "Secrets and variables" and then click on "Actions." Here, you can add new repository secrets. Let’s add two:

  • Name: AWS_ACCESS_KEY_ID

  • Secret: AKIAIOSFODNN7EXAMPLE (This is a placeholder, use your actual key)

  • Name: AWS_SECRET_ACCESS_KEY

  • Secret: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY (This is a placeholder, use your actual key)

Once these are saved, you can access them in your GitHub Actions workflow YAML file using the secrets context. For example, in a workflow that deploys to AWS:

name: Deploy to AWS

on: [push]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:

          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}


          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

          aws-region: us-east-1 # Example region

      - name: Run deployment script
        run: |
          echo "Deploying using AWS credentials..."
          # Your deployment commands here, e.g., using AWS CLI
          aws s3 sync ./dist s3://my-example-bucket --delete

In this workflow, {{ secrets.AWS_ACCESS_KEY_ID }} and {{ secrets.AWS_SECRET_ACCESS_KEY }} are placeholders that GitHub Actions will dynamically replace with the actual secret values you configured. When the workflow runs, these secrets are injected into the environment variables of the runner, but they are masked in the logs.

The core problem this solves is preventing sensitive credentials, API tokens, or private keys from being exposed in your codebase, which could be accidentally committed and pushed to a public repository. By using GitHub Secrets, these values are stored encrypted on GitHub’s servers and are only made available to your workflows.

Internally, when a workflow job starts, GitHub securely fetches the secrets associated with that repository (or organization/enterprise, depending on where they are defined) and injects them as environment variables into the runner’s execution context. For instance, AWS_ACCESS_KEY_ID becomes an environment variable named AWS_ACCESS_KEY_ID on the runner. This is why many tools and scripts can directly access them without needing special configuration, as they often read standard environment variables for credentials.

The secrets context is crucial. It’s a global context available in all workflows, allowing you to reference any secret defined for the repository, organization, or environment. You can also define secrets at the organization or enterprise level, which can then be inherited by multiple repositories, promoting consistency and easier management across your projects.

One of the most powerful, yet often overlooked, aspects of GitHub Secrets is their integration with environments. You can define environment-specific secrets that are only available when a workflow targets a particular environment (e.g., staging, production). This adds another layer of security, ensuring that production credentials are only used in production deployments. To use this, you’d go to your repository’s "Settings" -> "Environments", create an environment (e.g., "production"), and then under that environment’s settings, you can add secrets just like you would for the repository. In your workflow, you’d then specify the environment:

jobs:
  deploy_production:
    runs-on: ubuntu-latest
    environment: production # This targets the 'production' environment
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Deploy to production

        run: echo "Deploying to production using ${{ secrets.PROD_API_KEY }}"

Here, secrets.PROD_API_KEY would be a secret defined specifically for the "production" environment, not the general repository secrets.

The next concept you’ll likely encounter is managing secrets for multiple environments and how to best structure your workflows to leverage environment-specific secrets for safer deployments.

Want structured learning?

Take the full Github-actions course →