diff --git a/.ci/build.bash b/.ci/build.bash
index 5caa35e1ac5f4f868b9c81a150a4b525d46d9094..431667a72af897dfc10ed14d3a43e9ce2bf27a4c 100644
--- a/.ci/build.bash
+++ b/.ci/build.bash
@@ -6,25 +6,33 @@ set -x
 whoami
 env | sort
 
+if [ -z "${HBP}" ]; then
+    echo "USAGE: The HBP variable not specified"
+    exit 1
+fi
+
 if [ -z ${PYTHON_VERSION_MAJOR_MINOR} ]; then
     export PYTHON_VERSION_MAJOR=$(python -c "import sys; print(sys.version_info.major)")
     export PYTHON_VERSION_MAJOR_MINOR=$(python -c "import sys; print('{}.{}'.format(sys.version_info.major, sys.version_info.minor))")
 fi
 
 # Force the build to happen in the build workspace
-export HBP=$WORKSPACE
-cd $WORKSPACE
+cd $HBP
 export PYTHONPATH=
 
-cd ${WORKSPACE}/${VIRTUAL_COACH_DIR}
+VIRTUAL_COACH_DIR=${GIT_CHECKOUT_DIR:-"VirtualCoach"}
+
+cd ${HBP}/${VIRTUAL_COACH_DIR}
 
 
 # Configure build
-export VIRTUAL_ENV_PATH="${VIRTUAL_ENV}"
 export NRP_INSTALL_MODE=dev
-export PYTHONPATH=hbp_nrp_virtual_coach:${VIRTUAL_ENV_PATH}/lib/python${PYTHON_VERSION_MAJOR_MINOR}/site-packages:$PYTHONPATH
+export PYTHONPATH=hbp_nrp_virtual_coach:$PYTHONPATH
 
 # Build
 export IGNORE_LINT='platform_venv|config_files|examples/integration_test/test_experiment_folder'
-make devinstall
-make verify_base -i
+# verify_base-ci fails on dependencies mismatch, but ignores linter errors, which are cought by Jenkins afterwards
+rm -rf build_env
+virtualenv build_env \
+    && . build_env/bin/activate \
+    && make --always-make verify_base-ci
diff --git a/.ci/whl-package.bash b/.ci/whl-package.bash
index 2b3e37698586bb4d7792ae3f8b2ffe13ae926462..4587ab62755bb7e333b0667d33d7cc57ebd6cc79 100644
--- a/.ci/whl-package.bash
+++ b/.ci/whl-package.bash
@@ -8,3 +8,5 @@ make set-nrp-version
 pushd hbp_nrp_virtual_coach
 python3 setup.py sdist bdist_wheel
 popd
