Deploying a PHP project via GitLabs pipeline
I have a Symfony project on GitLab, I wanted to deploy to a server over ssh via deployer - and I found some minor issues with, the guides I found online - so, here is my take on how to go about it.

All deployment steps will be executed on the target server, so the server needs a deployer
user, with sufficient rights to do whatever steps you need it to do.
The steps I need are pretty basic, so I don't have spedial permissions set up on the server.
Setting up the target server
On the server, add the deployer
user.
sudo adduser --disabled-password --create-home deployer
Fill in what ever information you want - or just hit Enter
.
Then generate ssh-keys for that user and add the public key to the users authorized_keys
file - The later is needed for the ssh connection from GitLab.
sudo su - deployer
ssh-keygen -t ed25519 -C "deployer@example.com"
cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys
The php project
deployer.php
Here you need to add deployer as a dev
dependency.
php composer require --dev deployer/deployer
The content of the file will wary a lot - based on your projects needs - but here are a basic example - this one is build on the Symfony recipe.
<?php
namespace Deployer;
require 'recipe/symfony.php';
set('repository', 'git@gitlab.com:xyz/project.example.com.git');
set('application', 'project.example.com');
set('git_tty', true);
set('keep_releases', 3);
set('writable_mode', 'acl');
set('http_user', 'www-data');
set('default_stage', 'prod');
set('allow_anonymous_stats', false);
set('ssh_multiplexing', true);
set('bin/php', function() {
return which('php8.2');
});
set('env', [
'APP_ENV' => 'prod',
]);
host('project.example.com')
->setHostname('project.example.com')
->setRemoteUser('deployer')
->set('identity_file', '/root/.ssh/id_ed25519')
->set('labels', ['stage' => 'prod'])
->set('branch', 'main')
->set('deploy_path', '/var/www/project.example.com');
task('asset-map-compile', function () {
writeln('Compiling asset map');
run('cd {{release_or_current_path}} && {{bin/php}} bin/console asset-map:compile');
});
after('deploy:publish', 'database:migrate');
after('database:migrate', 'asset-map-compile');
after('deploy:failed', 'deploy:unlock');
.gitlab-ci.yml
You also need a .gitlab-ci.yml
file. This file will contain the different steps you want your pipeline to execute.
Mine is set up to only trigger the deployment step when pushing to the main
branch - and only if all tests have passed.
variables:
COMPOSER_ALLOW_SUPERUSER: 1
COMPOSER_DEFAULT_OPTIONS: '--optimize-autoloader --classmap-authoritative --no-progress --no-suggest --prefer-dist'
DOCKER_IMAGE_PHP: 'registry.gitlab.com/xyz/project.example.com:latest'
stages:
- build
- test
- deploy
build:
stage: build
image: ${DOCKER_IMAGE_PHP}
script:
- composer install ${COMPOSER_DEFAULT_OPTIONS}
variables:
APP_ENV: test
artifacts:
paths:
- vendor/
- bin/
phpunit:
stage: test
image: ${DOCKER_IMAGE_PHP}
script:
- php vendor/bin/phpunit
variables:
APP_ENV: test
dependencies:
- build
deploy_prod:
stage: deploy
image: ${DOCKER_IMAGE_PHP}
only:
- main
before_script:
- mkdir ~/.ssh
- chmod 700 ~/.ssh
- cp $DEPLOYER_SSH_KEY ~/.ssh/id_ed25519
- ssh-keyscan -t rsa,ed25519 "${DEPLOYER_SSH_HOST}" >> ~/.ssh/known_hosts
- chmod 600 ~/.ssh/*
- eval $(ssh-agent -s)
- ssh-add ~/.ssh/id_ed25519
script:
- php vendor/bin/dep deploy --revision="${CI_COMMIT_SHA}"
dependencies:
- build
when: on_success
The DEPLOYER_SSH_KEY
and DEPLOYER_SSH_HOST
variables are set as secrets in your pipeline.
Please note that the DEPLOYER_SSH_KEY
is your private key for the deployer user - and must be set up as a File
variable on your project - and when you paste the private key into the input file, you must be sure to end the file with a newline (don't ask)!
The variables can be found under Settings -> CI/CD -> Variables
on the project.

DOCKER_IMAGE_PHP
Yes, you need to build your own docker image to run your build/test/deployment on - and add that one to GitLabs repository.
Note! It is important that the image is named the same as your project - otherwise it will be rejected by GitLab.
Building the image is again up to your needs, but the Dockerfile could look like this.
FROM php:8.2-fpm
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions \
&& install-php-extensions amqp gd zip pdo_mysql igbinary intl
RUN apt-get -y update \
&& apt-get -y --no-install-recommends install git libzip-dev \
zip unzip wget curl rsync openssl openssh-client libicu-dev
COPY --from=composer /usr/bin/composer /usr/bin/composer
You need to log in to registry.gitlab.com in order to upload the image. You use your email and a password/access token to login.
docker login registry.gitlab.com
The Access Token must be set up with the following permissions read_api, read_registry, write_registry
- and it must have an expirey date set.
You can setup Access Tokens under Settings -> Access Tokens

The build and push the image.
docker build -t registry.gitlab.com/xyz/project.example.com .
docker push registry.gitlab.com/xyz/project.example.com
Done and ready - I hope
All the pieces should be in place now - so the next push to the main branch, should tigger the build -> test -> deploy
pipeline.
If any of your tests fail, the pipeline should fail and the deploy step should be skipped. If all tests pass, the the code should be deployed to the server.
Pushes to any other branch will only trigger the build -> test
part of the pipeline.
Credits where credits due
I based my guide on these articles
- https://docs.gitlab.com/ee/ci/pipelines/
- https://dev.to/jszutkowski/how-to-automate-deploys-with-gitlab-ci-cd-and-deployer-28pb
- https://www.digitalocean.com/community/tutorials/how-to-set-up-a-continuous-deployment-pipeline-with-gitlab-on-ubuntu
Some resources on the other tools can be found here
- https://deployer.org/
- https://symfony.com/
- https://github.com/mlocati/docker-php-extension-installer/
··· Ulrik