diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f46440f99971c70e3258e04bcbac90379f431e47..90474ebc548083df7cac2372b6dacbc17bb560fb 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,6 +1,7 @@
 stages:
   - check-code
   - compile
+  - deploy
 
 check-code:
   image: ninfra/puppet-checker:0.0.1
@@ -29,3 +30,12 @@ compile-profiles:
        done < <( find ./profile/manifests/ -name "*.pp" -a -exec grep ^class "{}" \; | awk "{ print \$2 }" );'
   tags:
     - docker
+
+deploy-role-onlyoffice:
+  stage: deploy
+  variables:
+    GIT_SUBMODULE_STRATEGY: recursive
+  script:
+    - sudo ./.scripts/deploy-puppet-master.sh role::onlyoffice
+  tags:
+    - libvirt
diff --git a/.scripts/deploy-role.sh b/.scripts/deploy-role.sh
new file mode 100755
index 0000000000000000000000000000000000000000..28109325df22f23ed3962b763de51d69019a162e
--- /dev/null
+++ b/.scripts/deploy-role.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Deploy a role using Puppet Apply.
+
+set -x
+
+CLASS=${1}
+
+if [ -z "${CLASS}" ]; then
+	echo "Usage: ${0} class"
+	exit 1
+fi
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+REPO_DIR="$( dirname ${SCRIPT_DIR} )"
+
+apt update
+apt install -qy puppet
+
+git -C ${REPO_DIR} submodule update --init
+
+# deploy a role 
+puppet apply \
+	--detailed-exitcodes \
+	--modulepath ${REPO_DIR}/modules \
+	--hiera-config ${REPO_DIR}/hiera.yaml \
+	--codedir ${REPO_DIR} \
+	-e "include ${CLASS}"
+
+test "${?}" = 2 || exit 1  # Puppet exit code 2: The run succeeded, and some resources were changed.