+
+cp hbp_nrp_virtual_coach/dist/*.whl ./
\ No newline at end of file
diff --git a/Jenkinsfile b/Jenkinsfile
index 047ca08201281570e5b228534d37f2459845f3a9..9c75756f6627da10548523fdc1c7150b571cc0a3 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -5,138 +5,4 @@
 // https://www.jenkins.io/doc/book/pipeline/shared-libraries/
 @Library('nrp-shared-libs@master') _
 
-// Before starting pipeline, we try to get the proper image tag
-def DEFAULT_BRANCH = 'development'
-// selectTopicBranch function is used to choose the correct branch name as topic
-// the function is defined in shared libs
-// 
-// In case there is a PR for a branch, then Jenkins runs a pipeline for this pull request, not for the branch, 
-// even if there are new commits to the branch (until PR is not closed). The BRANCH_NAME variable in this case is something like PR-###
-// The name of the branch which is merged is stored in CHANGE_BRANCH variable. Thus, we should choose CHANGE_BRANCH as topic
-//
-// If there is a branch without PR, then Jenkins creates build for it normally for every push and the branch name is stored in BRANCH_NAME variable.
-// CHANGE_BRANCH is empty in this case. Thus, we choose BRANCH_NAME as topic for branches without PR.
-def TOPIC_BRANCH = selectTopicBranch(env.BRANCH_NAME, env.CHANGE_BRANCH)
-// We try to pull the image with the topic name, or use default tag otherwise
-def IMG_TAG = checkImageTag("${TOPIC_BRANCH}", "${DEFAULT_BRANCH}")
-
-pipeline {
-    environment {
-        USER_SCRIPTS_DIR = "user-scripts"
-        ADMIN_SCRIPTS_DIR = "admin-scripts"
-        VIRTUAL_COACH_DIR = "VirtualCoach"
-        // GIT_CHECKOUT_DIR is a dir of the main project (that was pushed)
-        GIT_CHECKOUT_DIR = "${env.VIRTUAL_COACH_DIR}"
-
-        // That is needed to pass the variables into environment with the same name from 
-        // Jenkins global scope (def ..=..)
-        TOPIC_BRANCH = "${TOPIC_BRANCH}"
-        DEFAULT_BRANCH = "${DEFAULT_BRANCH}"
-
-        NexusPyPiRepoUrl = "https://nexus.neurorobotics.ebrains.eu/repository/nrp-pypi-internal/"
-
-        CODE_COVERAGE_LINE = 74
-    }
-    agent {
-        docker {
-            label 'ci_label'
-            alwaysPull true
-            // NEXUS_REGISTRY_IP and NEXUS_REGISTRY_PORT are Jenkins global variables
-            registryUrl "https://${env.NEXUS_REGISTRY_IP}:${env.NEXUS_REGISTRY_PORT}"
-            registryCredentialsId 'nexusadmin'
-            image "nrp:${IMG_TAG}"
-            args '--entrypoint="" -u root --privileged'
-        }
-    }
-    options { 
-        // Skip code checkout prior running pipeline (only Jenkinsfile is checked out)
-        skipDefaultCheckout true
-    }
-
-    stages {
-        stage('Code checkout') {
-            steps {
-                // clear workspace
-                sh "rm -rf *"
-                // Notify BitBucket on the start of the job
-                // The Bitbucket Build Status Notifier is used
-                // REF: https://plugins.jenkins.io/bitbucket-build-status-notifier/
-                
-                bitbucketStatusNotify(buildState: 'INPROGRESS', buildName: 'Code checkout')
-
-                // Debug information on available environment
-                echo sh(script: 'env|sort', returnStdout: true)
-
-                // Checkout main project to GIT_CHECKOUT_DIR
-                dir(env.GIT_CHECKOUT_DIR) {
-                    checkout([
-                        $class: "GitSCM",
-                        branches: scm.branches, 
-                        extensions: [
-                            [$class: 'CloneOption', noTags: false],
-                            [$class: 'LocalBranch', localBranch: "**"]
-                        ], 
-                        userRemoteConfigs: scm.userRemoteConfigs
-                    ])
-                    sh 'chown -R "${USER}" ./'
-                }
-
-                // Clone all dependencies
-                cloneRepoTopic(env.USER_SCRIPTS_DIR,  'git@bitbucket.org:hbpneurorobotics/user-scripts.git',      env.TOPIC_BRANCH, env.DEFAULT_BRANCH,     '${USER}')
-                cloneRepoTopic(env.ADMIN_SCRIPTS_DIR, 'git@bitbucket.org:hbpneurorobotics/admin-scripts.git',     env.TOPIC_BRANCH, 'master',       '${USER}')
-
-                sh "git config --global --add safe.directory '*'"
-            }
-        }
-
-        stage('Build VirtualCoach') {
-            steps {
-                bitbucketStatusNotify(buildState: 'INPROGRESS', buildName: 'Building VirtualCoach')
-
-                // Determine explicitly the shell as bash (needed for proper user-scripts operation)
-                dir(env.GIT_CHECKOUT_DIR){
-                    sh 'bash .ci/build.bash'
-                    // deliver artifacts
-                    // Make true to false when the coverage reaches 85%-90%. This with turn off auto threshold for coverage
-                    makeReports(true, env.CODE_COVERAGE_LINE)
-                }
-            }
-        }
-
-        stage('Create package') {
-            steps {
-                bitbucketStatusNotify(buildState: 'INPROGRESS', buildName: 'Building pynrp')
-
-                dir(env.GIT_CHECKOUT_DIR){
-                    // build package
-                    sh "export HBP=${WORKSPACE} && bash ./.ci/whl-package.bash"
-                    // upload package
-                    withCredentials([usernamePassword(credentialsId: 'nexusadmin', usernameVariable: 'USER', passwordVariable: 'PASSWORD')])
-                    {
-                        sh 'pip3 install twine packaging'
-                        sh 'twine upload -u $USER -p $PASSWORD --repository-url ${NexusPyPiRepoUrl} hbp_nrp_virtual_coach/dist/*.whl'
-                    }
-                }
-            }
-        }
-    }
-
-    post {
-        always {
-            dir(env.GIT_CHECKOUT_DIR){
-                archiveArtifacts 'p*.*'
-                archiveArtifacts 'test-reports/*.*'
-                archiveArtifacts 'coverage.xml'
-            }
-        }
-        aborted {
-            bitbucketStatusNotify(buildState: 'FAILED', buildDescription: 'Build aborted!')
-        }
-        failure {
-            bitbucketStatusNotify(buildState: 'FAILED', buildDescription: 'Build failed, see console output!')
-        }
-        success{
-            bitbucketStatusNotify(buildState: 'SUCCESSFUL', buildDescription: 'branch ' + env.GIT_BRANCH)
-        }
-    }
-}
+jenkinsCIpipelinePythonModule("VirtualCoach")