Gitlab - 4.4.2 Writing gitlab pipeline

Now that you know the basic commands and the overall flow needed to run a GitLab CI pipeline, this lesson walks through actually writing a .gitlab-ci.yml for the Node.js application from the previous session. The recommended approach is to create the file locally, push it to a feature branch, run it, and then merge to master. The pipeline will need two jobs: a build job that installs every required dependency, and a deploy job that runs the application.

Build and deploy jobs

The build job must first install NPM on the runner, because the default GitLab environment does not ship with Node. We add the script block to install NPM (the Node Package Manager) and then run npm install at the project root, which reads package.json and pulls every declared dependency into node_modules. The deploy job is responsible for running node index.js. As in the previous lesson, this job must also install Node beforehand, because the GitLab environment is shared and each job starts from a clean slate.

You might wonder why we don't merge both installation steps into the build job. The answer becomes clearer in a moment, once we look at how GitLab schedules jobs. For now, we keep two distinct jobs, commit and push everything except the node_modules folder (which we deliberately exclude since the pipeline will rebuild it):

build_job:
  script:
    - apt-get install npm
    - npm install

deploy_job:
  script:
    - apt-get install npm
    - node index.js

When we push and watch the pipeline run, we discover an important default behavior: GitLab does not care about the order in which jobs are declared in the YAML file. It tries to execute every job in parallel. As a result, the deploy job starts before the build job has finished installing the dependencies, and the pipeline fails because the prerequisites from the build step are not yet in place. To enforce a build-then-deploy order, we need to explicitly group the jobs into stages, which we will explore in the next lesson.

Summary

This lesson teaches you how to write a GitLab CI/CD pipeline by defining build and deploy jobs in a `.gitlab-ci.yml` file. You'll learn that each job must explicitly install Node.js and dependencies, then execute your application. Critically, you'll discover that GitLab runs jobs in parallel by default—which causes failures when jobs depend on each other—and that stages must be defined to control execution order.

Key points

  • Every GitLab pipeline starts with defining jobs in a `.gitlab-ci.yml` file
  • Node.js must be explicitly installed in each job since the GitLab runner lacks it by default
  • The build job installs project dependencies using npm install from package.json
  • The deploy job executes the application (e.g., node index.js after build completes)
  • GitLab executes all jobs in parallel unless you specify an execution order
  • Stages must be defined to enforce sequential job execution and prevent deployment failures

FAQ

Why must we install Node.js in every job if we already installed it in the build job?

Because each job runs in a separate GitLab runner environment that doesn't have Node.js pre-installed. Even the deploy job needs Node.js available, so it must be installed explicitly before running node index.js.

What happens if we don't define stages for our jobs?

GitLab will attempt to run all jobs in parallel. If the deploy job depends on the build job completing first, the pipeline will fail because dependencies won't be installed yet.

Should we commit the node_modules folder to the repository?

No. Since the pipeline installs all dependencies using npm install, committing node_modules is unnecessary and wastes repository space. Use .gitignore to exclude the node_modules folder.