GitHub Actions Secrets Management

Secrets store sensitive data — like passwords, API keys, and tokens — safely inside GitHub. Unlike environment variables, secrets are encrypted and never appear in workflow logs. This topic shows you how to create, access, and manage secrets securely in your workflows.

Why Secrets Exist

Imagine printing your house key on a flyer and posting it around town. Anyone who found the flyer could copy the key and enter your home. Storing a password directly in a workflow file does the same thing — anyone with repository access can read it.

Secrets solve this problem. You store the value once inside GitHub's encrypted vault. Your workflow references it by name. The actual value never appears in the file.

Without secrets (dangerous):
  run: aws s3 sync ./dist s3://my-bucket --key "AKIAIOSFODNN7EXAMPLE"

With secrets (safe):
  run: aws s3 sync ./dist s3://my-bucket
  env:
    AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}

Creating a Secret

Follow these steps to add a secret to your repository:

  1. Go to your repository on GitHub
  2. Click SettingsSecrets and variablesActions
  3. Click New repository secret
  4. Enter a name (uppercase letters with underscores, e.g. DATABASE_URL)
  5. Paste the secret value in the text box
  6. Click Add secret

Once saved, you cannot view the secret value again — not even as the repository owner. You can only update or delete it.

Using a Secret in a Workflow

Reference a secret using the secrets context with the double curly brace syntax:

steps:
  - name: Deploy to server
    run: ./deploy.sh
    env:
      DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
      DB_PASSWORD: ${{ secrets.DB_PASSWORD }}

GitHub replaces ${{ secrets.DEPLOY_TOKEN }} with the actual value at runtime — but only on the runner. The value never appears in the workflow file itself or in the logs.

How GitHub Hides Secrets in Logs

GitHub automatically masks secret values in workflow logs. If a secret appears in a log line, GitHub replaces it with asterisks:

Run echo "Token is $MY_SECRET"
Token is ***

This masking protects accidental exposure. However, if you encode the secret (e.g. as base64) and then print it, the masking may not catch it. Avoid printing secrets in any form.

Types of Secrets

Repository Secrets

Available to all workflows in one specific repository. This is the most common type.

Environment Secrets

Tied to a named GitHub environment (like staging or production). These secrets are only available when a job explicitly targets that environment. This adds a protection layer — your production database password is only accessible during the deploy-to-production job, not during testing jobs.

jobs:
  deploy-prod:
    runs-on: ubuntu-latest
    environment: production       ← Unlocks production secrets
    steps:
      - run: echo "${{ secrets.PROD_DB_PASSWORD }}"

Organization Secrets

Defined at the GitHub organization level and shared across multiple repositories. Useful for teams that use the same API keys in many projects. You can control which repositories can access each organization secret.

Secret Scope Comparison:

Repository Secret    → Available in: one repository
Environment Secret   → Available in: one job targeting that environment
Organization Secret  → Available in: selected repositories in the org

Accessing Secrets in Non-Shell Steps

Pass a secret directly as an input to a Marketplace action:

steps:
  - name: Notify Slack
    uses: slackapi/slack-github-action@v1.27.0
    with:
      webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
      webhook-type: incoming-webhook
      payload: '{"text": "Deployment complete!"}'

Common Secrets You Will Create

Secret Name              | Typical Use
-------------------------|-------------------------------------------
AWS_ACCESS_KEY_ID        | AWS authentication
AWS_SECRET_ACCESS_KEY    | AWS authentication
DOCKER_USERNAME          | Log in to Docker Hub
DOCKER_PASSWORD          | Log in to Docker Hub
HEROKU_API_KEY           | Deploy to Heroku
NPM_TOKEN                | Publish to npm
SLACK_WEBHOOK_URL        | Send Slack notifications
GH_TOKEN                 | GitHub API calls with elevated permissions
SONAR_TOKEN              | SonarCloud code quality scanning

Security Rules for Secrets

  • Never print a secret in a run step — even "for debugging"
  • Never pass a secret as a command-line argument — it may appear in process listings
  • Rotate secrets regularly and after any team member leaves
  • Use environment secrets for production credentials
  • Limit which branches can trigger jobs that use sensitive secrets
  • Audit secret access by reviewing the Actions tab logs

Secrets vs Variables

GitHub also offers a "Variables" feature (distinct from Secrets) for non-sensitive configuration values. Variables are visible in logs and accessible in the UI after creation.

Feature        | Secrets          | Variables
---------------|------------------|------------------
Encrypted      | Yes              | No
Visible in UI  | Never            | Yes
Visible in log | Masked           | Visible
Best for       | Passwords, keys  | Feature flags, URLs
Access syntax  | secrets.NAME     | vars.NAME

Use vars.NAME to reference a variable:

env:
  API_ENDPOINT: ${{ vars.API_ENDPOINT }}

Leave a Comment

Your email address will not be published. Required fields are marked *