Pages

Monday, 24 November 2025

How to run Node and npm via Docker

 

We sometimes don't want to pollute our local machine by installing Node if we don't use it often. In this scenario we can run a desired version of Node via Docker container:

docker run --rm \
  node:16-alpine \
  sh -c "node --version"

Output:

v16.20.2


This also mean that we can use Node tools against our local Node application repository, without the need to install Node locally:

docker run --rm \
  -v "$PWD":/app \
  -w /app \             
  node:16-alpine \
  sh -c "npm install && npm audit"


The above command should be run from the project's root directory.

If package.json lists some dependencies from a private package hosted on GitHub Packages e.g.:

  "dependencies": {
    ...
    "@foo/bar": "^0.5.4",
    ...
  } 

...and inside Docker there is no GitHub token, npm install might throw this error if npm can't be authenticated against GitHub:

npm ERR! 401 Unauthorized - GET https://npm.pkg.github.com/download/@foo/bar/0.5.4/db46279e9b10a74cec83b15ac06422c479e4d193fd3c8366c839ace085244c9b - authentication token not provided

This token is GitHub Personal Access Token (PAT) and it should have a permission to read our private npm package.

If our local machine is authenticating via GitHub CLI (gh) we can run:

gh auth token

Output is PAT that npm can use and it will be in this format:

ghp_xxx...

We can store this value in the local env variable:

export NODE_AUTH_TOKEN=$(gh auth token)

We now need to create a local .npmrc file which contains authentication 

echo "@foo:registry=https://npm.pkg.github.com/" > .npmrc
echo "//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}" >> .npmrc

...so .npmrc file will look like this:

@foo:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=ghp_aAT3B3N...iiOO

If we try to execute npm install again, the issue with missing token should be resolved now.


---

How to run TypeScript compiler (tsc) via Docker

 

TypeScript compiler, often referred to as tsc , is responsible for compiling TypeScript code into JavaScript. It takes TypeScript source files as input and generates equivalent JavaScript files that can run in any JavaScript environment, ensuring compatibility with browsers.

If for any reason we don't want to install TypeScript compiler on our machine but want to use it to check the TypeScript syntax in the project, we can run in from a Docker container.

Let's first formulate a command which only checks the syntax:

tsc \
   --project ./tsconfig.json \
   --noEmit \
   --skipLibCheck

--project ./tsconfig.json - specifies the project configuration file (we can also use the short version of --project which is -p). tsc will compile only files included by that config.

--noEmit - TypeScript performs type-checking only, but does not output any .js, .d.ts, or build artifacts. Useful for: CI validation, pre-commit checks, linting purely for types, speeding up checks when we don’t care about compiled output.

--skipLibCheck - Tells TS not to check types inside node_modules or .d.ts libraries. This makes type checking much faster and avoids irrelevant type errors from dependencies. It skips: DefinitelyTyped typings, node_modules/*/*.d.ts, any imported library declaration file.


Before running TypeScript compiler, we need to install project's dependencies by running:

npm install

This is because:
  • This installs tsc (if package.json lists typescript among devDependencies, which it should in our case)
  • tsc must load types from our dependencies (@types/..., or any .d.ts shipped by packages).
  • Without node_modules, TypeScript cannot resolve imports like:
                import express from "express";

          and type-checking will fail.


If we have Node installed locally, we can run npm install before running Docker but otherwise, we can just invoke npm from the Docker image (npm install will install tsc):

docker run --rm \
  -v "$PWD":/app \
  -w /app \
  node:20-alpine \
  sh -c "npm install && npx tsc -p ./tsconfig.json --noEmit --skipLibCheck --listFiles --diagnostics"

-v "$PWD":/app - mounts our current directory to the container
-w /app - sets the working directory
node:20-alpine - lightweight Node image
npm install && npx tsc ... - installs project dependencies and runs TypeScript from our local node_modules


I also added --listFiles and --diagnostics flags, so tsc outputs something even if checks of all files pass as otherwise it will not emit any message.


---