GitHub CI/CD send secrets from other job
02:10 17 Nov 2025

I’m trying to chain two reusable GitHub Actions workflows:

Workflow A (secrets.yaml) → fetches secrets from Vault

Workflow B (build.yaml) → needs the output DOCKER_CONFIG generated by workflow A

My caller workflow looks like this:

  first:
    uses: project/continuous-integration/.github/workflows/secrets.yaml@main
    with:
      vault_url: "https://..."
    secrets:
      TOKEN: ${{ secrets.TOKEN }}

  second:
    needs: first
    uses: project/continuous-integration/.github/workflows/build.yaml@main
    with:
      # … build inputs …
    secrets:
      DOCKER_CONFIG: ${{ needs.first.outputs.DOCKER_CONFIG }}

And inside secrets.yaml:

  workflow_call:
    inputs:
      vault_url:
        required: true
        type: string
    secrets:
      TOKEN:
        required: true
    outputs:
      DOCKER_CONFIG:
        value: ${{ jobs.fetch.outputs.DOCKER_CONFIG }}

jobs:
  fetch:
    runs-on: ubuntu-latest
    outputs:
      DOCKER_CONFIG: ${{ steps.fetch.outputs.DOCKER_CONFIG }}

    steps:
      - name: Fetch secrets from Vault
        id: fetch
        run: |
          DOCKER_CONFIG=$(curl ... )  # fetch from Vault
          echo "::add-mask::$DOCKER_CONFIG"

          if [[ -z "$DOCKER_CONFIG" ]]; then
            echo "Missing DOCKER_CONFIG"
            exit 1
          fi

          echo "DOCKER_CONFIG=$DOCKER_CONFIG" >> "$GITHUB_OUTPUT"

What I expected:

needs.first.outputs.DOCKER_CONFIG should provide the value to the second job so I can pass it as a secret.

What actually happens:

I get: Unexpected value ''

My question:

How can I correctly return an output from a reusable workflow and then pass that value as a secret into another reusable workflow? Is it even allowed to map needs..outputs.* to secrets: of a called workflow?

github continuous-integration github-actions