Git, CI/CD & GitOpsXXXVIII · CI ArchitectureCI Architecture
Artifacts, caches, and outputs — three distinct mechanisms for moving data through a job
What you'll learn
- Distinguish artifacts, caches, and outputs by lifetime, scope, and purpose
- Identify the correct use case for each mechanism
- Apply actions/upload-artifact@v4 and actions/download-artifact@v4 to move build outputs between jobs
- Apply actions/cache@v4 to speed up repeated dependency downloads
- Recognise the failure modes of treating a cache as an artifact or an artifact as a cache
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
A CI job has three distinct mechanisms for moving data through its lifetime, and they are not interchangeable. Artifacts are durable blobs the workflow produces and may need to download later. Caches are reusable payloads the workflow fetches on every run to avoid repeating expensive work. Outputs are structured values a step returns to the job and the workflow. Treating a cache as an artifact wastes storage. Treating an artifact as a cache loses the audit trail. Treating an output as an artifact fails because outputs do not survive the job.
The three mechanisms at a glance
flowchart TB
subgraph AR["Artifact (durable, audited, downloadable)"]
A1["terraform plan file"]
A2["container image tarball"]
A3["SBOM"]
A4["ansible-playbook artefact"]
end
subgraph CA["Cache (reusable, key-addressable, best-effort)"]
C1["pip download cache"]
C2["node_modules"]
C3["maven local repo"]
C4["go module cache"]
end
subgraph OU["Output (per-job, structured, ephemeral)"]
O1["image_tag from build step"]
O2["plan_id from plan step"]
O3["deploy_sha from deploy step"]
end
| Mechanism | Lifetime | Scope | Purpose |
|---|---|---|---|
| Artifact | Workflow run (days to months) | Same workflow run, cross-job | Move build outputs to other jobs or humans |
| Cache | Days (key-matched) | Same workflow, any future run | Skip redundant work on subsequent runs |
| Output | Job (seconds to minutes) | Same job, downstream steps | Pass structured values from one step to the next |
The three are layered: outputs flow within a job, artifacts flow across jobs in the same workflow, caches flow across runs.
Artifacts: durable, auditable, downloadable
An artifact is a blob the workflow uploads to the control plane’s artifact store. Other jobs in the same workflow can download it; humans with the right permissions can download it. The artifact is bound to the run ID and the commit SHA; it is auditable and it survives the runner:
steps:
- name: Build plan
run: terraform plan -out=tfplan
- name: Upload plan
uses: actions/upload-artifact@v4
with:
name: terraform-plan
path: tfplan
retention-days: 30
The actions/upload-artifact@v4 step uploads the tfplan
file. Another job in the same workflow can download it with
actions/download-artifact@v4:
steps:
- name: Download plan
uses: actions/download-artifact@v4
with:
name: terraform-plan
path: ./plan
- name: Apply plan
run: terraform apply -input=false ./plan/tfplan
The artifact is the mechanism for moving a terraform plan
from the plan job to the apply job. It is also the mechanism
for humans to download the plan and review it.
Artifacts are appropriate for: Terraform plan files, container image tarballs, SBOMs and attestations, test reports.
Artifacts are inappropriate for: dependencies that should be
cached (a pip cache belongs in actions/cache), ephemeral
build outputs that should never leave the runner.
Caches: reusable, key-addressable, best-effort
A cache is a payload keyed by a hash the workflow computes (typically the lockfile’s hash). The runner checks the cache store for a matching key. If present, the runner restores it. If absent, the runner does the work and uploads the result for next time:
steps:
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: pip-${ hashFiles('requirements.txt') }
restore-keys: |
pip-
- name: Install dependencies
run: pip install -r requirements.txt
The cache is best-effort: a missing cache is a cache miss, not a failure. The workflow falls back to doing the work. This is the correct semantic for dependencies: a build that fails because the cache is missing has coupled correctness to performance.
Caches are appropriate for: language dependency caches (pip,
npm, maven, go modules, composer, bundle), Docker
layer caches for self-hosted runners, build caches that speed
up subsequent runs.
Caches are inappropriate for: build outputs that must be
present for the job to succeed (the terraform plan file is
required for terraform apply; a cache miss would cause the
apply to fail), audit-relevant artefacts (SBOMs, signed plans).
Outputs: ephemeral and structured
An output is a structured value a step returns via
$GITHUB_OUTPUT. The job’s subsequent steps can read it via
${ steps.step_id.outputs.key }. Outputs do not survive the
runner; they do not appear in other workflows; they are not
auditable. They are the right mechanism for “step A discovers
the image tag, step B uses it” within a single job (covered
in detail in lesson XXXVIII-05).
How they interact
flowchart LR
subgraph J1["Plan job"]
S1["step: terraform plan"] --> S2["step: write image_tag to $GITHUB_OUTPUT"]
S1 --> S3["step: upload tfplan as artifact"]
end
subgraph J2["Apply job"]
S4["step: download tfplan artifact"] --> S5["step: terraform apply"]
S4 --> S6["step: use image_tag output"]
end
CACHE["Cache store\n(pip, go modules, maven)"]
J1 -.miss/hit.-> CACHE
J2 -.miss/hit.-> CACHE
- Caches speed up dependency installation in both jobs.
- Artifacts move the
tfplanfrom the plan job to the apply job. - Outputs move the
image_tagfrom the discovery step to the consumption step within each job.
A failure in the artifact store (the tfplan did not upload)
breaks the apply job. A failure in the cache store (the pip
cache is missing) is a cache miss; the workflow installs from
the registry. A failure in the output (the step did not write
to $GITHUB_OUTPUT) breaks only the consuming step, only
within the same job.
Cache eviction and retention
- Artifacts are retained for a configurable period
(
retention-days: 30is common; maximum is vendor policy, often 90 days). Available for download by anyone with workflow read permission for the entire retention window. - Caches are retained until they are evicted. The eviction policy is “least recently used” and is not user-controllable. A cache not accessed in weeks may be evicted. A miss after eviction is non-fatal.
Anything that must be retrievable in six months is an artifact (with appropriate retention) or is stored elsewhere entirely (S3, internal artifact registry, Terraform Cloud’s plan storage). A cache is a performance optimisation, not a record.
Production discipline
- Use artifacts for any blob that must be present for a
downstream job or human. Set
retention-daysexplicitly; do not rely on the default. - Use caches for any work that is expensive to repeat and safe to skip. Key the cache by the lockfile hash so changes invalidate the cache automatically.
- Use outputs for structured values that flow between steps in the same job. Outputs are not storage.
- Never put secrets in caches or artifacts. Both stores are accessible to anyone with workflow read permission.
- Document which mechanism each path uses. A future maintainer should identify why a value is in an artifact versus a cache versus an output without re-deriving the design.
Cross-course references
- Linux for Production Sysadmins - Part XXXIV (ConfigMgmt) applies the same three-mechanism model to package builds.
- Ansible for Production Sysadmins - Part XXXVII (RepoArch) applies the same model to molecule runs.
- Terraform for Production Sysadmins - Parts IX-XII (State) apply the strictest version: Terraform Cloud’s plan storage is artifact-like, separate from the dependency cache.
Quiz
Knowledge check · 4 questions
Q1. A pipeline stores its `terraform plan` output in `actions/cache@v4` rather than `actions/upload-artifact@v4`. What is the failure mode?
Q2. Putting AWS credentials in an artifact's metadata is acceptable because the artifact store is encrypted at rest.
Q3. Name the three mechanisms for moving data through a CI job and identify which is best-effort and which is durable and auditable.
Q4. Redesign a pipeline that confuses caches and artifacts so that the plan-and-apply flow is durable, the dependency installation is fast, and no sensitive data crosses into either store.
Team T's pipeline stores the `terraform plan` output in `actions/cache@v4` keyed by the commit SHA. It stores `pip` dependencies in `actions/upload-artifact@v4` keyed by `requirements.txt`. The apply job downloads the plan from the cache; the install job downloads pip from the artifact. The apply job fails one in four runs because the cache is evicted; the pip artifact grows unbounded across many workflows.
Passing score: 75%. Answers are checked in this browser.