Node.js GitHub Actions Workflow Template
Last updated
This is a complete .github/workflows/ci.yml for a Node.js project using npm. It installs dependencies from a cache, runs lint, build and tests on two Node versions, and cancels superseded pull request runs. Copy it, adjust the scripts to match your package.json, and commit.
The workflow
name: CI
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
concurrency:
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
node: [20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
- run: npm run lint --if-present
- run: npm run build --if-present
- run: npm test
What the non-obvious parts do
permissions: contents: readlimits the automaticGITHUB_TOKENto reading the repo. Add more scopes per job only when needed.concurrencygives each pull request its own group. A new push cancels the older run. Pushes tomainare not cancelled, so every commit on main gets a result.timeout-minutes: 15stops a hung test from using the six-hour default. Set it to about twice your normal run time.fail-fast: falselets both Node versions finish, so you see every failure, not just the first.cache: npmmakessetup-nodecache npm's download folder (notnode_modules), keyed onpackage-lock.json.npm cithen installs from local tarballs. Details in caching npm in GitHub Actions.npm ciinstalls exactly what the lockfile says and fails if it is out of sync. Use it instead ofnpm installin CI.--if-presentskips scripts you have not defined, so the template works on small projects.
Variants
pnpm or Yarn
Switch the cache option and install command. For pnpm, add pnpm/action-setup@v4 before setup-node and set cache: pnpm. See caching pnpm and caching Yarn.
Monorepo
Run tasks through your build tool and pass a remote cache so unchanged packages are skipped. See CI for Nx and Turborepo monorepos.
Add a deploy job
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- run: ./scripts/deploy.sh
Use OIDC for cloud credentials instead of stored keys; see the OIDC glossary entry.
macOS or Windows
Add them to the matrix with os: [ubuntu-latest, macos-latest] and runs-on: ${{ matrix.os }}. Use them only if you ship to those platforms, since they cost more per minute.
Run it on faster machines
If your tests are the slow part, change one line and keep everything else:
runs-on: [self-hosted, Linux, compiler-ci]
compiler.dev runs the job on a fresh 8 vCPU / 16 GB machine. If it ever cannot, the job falls back to your original runner. See pricing and how to speed up GitHub Actions.
FAQ
Should I cache node_modules?
Usually no. Caching npm's download cache and running npm ci is safer, because node_modules can hold platform-specific binaries and npm ci deletes it anyway.
Which Node versions should I test?
The active LTS lines you support. Check the Node.js release schedule and drop versions that have reached end of life.
How do I run tests in parallel?
Use your test runner's workers locally and test sharding across machines. Jest and Vitest both support --shard.
Why is my cache not restoring?
Usually the lockfile path is wrong or you are on a different branch scope. Read about cache keys.
Related
- .NET GitHub Actions Workflow TemplateCopy-paste GitHub Actions CI for .NET with NuGet caching, restore, build and test steps, a SQL Server service option, concurrency and permissions.
- Django GitHub Actions Workflow TemplateCopy-paste GitHub Actions CI for Django with a Postgres service container, pip caching, migrations check and tests, plus concurrency and permissions.
- Docker Build GitHub Actions Workflow TemplateCopy-paste GitHub Actions workflow to build Docker images with Buildx, GitHub cache layers and a push to GHCR on main, with minimal permissions.
- Elixir GitHub Actions Workflow TemplateCopy-paste GitHub Actions CI for Elixir and Phoenix with a Postgres service, deps and _build caching via erlef/setup-beam, plus format and tests.