TL;DR —
actions/upload-artifact@v4saves files for download or for a later job; pair withif: always()so failed runs still cough up debug logs. Use artifacts for human/durable outputs (test reports, dist/, logs), andactions/cachefor speeding up dependency installs — they're not interchangeable.
Artifacts letyou save files from a workflow run and share them between jobs or download them afterward.
- name: Upload test results
uses: actions/upload-artifact@v4
with:
name: test-results # artifact name (shown in the UI)
path: | # one or more paths
test-results.xml
coverage/
logs/*.log
retention-days: 30 # default: 90 days
if-no-files-found: warn # warn | error | ignore- uses: actions/upload-artifact@v4
if: always() # runs even if previous steps failed
with:
name: debug-logs
path: '*.log'Each job gets a fresh VM — use artifacts to pass files between jobs.
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: python -m build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: ./dist # where to put the files locally
- run: ls -la ./dist
- run: ./deploy.sh- uses: actions/download-artifact@v4
# no 'name' → downloads ALL artifacts into separate directories
with:
path: ./all-artifacts# List artifacts for the latest run
gh run view <run-id> --json artifacts --jq '.artifacts[].name'
# Download all artifacts from a run
gh run download <run-id>
# Download a specific artifact
gh run download <run-id> --name test-results --dir ./results
# Download from the latest run on current branch
gh run download- name: Run tests
run: pytest src/ --junitxml=test-results.xml
- uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-py${{ matrix.python-version }}
path: test-results.xml
retention-days: 14# In build job
- uses: actions/upload-artifact@v4
with:
name: dist-${{ steps.version.outputs.tag }}
path: dist/
# In deploy job
- uses: actions/download-artifact@v4
with:
name: dist-${{ needs.build.outputs.version }}
path: dist/- run: pytest src/ --cov=src --cov-report=html
- uses: actions/upload-artifact@v4
with:
name: coverage-report
path: htmlcov/
retention-days: 7| Limit | Value |
|---|---|
| Max artifact size | 10 GB per artifact |
| Max total storage | Depends on GitHub plan |
| Default retention | 90 days |
| Minimum retention | 1 day |
| Maximum retention | 400 days (enterprise) |
| Artifacts | Cache | |
|---|---|---|
| Purpose | Store build outputs to download later | Speed up builds by reusing files |
| Shared between | Jobs in the same run, or downloaded externally | Runs on the same branch |
| Survives after run | Yes (until retention expires) | Yes (until evicted by size limit) |
| Action | upload-artifact / download-artifact |
actions/cache |
| Example | dist/, test results, logs |
~/.cache/pip, node_modules |