In this example we are going to manually run Jenkins pipeline stages against our GitHub repository. When we trigger a build in Jenkins, it will connect to GitHub via SSH and run all the stages listed in the Jenkinsfile which is also stored in the GitHub repository.


Jenkins pulls our dockerised GitHub repository, builds containers, runs all tests in containers, stops containers and clears docker artifacts. That's all!


Jenkins server GitHub SSH integration

Make sure that the SSH key of Jenkins server is added to GitHub repository. Login to Jenkins UI and do the following.

  1. Click "Credentials" link on the left.

  2. Click "Jenkins/Global" and then "Add credentials" link.

  3. Select "SSH Username and private key" from "Kind" option.

  4. Set a username (e.g. GitHub-inanzzz) for "Username" option.

  5. Tick "Enter directly" radio button and paste content of the ~/.ssh/ file into the textarea.

  6. Save and exit.

Allow Jenkins user to execute "docker" commands

First of all make sure that the "docker" and "docker-compose" are installed on the Jenkins server. In addition to that, make sure that the jenkins user can execute $ docker ... and $ docker-compose ... CLI commands. Run $ sudo usermod -aG docker $USER and $ sudo usermod -aG docker jenkins commands. You will need to restart the Jenkins server to let the changes take affect. Note: You can use $ sudo -i -u jenkins command to login as jenkins user on Jenkins server.

Setup "Pipeline" project

In Jenkins's main page, click "New Item" link to create a new item called "Test" and then select "Pipeline" option in the following page.

  1. Select "Pipeline script from SCM" option under "Pipeline" section.

  2. Select "Git" as SCM.

  3. Use for "Repository URL".

  4. Select GitHub-inanzzz from "Credentials".

  5. Optionaly change */master to */develop.

  6. Add ci/pipeline/branch/develop/Jenkinsfile to "Script path".

  7. Save and exit.

If you click "Build Now" link, it should successfuly run the first build and the console output should look like below.

Application structure

├── ci
│   └── pipeline
│   └── branch
│   └── develop
│   └── Jenkinsfile
├── composer.json
├── docker
│   └── ci
│   ├── docker-compose.yml
│   ├── .env
│   ├── Makefile
│   └── php
│   └── Dockerfile
├── src
│   └── Football.php
└── tests
   └── FootballTest.php



pipeline {
agent any

options {

stages {
stage('Checkout SCM') {
steps {
echo '> Checking out the source control ...'
checkout scm
stage('Git Pull') {
steps {
echo '> Pulling the code from GitHub repository ...'
sh 'git remote update && git checkout develop && git pull origin develop'
stage('Docker Up') {
steps {
echo '> Building the docker containers ...'
sh 'cd docker && cd ci && make build'
stage('Composer Install') {
steps {
echo '> Building the application within the container ...'
sh 'cd docker && cd ci && make composer'
stage('Test') {
steps {
echo '> Running the application tests ...'
sh 'cd docker && cd ci && make test'


"name": "inanzzz/game",
"require": {
"php": ">=7.2"
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.13",
"phpstan/phpstan": "^0.11.2",
"phpunit/phpunit": "^7.3"
"autoload": {
"psr-4": {
"App\\": "src/",
"App\\Tests\\": "tests/"
"config": {
"sort-packages": true


version: '3'

context: php
container_name: '${COMPOSE_PROJECT_NAME}_php'
hostname: '${COMPOSE_PROJECT_NAME}-php'
- .env
- ../../:/app:consistent
working_dir: /app




The reason why we have this file is because the Jenkins Pipeline plugin doesn't run $ docker-composer ... commands.

CONTAINER := game_php

@docker-compose up -d

@docker exec -i $(CONTAINER) composer install

@docker exec -i $(CONTAINER) vendor/bin/php-cs-fixer fix src --rules=@PSR2 --using-cache=no --dry-run --verbose --diff
@docker exec -i $(CONTAINER) vendor/bin/phpunit tests
@docker exec -i $(CONTAINER) vendor/bin/phpstan analyse src tests --no-progress --level=max
@make -s down

@docker-compose down --volumes
@make -s clean

@docker system prune --volumes --force


FROM php:7.2-cli-alpine

RUN apk update \
&& apk add --no-cache $PHPIZE_DEPS \
bash \
git \
zip \

RUN curl -sS | php -- --install-dir=/usr/local/bin --filename=composer

RUN rm -rf /var/cache/apk/*

CMD tail -f /dev/null


If you click "Build Now" link, it should successfuly run the first build and the console output should look like below.

Started by user root
Obtained ci/pipeline/branch/develop/Jenkinsfile from git
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/lib/jenkins/workspace/game
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Checkout SCM)
[Pipeline] echo
> Checking out the source control ...
[Pipeline] checkout
using credential 1a41495c-ad85
> git rev-parse --is-inside-work-tree # timeout=10
Fetching changes from the remote Git repository
> git config remote.origin.url # timeout=10
Fetching upstream changes from
> git --version # timeout=10
using GIT_SSH to set credentials
> git fetch --tags --progress +refs/heads/*:refs/remotes/origin/*
> git rev-parse refs/remotes/origin/develop^{commit} # timeout=10
> git rev-parse refs/remotes/origin/origin/develop^{commit} # timeout=10
Checking out Revision 139991 (refs/remotes/origin/develop)
> git config core.sparsecheckout # timeout=10
> git checkout -f 139991
Commit message: "Format"
> git rev-list --no-walk 86e9d7 # timeout=10
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Git Pull)
[Pipeline] echo
> Pulling the code from GitHub repository ...
[Pipeline] sh
+ git remote update
Fetching origin
+ git checkout develop
Previous HEAD position was 139991f... Format
Switched to branch 'develop'
Your branch and 'origin/develop' have diverged,
and have 10 and 1 different commit each, respectively.
(use "git pull" to merge the remote branch into yours)
+ git pull origin develop
* branch develop -> FETCH_HEAD
Merge made by the 'recursive' strategy.
ci/pipeline/branch/develop/Jenkinsfile | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Docker Up)
[Pipeline] echo
> Building the docker containers ...
[Pipeline] sh
+ cd docker
+ cd ci
+ make build
Creating network "game_default" with the default driver
Creating game_php ...
Creating game_php ... [32mdone[0m
[1B[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Composer Install)
[Pipeline] echo
> Building the application within the container ...
[Pipeline] sh
+ cd docker
+ cd ci
+ make composer
Do not run Composer as root/super user! See for details
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Nothing to install or update
Generating autoload files
ocramius/package-versions: Generating version class...
ocramius/package-versions: ...done generating version class
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Test)
[Pipeline] echo
> Running the application tests ...
[Pipeline] sh
+ cd docker
+ cd ci
+ make test
Loaded config default.
Legend: ?-unknown, I-invalid file syntax, file ignored, S-Skipped, .-no changes, F-fixed, E-error

Checked all files in 0.008 seconds, 10.000 MB memory used
PHPUnit 7.5.4 by Sebastian Bergmann and contributors.

. 1 / 1 (100%)

Time: 18 ms, Memory: 4.00MB

OK (1 test, 1 assertion)

[OK] No errors

Stopping game_php ...
Stopping game_php ... [32mdone[0m
[1BRemoving game_php ...
Removing game_php ... [32mdone[0m
[1BRemoving network game_default
Total reclaimed space: 0B
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS