I wanted to do an automatic deployment every time I make a push to the repository. Here is a simple configuration for the GitLab CI/CD pipeline I used at first to build and deploy to the remote server. It builds backend and frontend. Use it for any PHP application, for example, based on Laravel or Symfony frameworks.

Using Gitlab CD you will not need to install Composer and NodeJS on your production machine, because everything will be prepared before deployment.

Prepare webserver

You can use an existing user or create a new one. To create a new user, run the commands below.

1
sudo adduser deployer

Add web server user (e.g. www-data) to the deployer group, so you don’t have to change file permissions every time.

1
sudo usermod -a -G deployer www-data

Create a new directory for the project.

1
sudo mkdir /var/www/your_project

Then change the owner of the folder to the deployer user.

1
sudo chown -R deployer:deployer /var/www/your_project

If you use a config file, you can put it in the new directory. If not, delete cp .env.local $CI_COMMIT_SHA/ command in the configuration below.

Create a new SSH key, if you don’t have it. To do that, log in as a deployer user.

1
su deployer

Then run command to create SSH key. Change email in the example below.

1
ssh-keygen -t ed25519 -C "[email protected]"

Copy content of the public key to the authorized_keys file.

1
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

Copy the private key content to use it on GitLab configuration.

1
cat ~/.ssh/id_rsa

Setup GitLab repository

To enable or disable GitLab CI/CD Pipelines in your project, go to GitLab, then navigate to Settings > General > Visibility, project features, permissions page, then expand the Repository section and enable the Pipelines toggle.

Add environment variables. Go to your project’s Settings > CI/CD and expand the Variables section. Click the Add Variable button.

Add these variables:

  • SSH_PRIVATE_KEY - copied id_rsa from the step above
  • SSH_USER - deployer
  • SSH_HOST - your server IP address
  • PROJECT_FOLDER - any folder name you wanted

Prepare CI/CD project configuration

Create a .gitlab-ci.yml file in the root of the project with the content below. Modify any step if necessary.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
image: bogkonstantin/php-7.4-node-12-debug:latest

stages:
- build
- deploy

build:
stage: build
only:
- master
artifacts:
paths:
- ./
script:
- export APP_ENV=prod
- composer install --no-dev --optimize-autoloader
- yarn install
- yarn encore production
- rm -r node_modules
- mkdir -p var && chmod -R 777 var

deploy:
stage: deploy
only:
- master
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" >> ~/.ssh/config'
script:
- zip -r $CI_COMMIT_SHA.zip .
- scp -p $CI_COMMIT_SHA.zip [email protected]$SSH_HOST:/var/www/$PROJECT_FOLDER/
- ssh [email protected]$SSH_HOST "cd /var/www/$PROJECT_FOLDER/ && unzip -q $CI_COMMIT_SHA.zip -d $CI_COMMIT_SHA && rm $CI_COMMIT_SHA.zip && cp .env.local $CI_COMMIT_SHA/ && ln -sfn $CI_COMMIT_SHA current && exit"

Some explanations

  • image - is a Docker image, created by me. It is a Debian-based image with PHP 7.4, NodeJS 12, Composer 2, npm, and Yarn installed. See Dockerfile here for details
  • stages - only build and deploy here. It is a good idea to add tests as a new stage
  • build - in the build stage we will install composer packages (without dev packages), build frontend and remove node_modules folder to reduce the package size, since we will not use it in production. Change yarn command to npm if you use npm
  • deploy - add the SSH key to the running container, archive built application, upload it to the remote server, unpack it and change symlink to the new build.

That’s it. If you want to test it locally - use Gitlab Runner. Read about it here.
Make a commit and push it to the remote repository.
Don’t forget to change the folder in the Nginx or Apache configuration.