GitHub Actions Tutorial — Job Outputs
Job Outputs
Since each job runs on a separate runner (fresh VM), there’s no shared filesystem between jobs. To pass data from one job to another, you use job outputs — values set in one job and read by downstream jobs via the needs context.
How Job Outputs Work
Job A (producer) Job B (consumer)
┌──────────────────┐ ┌──────────────────┐
│ Step: set output │ │ Read output via │
│ → version=2.1 │──── needs ───►│ needs.A.outputs │
│ │ │ .version │
│ outputs: │ │ │
│ version: ... │ │ │
└──────────────────┘ └──────────────────┘
Step 1 — Set a Step Output
Inside a step, write to $GITHUB_OUTPUT:
steps:
- name: Set version
id: version-step # ← step ID (required to reference later)
run: echo "version=2.1.0" >> "$GITHUB_OUTPUT"
Step 2 — Map Step Output to Job Output
In the job’s outputs: section, map the step output to a job output:
jobs:
build:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version-step.outputs.version }}
steps:
- name: Set version
id: version-step
run: echo "version=2.1.0" >> "$GITHUB_OUTPUT"
Step 3 — Read in a Downstream Job
The downstream job reads the output via needs.<job-id>.outputs.<name>:
jobs:
build:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version-step.outputs.version }}
steps:
- name: Set version
id: version-step
run: echo "version=2.1.0" >> "$GITHUB_OUTPUT"
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Use version
run: echo "Deploying version ${{ needs.build.outputs.version }}"
Output:
Deploying version 2.1.0
Complete Example — Dynamic Version
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.pkg.outputs.version }}
sha-short: ${{ steps.sha.outputs.sha }}
steps:
- uses: actions/checkout@v4
- name: Get package version
id: pkg
run: echo "version=$(node -p 'require(\"./package.json\").version')" >> "$GITHUB_OUTPUT"
- name: Get short SHA
id: sha
run: echo "sha=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
- name: Build
run: echo "Building version ${{ steps.pkg.outputs.version }}"
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Deploy
run: |
echo "Version: ${{ needs.build.outputs.version }}"
echo "Commit: ${{ needs.build.outputs.sha-short }}"
Multiple Outputs
A job can have multiple outputs:
jobs:
analyze:
runs-on: ubuntu-latest
outputs:
should-deploy: ${{ steps.check.outputs.deploy }}
environment: ${{ steps.check.outputs.env }}
version: ${{ steps.check.outputs.version }}
steps:
- name: Determine deployment
id: check
run: |
echo "deploy=true" >> "$GITHUB_OUTPUT"
echo "env=production" >> "$GITHUB_OUTPUT"
echo "version=3.0.0" >> "$GITHUB_OUTPUT"
deploy:
needs: analyze
if: needs.analyze.outputs.should-deploy == 'true'
runs-on: ubuntu-latest
steps:
- run: |
echo "Deploying ${{ needs.analyze.outputs.version }}"
echo "Environment: ${{ needs.analyze.outputs.environment }}"
Common Mistakes
| Mistake | Fix |
|---|---|
| Output is empty in downstream job | Make sure the step has an id: and the job has outputs: mapping |
Using set-output command |
Deprecated. Use echo "key=value" >> "$GITHUB_OUTPUT" instead |
| Output value contains newlines | Outputs are single-line. For multi-line data, use artifacts instead |
Missing needs: on consumer job |
The consumer must declare needs: <producer-job> |
Quick Reference
| Concept | Syntax |
|---|---|
| Set step output | echo "key=value" >> "$GITHUB_OUTPUT" |
| Step ID | id: my-step |
| Map to job output | outputs: name: ${{ steps.my-step.outputs.key }} |
| Read in next job | ${{ needs.job-id.outputs.name }} |
| Requires | needs: job-id on the consuming job |
← Previous: Job Dependencies
Next: Container Jobs →