Gitlab Continuous Integration npm Background Process

Here is an example of running a process in the background using the systemd service manager.

In this example, Gitlab CI/CD will deploy a React web app in an HTTP server running on Ubuntu.

Step 1: On the runner, create a service unit file

vi /etc/systemd/system/hello-react.service

[Unit]
Description=Hello React service
After=network.target
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=always
RestartSec=1
User=gitlab-runner
ExecStart=npx http-server -p 3000 /home/gitlab-runner/hello-react/

[Install]
WantedBy=multi-user.target

Step 2: On the runner, grant the sudo permission to user gitlab-runner with no password restriction.

$ sudo usermod -a -G sudo gitlab-runner
$ sudo visudo

Now add the following line to the bottom of the file

gitlab-runner ALL=(ALL) NOPASSWD: ALL

Step 3: In your code repo, create deploy.sh

#!/bin/bash

echo "Build the app for production"
npm run build

echo "Copy over the build"
rm -fr /home/gitlab-runner/hello-react/*
cp -r build/* /home/gitlab-runner/hello-react/

echo "Running server in the background"
sudo systemctl restart hello-react

echo "HTTP server started."

Step 4: In your code repo, update .gitlab-ci.yml

image: node:latest

stages:
    - build
    - test
    - deploy

cache:
    paths:
        - node_modules/

install_dependecies:
    stage: build
    script:
        - npm install

    artifacts:
        paths:
            - node_modules/
run_unit_tests:
    stage: test
    script:
        - npm test

deploy_app:
    stage: deploy
    script:
        - bash -c './deploy.sh'

That's it. The CI/CD job will finish without halting. The app will be deployed, and you can visit it at http://your-server-ip:3000


According to Tomasz Maczukin on a related GitLab issue:

I think the best solution would be to use some service manager (systemd, runit, upstart, sysv - whatever is present on this system).

On the server you should prepare configuration file to start the service. Then in CI job you would do e.g. systemctl start tomcat. This command is expected to exit just after calling and it's service manager (outside of Runner's scope) who starts the process.

Process started with Runner, even if you add nohup and & at the end, is marked with process group ID. When job is finished Runner is sending kill signal to whole process group. So any process started directly from CI job will be terminated at job end. Using service manager you're not starting the process in context of Runner's job. Your only notifying a manager to start a process using prepared configuration :)