Node.js & npm

This guide covers how to install and manage Node.js and npm on the platform — both locally and in GitLab CI pipelines — including the private Verdaccio registry and common troubleshooting scenarios.

Platform Standard

The platform standard Node.js version is Node 24 (LTS). All platform Docker images use node:24 as their base image.


Local Installation

nvm (Node Version Manager) is the recommended way to install and switch between Node.js versions.

# Install nvm
brew install nvm
# Reload shell
source ~/.zshrc # or ~/.bashrc
# Install the platform-standard Node version
nvm install 24
nvm use 24
# Verify
node --version # v24.x.x
npm --version

Respecting .nvmrc

All platform projects should include a .nvmrc file at the project root pinning the Node version:

24

Registry Architecture

The platform uses two separate registries with distinct roles:

RegistryRoleURL
ArtifactorySource of truth — stores all published internal packageshttps://sharenow.jfrog.io/sharenow/api/npm/npm-local/
VerdaccioProxy only — forwards install requests to Artifactory and the public registryhttps://verdaccio.eks-eu-central-1-private.prod.shared.carloop.dev/

Verdaccio is a read-only proxy. It is used for npm install and related download operations. It does not store packages itself. All package publishing goes directly to Artifactory.


Private npm Registry (Verdaccio)

Verdaccio proxies the public npm registry and Artifactory. All npm install calls on the platform network must go through it.

Registry URL: https://verdaccio.eks-eu-central-1-private.prod.shared.carloop.dev/

Note: The public npmjs.org registry has been cut off from the platform network. Any tool that tries to reach it directly — including npm -g, npx, and corepack — will fail unless the relevant environment variable is set to point at Verdaccio.

Required environment variables

Two separate variables control different parts of the Node.js toolchain. Both must point to the Verdaccio registry:

VariableToolValue
NPM_CONFIG_REGISTRY (or npm_config_registry)npm install, npm -g, npxhttps://verdaccio.eks-eu-central-1-private.prod.shared.carloop.dev/
COREPACK_NPM_REGISTRYcorepack (used to download pnpm, yarn)https://verdaccio.eks-eu-central-1-private.prod.shared.carloop.dev

Set both at the GitLab group level (Settings > CI/CD > Variables) so every pipeline in your group inherits them automatically. They are also needed inside Dockerfile builds — see the Dockerfile section below.

Configuring .npmrc

Every platform project should have an .npmrc at the project root with this content:

package-lock=false
save-exact=true
registry=https://verdaccio.eks-eu-central-1-private.prod.shared.carloop.dev/
SettingValueReason
package-lockfalsePlatform convention — avoids package-lock.json conflicts
save-exacttruePins exact versions for reproducible installs
registryVerdaccio URLRoutes all installs through the internal mirror

Testing registry access

# Verify you can reach the registry
npm ping
# Check which registry npm is using
npm config get registry

If you are off-network (e.g. no VPN), you can temporarily fall back to the public registry:

npm install --registry https://registry.npmjs.org

GitLab CI Pipelines

Publishing packages (Artifactory)

When publishing an npm package through auto-devops, the CI pipeline automatically configures the publish registry to Artifactory. It does this by scoping the registry to your package's scope:

npm config set @<scope>:registry https://sharenow.jfrog.io/sharenow/api/npm/npm-local/

This means:

  • Scoped installs (@<scope>/package) resolve through Artifactory via Verdaccio.
  • npm publish for scoped packages is sent directly to Artifactory, not Verdaccio.
  • You do not need to configure this manually — auto-devops handles it as part of the publish job.

Your package.json must declare the correct scope for this to work:

{
"name": "@your-scope/your-package"
}

Node.js in Dockerfile builds

The standard platform pipeline (container_build_dind) builds your service from a Dockerfile. Use Node 24 as your base:

FROM node:24 AS base
# Route all npm/npx/corepack traffic through the internal Verdaccio registry.
# Both variables are required: NPM_CONFIG_REGISTRY covers npm/npx,
# COREPACK_NPM_REGISTRY covers corepack when downloading pnpm or yarn.
ENV NPM_CONFIG_REGISTRY=https://verdaccio.eks-eu-central-1-private.prod.shared.carloop.dev/
ENV COREPACK_NPM_REGISTRY=https://verdaccio.eks-eu-central-1-private.prod.shared.carloop.dev
WORKDIR /app
COPY package*.json ./
# Use BuildKit cache mount to speed up repeated builds
RUN --mount=type=cache,target=/root/.npm npm install
COPY . .
RUN npm run build

Key points:

  • Both ENV lines must appear before the first RUN npm install / RUN corepack call — Docker processes ENV instructions in order.
  • NPM_CONFIG_REGISTRY covers npm install, npm -g, and npx.
  • COREPACK_NPM_REGISTRY covers corepack enable and any pnpm/yarn download triggered by corepack.
  • The --mount=type=cache flag caches the npm store across Docker builds on the same runner — this requires BuildKit (DOCKER_BUILDKIT=1), which the platform build runner enables by default.

Node.js in script-based jobs

For jobs that run npm directly in the CI script (not inside a Dockerfile), use the node:24 image:

my-job:
image: node:24
tags:
- build-prod-admin
variables:
NPM_CONFIG_REGISTRY: https://verdaccio.eks-eu-central-1-private.prod.shared.carloop.dev/
script:
- npm install
- npm test

