Skip to main content
In this lesson we’ll demonstrate how to run CI jobs inside a Docker image that already contains the required tooling (for example, Node.js). Using a prebuilt image avoids installing dependencies at runtime and makes pipelines faster and more reliable. If a job runs without specifying a Node image, the runner uses the default image (often Ruby), and Node will not be available:
Console output from such a job shows the Docker executor using the default image and then failing because Node is not installed:
Options to make Node available to your job:
  • Manually install Node.js in the job (works, but slow and error-prone).
  • Use a Docker image that already contains Node.js (recommended).
Table: Comparison of approaches

Installing Node.js inside a job (before_script)

You can install Node.js inside a job using the NodeSource installer. In many CI containers you run as root, so sudo is not required. Use a YAML literal block (|) to keep the multi-line shell script readable and executed as one script. Example: shell commands to install Node.js (one-time reference)
Example GitLab CI job that performs the install in before_script (note the YAML literal block):
Console excerpt after a successful install (shows Node/npm versions):
Installing packages inside the job works, but every job runner will repeat these steps, which increases job runtime. Prefer using an image that already contains the tools you need.

Use the image keyword to run the job in a Node.js container

A more efficient approach is to run the job inside a container image that already includes Node.js. You can set an image globally under default: or per-job with the image: keyword. The GitLab CI YAML reference documents these options and related settings (pull policy, entrypoint, etc.).
A browser screenshot of the GitLab Docs page titled "CI/CD YAML syntax reference," showing navigation menus on the left, the main documentation content in the center, and a keyword index on the right.
Example: use a Node.js image at the job level (fast and simple):
When using the official Node image, the runner pulls the image and Node/npm are immediately available. This typically results in much faster jobs because you skip the installation steps. Console excerpt showing a fast success using the Node image:
This is the same behavior shown in the pipeline UI when the job uses the Node image:
A GitLab CI pipeline page titled "Exploring Gitlab CI Concepts" showing a passed pipeline with a green check and a successful "deploy-job" box, plus the project's left navigation sidebar.
Quick tips
  • Use official images (for Node: node:<version>-alpine or node:<version>) when available for smaller images and consistent tooling.
  • If your project needs extra packages, create a custom image with a Dockerfile and publish it to a registry.
  • Set image: under default: if multiple jobs share the same runtime to reduce repetition.

Summary

  • Prefer using a Docker image that already contains the required language runtime (use the image: keyword).
  • Installing software in before_script is possible (use YAML literal blocks for multi-line scripts) but is slower and duplicates effort across runners.
  • You can set image: per-job or globally under default: depending on your pipeline needs.
Links and references

Watch Video