Caching and Artifacts in GitHub Actions: Speed Up Your CI/CD Workflows

Continuous Integration (CI) and Continuous Deployment (CD) pipelines are amazing — they automate building, testing, and deploying your applications. But if you run the same workflow repeatedly, some steps like installing dependencies or downloading tools can slow things down.

This is where caching and artifacts come into play. They help speed up workflows and share files between jobs.


1. What is Caching in GitHub Actions?

Caching is like storing reusable resources. Instead of downloading dependencies every time your workflow runs, you save them in a cache, and next time you can restore it.

Think of it as saving your homework: you don’t redo it every time — you just pick it up from last time.

Why Cache?

  • Reduce workflow run time.

  • Avoid repeated network calls.

  • Save bandwidth and improve developer productivity.


2. Using Node.js Cache (Simple Way)

GitHub Actions has a built-in caching option for Node.js:

- uses: actions/setup-node@v3
  with:
    node-version: 22.x
    cache: 'npm'

What happens here?

  1. GitHub sets up Node.js (v22.x in this example).

  2. It checks if a cache exists for your npm dependencies (based on package-lock.json).

  3. If the cache exists, it restores node_modules — workflow runs faster.

  4. If the cache doesn’t exist, it installs dependencies and creates a cache for future runs.


Visual Diagram: Node.js Caching (Shorthand)

[GitHub Runner]
      |
      |---> Setup Node.js (v22.x)
      |
      |---> Check npm cache
      |       |
      |       |---> Cache found --> Use cached node_modules
      |       |---> Cache not found --> Run npm install
      |                                     |
      |                                     --> Save cache for next run
      |
[Workflow continues...]

3. Manual Cache Using actions/cache

Sometimes you want full control over what gets cached. You can use the actions/cache action:

- name: Cache npm dependencies
  uses: actions/cache@v3
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

How it works

  1. GitHub checks if a cache exists with the key you provided.

  2. If yes, it restores the cached folder (~/.npm) to speed up installation.

  3. If no, it runs npm install and saves the cache for the next workflow run.


Visual Diagram: Manual Cache

[GitHub Runner]
      |
      |---> Check cache key
      |       |
      |       |---> Cache found --> Restore ~/.npm
      |       |---> Cache not found --> Run npm install
      |                                     |
      |                                     --> Save to cache
      |
[Workflow continues...]

4. Artifact: Sharing Files Between Jobs

Caching is temporary and scoped to workflow runs, while artifacts let you store and share files between jobs.

Use Cases

  • Sharing build outputs (.jar, .zip, .tar.gz) between jobs.

  • Sharing test reports (JUnit, Coverage) between steps.

  • Archiving logs for debugging failed jobs.

How to Create Artifacts

- name: Upload build artifact
  uses: actions/upload-artifact@v3
  with:
    name: build-files
    path: build/

How to Download Artifacts in Another Job

- name: Download build artifact
  uses: actions/download-artifact@v3
  with:
    name: build-files
    path: ./build

Visual Diagram: Artifact Workflow

[Job 1: Build]
      |
      |---> Build project
      |
      |---> Upload artifact (build/)
      |
[Job 2: Test/Deploy]
      |
      |---> Download artifact (build/)
      |
      |---> Run tests or deploy

5. Combining Cache and Artifacts

Many workflows use both:

  • Cache: Save dependencies (npm, yarn, .m2, pip) → speed up install time.

  • Artifacts: Save build outputs, logs, and reports → share between jobs.

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 22.x
          cache: 'npm'
      - run: npm install
      - run: npm run build
      - name: Upload build artifact
        uses: actions/upload-artifact@v3
        with:
          name: build-files
          path: build/

  deploy:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - uses: actions/checkout@v3
      - name: Download build artifact
        uses: actions/download-artifact@v3
        with:
          name: build-files
      - run: echo "Deploying build..."

Result:

  • Job build caches Node.js dependencies.

  • Job deploy reuses cached Node.js dependencies and downloads build outputs for deployment.


6. Tips and Best Practices

  1. Use Cache for Dependencies, Artifacts for Outputs.

  2. Use meaningful cache keys, based on lockfiles (package-lock.json, yarn.lock).

  3. Limit artifact size; GitHub limits artifacts to 10GB per repository.

  4. Clean cache periodically for outdated dependencies.


Summary Table

FeatureScopeUse CaseLifetime
CacheWorkflow runDependencies (npm, pip, etc.)Auto-managed by GitHub, can expire
ArtifactJob-to-job sharingBuild outputs, logs, reportsStored until deleted (90 days by default)

Conclusion

Caching and artifacts are key features in CI/CD:

  • Caching: Speed up repeated tasks like dependency installation.

  • Artifacts: Share files between jobs or store build results.

By combining both, your CI/CD pipelines can become faster, more reliable, and efficient.

0
Subscribe to my newsletter

Read articles from Muhammad Sufiyan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Muhammad Sufiyan
Muhammad Sufiyan

As a former 3D Animator with more than 12 years of experience, I have always been fascinated by the intersection of technology and creativity. That's why I recently shifted my career towards MERN stack development and software engineering, where I have been serving since 2021. With my background in 3D animation, I bring a unique perspective to software development, combining creativity and technical expertise to build innovative and visually engaging applications. I have a passion for learning and staying up-to-date with the latest technologies and best practices, and I enjoy collaborating with cross-functional teams to solve complex problems and create seamless user experiences. In my current role as a MERN stack developer, I have been responsible for developing and implementing web applications using MongoDB, Express, React, and Node.js. I have also gained experience in Agile development methodologies, version control with Git, and cloud-based deployment using platforms like Heroku and AWS. I am committed to delivering high-quality work that meets the needs of both clients and end-users, and I am always seeking new challenges and opportunities to grow both personally and professionally.