In the previous section, we registered a group runner. In this lesson, we will implement a simple CI/CD process.

To make the shell script execution more secure, for each project group, we will create a user in the Instance with the same name and only grant execution permission to the build folder for that user. In this case, we need to create a user named ecommerce.
Run with root privileges and create the user ecommerce with the corresponding information:
exit
adduser ecommerce

We will grant sudo privileges to both gitlab-runner and ecommerce users so that when a user uses sudo, they won’t need to enter a password.
Type visudo:
visudo

Add the following lines below root ALL=(ALL:ALL) ALL:
gitlab-runner ALL=(ALL) NOPASSWD:ALL
ecommerce ALL=(ALL) NOPASSWD:ALL

Press Ctrl+X → Y → Enter to save.
We need to install the net-tools utility.
apt install net-tools
We can verify by using the command:
netstat -tlpun
We should see two projects running on port 3000 (frontend) and port 5214 (backend).

net-tools will check if a process for the backend or frontend is running and will kill it during the CI/CD process. If it’s not killed, the application won’t be able to run with the new source code!
Before running CI/CD, we need to check if the gitlab-runner user has the required tools: node, dotnet, pm2 by running:
su gitlab-runner
cd
dotnet --version
node -v
npm -v
pm2 -v
In the backend project, select build → Pipelines Editor → Configure pipeline.

We will add the .gitlab-ci.yml file to the main branch to simulate running the CI/CD pipeline when deploying to production. You can customize it according to your strategy!

In the .gitlab-ci.yml file, we will enter the following:
variables:
USER_PROJECT: "ecommerce"
PATH_PROJECT: "/home/${USER_PROJECT}/${CI_PROJECT_NAME}"
stages:
- build
- deploy
before_script:
- sudo mkdir -p $PATH_PROJECT
build:
stage: build
variables:
GIT_STRATEGY: clone
script:
- dotnet restore
tags:
- group-ecommerce-shell-runner
only:
- tags
deploy:
stage: deploy
variables:
GIT_STRATEGY: none
script:
- sudo cp -rf * ${PATH_PROJECT}
- sudo chown -R ${USER_PROJECT}. ${PATH_PROJECT}
- pid=$(sudo netstat -tulnp | grep :5214 | awk '{print $7}' | cut -d'/' -f1) || true
- if [ -n "$pid" ]; then sudo kill -9 $pid; fi
- sudo su ${USER_PROJECT} -c "cd ${PATH_PROJECT};nohup dotnet run > log_be.txt 2>&1 &"
tags:
- group-ecommerce-shell-runner
only:
- tags
Explanation of the file above:
Variables
Before Running Stages
Stages
clone means the source code will be cloned from the repository.pid variable. If not found, pid will be empty (true prevents an error from occurring).pid variable is not empty (i.e., there’s a process listening on port 5214), that process will be forcefully terminated using the kill -9 command.nohup. The output will be logged to log_be.txt.In the Visualize tab, we can see two stages: build and deploy, with two jobs: build backend and deploy backend.

Commit the changes.

We will see the pipeline has failed.

Click view pipeline.

You see that it says 0 jobs, meaning that when it read the .gitlab-ci.yml file, no jobs were executed. This is because we have an only: tags attribute in the script. It means that jobs will only run when a tag is created.
Now, I want to ensure that when committing to the main branch, no pipeline is executed unless a tag is created. Go to Settings → CI/CD → Runners → Expand → Disable Instance Runner.

Let’s enter the Pipeline Editor and add a line to check, then commit the changes.

There is nothing added beyond the initial pipeline.

Now let’s test the CI/CD pipeline by creating tags.
Select Code → Tags.

Create a new tag.

Enter the name, create from main, and click Create Tag.

Check the pipeline, and you’ll see a new pipeline running. Click to view details.

To see the detailed logs of each command, click on the corresponding stage.

Click on the build backend job.

Then click on the deploy backend job.

Check if the website is still running normally.
The website is still running smoothly.

Go to Pipelines → Pipeline editor → Main → Enter the file → Commit Changes.

In the .gitlab-ci.yml file, enter the following line:
variables:
USER_PROJECT: "ecommerce"
PATH_PROJECT: "/home/${USER_PROJECT}/${CI_PROJECT_NAME}"
stages:
- build
- deploy
before_script:
- sudo mkdir -p $PATH_PROJECT
build:
stage: build
variables:
GIT_STRATEGY: clone
script:
- npm install
tags:
- group-ecommerce-shell-runner
only:
- tags
deploy:
stage: deploy
variables:
GIT_STRATEGY: none
script:
- sudo cp -rf * ${PATH_PROJECT}
- sudo chown -R ${USER_PROJECT}. ${PATH_PROJECT}
- pid=$(sudo netstat -tulnp | grep :3000 | awk '{print $7}' | cut -d'/' -f1) || true
- if [ -n "$pid" ]; then sudo kill -9 $pid; fi
- sudo su ${USER_PROJECT} -c "cd ${PATH_PROJECT}; pm2 start npm --name '${CI_PROJECT_NAME}' -- run 'start'"
tags:
- group-ecommerce-shell-runner
only:
- tags
Then create tags and see the pipeline run successfully.



Then check if the backend is still running normally.
