GitLab CI/CD: Architecting, Deploying, and Optimizing Pipelines

Optimization Security and Monitoring

Extends Reuse Configuration NodeJS Jobs

Learn how to eliminate duplication in your GitLab CI/CD pipelines by reusing configuration with the extends keyword. This guide covers three primary strategies:

MethodDescriptionExample
extendsInherit settings from hidden jobs (templates).rspecrspec 1
YAML anchors & aliasesReuse blocks of config (scripts, variables, etc.)&default_scripts / *default_scripts
Reference tagsShare tags or other fields across multiple jobstags: &common_tags / tags: *common_tags

Note

You can combine include with extends to pull in templates from external files and inherit from them in one step.

1. Anchors & Aliases

Use YAML anchors to define reusable snippets:

.default_scripts: &default_scripts
  - ./default-script1.sh
  - ./default-script2.sh

job1:
  script:
    <<: *default_scripts

2. Using extends

Define hidden jobs (prefixed with a dot) as templates, then inherit from them:

.tests:
  rules:
    - if: $CI_PIPELINE_SOURCE == "push"

.rspec:
  extends: .tests
  script:
    - rake rspec

rspec 1:
  extends: .rspec
  variables:
    RSPEC_SUITE: '1'

rspec 2:
  extends: .rspec
  variables:
    RSPEC_SUITE: '2'

GitLab supports up to 11 inheritance levels, but keeping it under three levels is recommended:

Warning

Deep inheritance trees can become hard to maintain. Aim for 2–3 levels only.

Multi-template Inheritance

You can merge multiple hidden jobs:

.only-important:
  variables:
    URL: "http://my-url.internal"
    IMPORTANT_VAR: "details"
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - if: $CI_COMMIT_BRANCH == "stable"
  tags:
    - production
  script:
    - echo "Hello world!"

.in-docker:
  image: alpine
  variables:
    URL: "http://docker-url.internal"
  tags:
    - docker

rspec:
  extends:
    - .only-important
    - .in-docker
  variables:
    GITLAB: "is-awesome"
  script:
    - rake rspec

3. Refactoring NodeJS Test Jobs

Imagine a pipeline where the test stage contains two nearly identical NodeJS jobs: unit_testing and code_coverage. To speed up feedback, other stages are commented out.

The image shows a GitLab pipeline editor with a visual representation of a CI/CD pipeline, including stages like testing, containerization, and deployment.

Both jobs share the following configuration:

stage: test
image: node:17-alpine3.14
services:
  - name: siddharth67/mongo-db:non-prod
    alias: mongo
    pull_policy: always
variables:
  MONGO_URI: 'mongodb://mongo:27017/superData'
  MONGO_USERNAME: non-prod-user
  MONGO_PASSWORD: non-prod-password
cache:
  key:
    files:
      - package-lock.json
    prefix: node_modules
  policy: pull-push
  when: on_success
  paths:
    - node_modules
before_script:
  - npm install

3.1 Create a Hidden Template

Extract the shared configuration into a single hidden job:

.prepare_nodejs_environment:
  image: node:17-alpine3.14
  services:
    - name: siddharth67/mongo-db:non-prod
      alias: mongo
      pull_policy: always
  variables:
    MONGO_URI: 'mongodb://mongo:27017/superData'
    MONGO_USERNAME: non-prod-user
    MONGO_PASSWORD: non-prod-password
  cache:
    policy: pull-push
    when: on_success
    paths:
      - node_modules
    key:
      files:
        - package-lock.json
      prefix: node_modules
  before_script:
    - npm install

3.2 Refactor Job Definitions

Have both jobs inherit from the .prepare_nodejs_environment template:

unit_testing:
  stage: test
  extends: .prepare_nodejs_environment
  script:
    - npm test
  artifacts:
    when: always
    expire_in: 3 days
    name: Moca-Test-Results
    paths:
      - test-results.xml
    reports:
      junit: test-results.xml

code_coverage:
  stage: test
  extends: .prepare_nodejs_environment
  script:
    - npm run coverage
  artifacts:
    name: Code-Coverage-Report
    when: always
    expire_in: 3 days
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
  coverage: /All files[^|]*\|[^|]*\s+(\d+\.\d+)/
  allow_failure: true

4. Inspecting the Merged Configuration

GitLab’s CI Lint or Full configuration view will show the merged result for unit_testing:

unit_testing:
  image: node:17-alpine3.14
  services:
    - name: siddharth67/mongo-db:non-prod
      alias: mongo
      pull_policy: always
  variables:
    MONGO_URI: mongodb://mongo:27017/superData
    MONGO_USERNAME: non-prod-user
    MONGO_PASSWORD: non-prod-password
  cache:
    policy: pull-push
    when: on_success
    paths:
      - node_modules
    key:
      files:
        - package-lock.json
      prefix: node_modules
  before_script:
    - npm install
  script:
    - npm test
  artifacts:
    paths:
      - test-results.xml

5. Pipeline Execution

When you commit these changes, both unit_testing and code_coverage run in parallel with the shared setup:

$ npm install
$ npm test
# …
Job succeeded

This approach centralizes maintenance: updating .prepare_nodejs_environment applies to all linked jobs.


For more on merging hidden jobs or combining include with extends, see the GitLab CI/CD documentation on extends.

Watch Video

Watch video content

Previous
Optimize CICD configuration files