Caching node_modules between jobs

GitLab CI caching avoids re-downloading packages on every job:

my-job:
image: node:24
tags:
- build-prod-admin
cache:
key:
files:
- package.json
paths:
- node_modules/
variables:
NPM_CONFIG_REGISTRY: https://verdaccio.eks-eu-central-1-private.prod.shared.carloop.dev/
script:
- npm install
- npm test

Use package.json as the cache key so the cache is invalidated when dependencies change.


Troubleshooting

Wrong Node version

Symptoms: Runtime errors about unsupported syntax, API differences, or version mismatch warnings.

# Check what version is active
node --version
# Switch to the version in .nvmrc
nvm use
# If the version is not installed
nvm install

In CI, always pin the version explicitly using the image: node:24 field or by matching .nvmrc:

before_script:
- node --version # log it for debugging

npm install fails — registry unreachable

Symptoms: ECONNREFUSED, ENOTFOUND, or ERR_INVALID_URL during npm install.

# Check current registry
npm config get registry
# Test connectivity
curl -I https://verdaccio.eks-eu-central-1-private.prod.shared.carloop.dev/
# In CI, verify the NPM_CONFIG_REGISTRY variable is set
echo $NPM_CONFIG_REGISTRY

If the registry is unreachable in a Docker build, ensure:

  • The build runner has network access to the internal registry.
  • The ENV NPM_CONFIG_REGISTRY line is placed before the first RUN npm install.

corepack fails to download pnpm or yarn

Symptoms: corepack errors such as RequestError, ENOTFOUND registry.npmjs.org, or a hang when running pnpm install for the first time after corepack enable.

This happens because corepack uses its own registry variable — NPM_CONFIG_REGISTRY alone is not enough.

Fix — set COREPACK_NPM_REGISTRY at the GitLab group level:

VariableValue
COREPACK_NPM_REGISTRYhttps://verdaccio.eks-eu-central-1-private.prod.shared.carloop.dev

Note: the trailing slash is intentionally omitted for this variable.

In a Dockerfile:

ENV COREPACK_NPM_REGISTRY=https://verdaccio.eks-eu-central-1-private.prod.shared.carloop.dev
RUN corepack enable && corepack prepare pnpm@latest --activate

In a CI script job:

variables:
COREPACK_NPM_REGISTRY: https://verdaccio.eks-eu-central-1-private.prod.shared.carloop.dev
NPM_CONFIG_REGISTRY: https://verdaccio.eks-eu-central-1-private.prod.shared.carloop.dev/
script:
- corepack enable
- pnpm install

Stale or corrupted cache

Symptoms: Packages missing at runtime despite successful install, or Cannot find module errors after switching branches.

# Clear npm cache locally
npm cache clean --force
# Delete node_modules and reinstall
rm -rf node_modules && npm install

In CI, force a cache refresh by changing the cache key or clearing the cache manually in the GitLab UI (Pipeline > Caches).

For Gatsby/Next.js projects, also clear the framework cache:

npm run clean # usually calls gatsby clean or next clean

EACCES permission errors

Symptoms: npm ERR! code EACCES when installing global packages.

This typically happens when npm tries to write to a directory owned by root. When using nvm, global packages are installed into your home directory and this should not occur. If it does:

# Check npm prefix (should point to ~/.nvm/versions/node/...)
npm config get prefix
# If it points to /usr/local, reset it
npm config set prefix ~/.npm-global
export PATH=~/.npm-global/bin:$PATH

Never use sudo npm install -g. Use nvm instead.


package-lock.json conflicts

Symptoms: Git merge conflicts in package-lock.json, or CI fails after a merge.

The platform convention is to disable package-lock.json via .npmrc:

package-lock=false

If a package-lock.json was committed by mistake:

# Remove it and disable future generation
rm package-lock.json
echo "package-lock=false" >> .npmrc
git add .npmrc
git rm --cached package-lock.json

Module version conflicts / peer dependency errors

Symptoms: npm ERR! peer dep missing, ERESOLVE errors.

# Inspect the dependency tree
npm ls <package-name>
# Install with legacy peer deps resolution (last resort — prefer fixing the conflict)
npm install --legacy-peer-deps

Prefer resolving the conflict by aligning package versions rather than suppressing the error with --legacy-peer-deps.


Docker build npm install is slow

Symptoms: npm install takes several minutes on every CI build.

Ensure you are:

  1. Using --mount=type=cache,target=/root/.npm in the Dockerfile RUN instruction (requires BuildKit).
  2. Copying only package.json (and package-lock.json if present) before running npm install — this preserves the Docker layer cache when source files change but dependencies do not:
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm npm install
COPY . . # source files copied after install
RUN npm run build

Quick Reference

TaskCommand / Value
Install Node 24nvm install 24
Use version from .nvmrcnvm use
Check active versionnode --version
Check registrynpm config get registry
Clear npm cachenpm cache clean --force
Reinstall dependenciesrm -rf node_modules && npm install
List installed nvm versionsnvm ls
Set default nvm versionnvm alias default 24
NPM_CONFIG_REGISTRYhttps://verdaccio.eks-eu-central-1-private.prod.shared.carloop.dev/
COREPACK_NPM_REGISTRYhttps://verdaccio.eks-eu-central-1-private.prod.shared.carloop.dev