diff --git a/jobs/defaults.yaml b/jobs/defaults.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..cb1e056080a40810585b6c627ce221ac5c80c203
--- /dev/null
+++ b/jobs/defaults.yaml
@@ -0,0 +1,5 @@
+- defaults:
+    name: global
+    gitlab_url: https://gitlab-staging.swh.network
+    gitlab_connection_name: gitlab-staging
+    gitlab_project: false
diff --git a/jobs/swh-docs.yaml b/jobs/swh-docs.yaml
index 5ff6e02b27c1ee6fb2e3430bd8516416f4e6f946..af6ec77eb9eb98d78b6454da23275cb95b279c87 100644
--- a/jobs/swh-docs.yaml
+++ b/jobs/swh-docs.yaml
@@ -6,6 +6,7 @@
       - "{name}/publish"
       - "{name}/dev"
       - "{name}/build-on-diff"
+      - "{name}/gitlab-builds"
 
 - job-template:
     name: "{name}/publish"
@@ -122,7 +123,7 @@
           keep-all: false
           includes: "**/*"
 
-- job-template:
+- job-template: &doc_build_on_diff
     name: "{name}/build-on-diff"
     display_name: Phab. diff
     project-type: pipeline
@@ -154,3 +155,22 @@
           description: URI of the staging repository
 
     dsl: !include-jinja2: templates/swh-docs-pipeline-diff.groovy.j2
+
+- job-template:
+    name: "{name}/gitlab-builds"
+    display_name: GitLab builds
+    gitlab_project: true
+    auth-token:
+    parameters:
+    properties:
+      - gitlab:
+          connection: "{gitlab_connection_name}"
+    triggers:
+      - gitlab:
+          trigger-push: true
+          trigger-merge-request: true
+          add-ci-message: true
+          cancel-pending-builds-on-update: true
+          # secret jenkins token is generated when executing tox
+          secret-token: !include-raw: jobs/templates/jenkins-token
+    <<: *doc_build_on_diff
diff --git a/jobs/swh-packages.yaml b/jobs/swh-packages.yaml
index 037a7df2327c32ec39277ed433c5006282c23faf..5ee4775dc7b9e7e7f1e79b163f93c1ce4261d582 100644
--- a/jobs/swh-packages.yaml
+++ b/jobs/swh-packages.yaml
@@ -1,7 +1,5 @@
 - job-group:
     name: "swh-jobs-{name}"
-    gitlab_url: https://gitlab-staging.swh.network
-    gitlab_connection_name: gitlab-staging
     gitlab_project_name: "swh/devel/{repo_name}"
     jobs:
       - "{name}"
diff --git a/jobs/templates/incoming-tag.yaml b/jobs/templates/incoming-tag.yaml
index 97dd831f5b05aa7db8c01ef22a5eb24664a70bb3..ece06c3ff333dbb69b16e3c68557acd9be919b12 100644
--- a/jobs/templates/incoming-tag.yaml
+++ b/jobs/templates/incoming-tag.yaml
@@ -5,7 +5,6 @@
     auth-token: "ph4br1cat0r"
     incoming_tag_auto_pypi_host: pypi.org
     sandbox: true
-    gitlab_project: false
     properties:
       - build-discarder:
           num-to-keep: 20
diff --git a/jobs/templates/swh-docs-pipeline-diff.groovy.j2 b/jobs/templates/swh-docs-pipeline-diff.groovy.j2
index b56de4296e0b462f87124d1c08465217436c78cc..7dc72b1a185290d6bacc6a03abc3b36e21f4f850 100644
--- a/jobs/templates/swh-docs-pipeline-diff.groovy.j2
+++ b/jobs/templates/swh-docs-pipeline-diff.groovy.j2
@@ -3,25 +3,35 @@ pipeline {
     {%- include 'templates/includes/agent-docker.groovy.j2' -%}
   {% endfilter %}
 
+  {%- if not gitlab_project %}
   environment {
     PHAB_CONDUIT_URL = 'https://forge.softwareheritage.org/api/'
   }
+  {%- endif %}
 
   stages {
     stage('Checkout swh environment') {
       steps {
+        {%- if not gitlab_project %}
         {% filter indent(width=8) %}
           {%- include 'templates/includes/create-phabricator-artifacts.groovy.j2' -%}
         {% endfilter %}
+        {%- else %}
+          updateGitlabCommitStatus name: 'jenkins', state: 'running'
+        {%- endif %}
         checkout([
           $class: 'GitSCM',
           doGenerateSubmoduleConfigurations: false,
           extensions: [[$class: 'CloneOption', depth: 1, shallow: true]],
           gitTool: 'Default',
           submoduleCfg: [],
-          userRemoteConfigs: [
-            [url: 'https://forge.softwareheritage.org/source/swh-environment.git'],
-          ],
+          userRemoteConfigs: [[
+            {%- if not gitlab_project %}
+            url: 'https://forge.softwareheritage.org/source/swh-environment.git'
+            {%- else %}
+            url: '{{gitlab_url}}/swh/devel/swh-environment.git'
+            {%- endif %}
+          ]],
         ])
       }
     }
@@ -40,6 +50,7 @@ pipeline {
       }
     }
 
+    {%- if not gitlab_project %}
     stage('Apply phabricator diff') {
       steps {
         dir('swh-docs') {
@@ -49,11 +60,20 @@ pipeline {
         }
       }
     }
+    {%- endif %}
 
     stage('Build Software Heritage documentation') {
       steps {
         dir('swh-docs') {
           script {
+            {%- if gitlab_project %}
+            if ("${env.gitlabMergeRequestIid}" != "") {
+              sh "git fetch origin merge-requests/${env.gitlabMergeRequestIid}/head:merge_request && \
+                  git checkout merge_request"
+            } else {
+              sh "git checkout ${env.gitlabSourceBranch}"
+            }
+            {%- endif %}
             sh '''#!/bin/bash
             SPHINXOPTS='-W -q --keep-going -w errors.log' SPHINXOPTCOLOR='--no-color' tox -e sphinx-dev
             '''
@@ -64,7 +84,19 @@ pipeline {
   }
 
   post {
+    {%- if gitlab_project %}
+    failure {
+      updateGitlabCommitStatus name: 'jenkins', state: 'failed'
+    }
+    success {
+      updateGitlabCommitStatus name: 'jenkins', state: 'success'
+    }
+    aborted {
+      updateGitlabCommitStatus name: 'jenkins', state: 'canceled'
+    }
+    {%- endif %}
     always {
+      {%- if not gitlab_project %}
       step([$class: 'PhabricatorNotifier',
             commentOnSuccess: true,
             commentWithConsoleLinkOnFailure: true,
@@ -75,6 +107,7 @@ pipeline {
             lintFile: '.phabricator-lint',
             lintFileSize: '1000000',
       ])
+      {%- endif %}
 
       archiveArtifacts(
         allowEmptyArchive: true,
diff --git a/jobs/templates/swh-pipeline-diff.yaml b/jobs/templates/swh-pipeline-diff.yaml
index eb40b91ab01759dc2cdc12c9aca06e5d7fbeae61..99fa9754ad76ad1393dd694d030c3c8cadb9e011 100644
--- a/jobs/templates/swh-pipeline-diff.yaml
+++ b/jobs/templates/swh-pipeline-diff.yaml
@@ -10,7 +10,6 @@
     sandbox: true
     auth-token: "ph4br1cat0r"
     phabricator_diff: true
-    gitlab_project: false
     do_cypress: false
     timeout: 10
     max_concurrent: 0
diff --git a/jobs/templates/swh-pipeline.yaml b/jobs/templates/swh-pipeline.yaml
index f1ecdbe8e366b1c1bb7c6568d5d2b058f742d5ec..0d8938e0ec69b3ca9af2a5b88715dbf961c84bc0 100644
--- a/jobs/templates/swh-pipeline.yaml
+++ b/jobs/templates/swh-pipeline.yaml
@@ -16,7 +16,6 @@
           days-to-keep: 90
           artifact-num-to-keep: 20
     phabricator_diff: false
-    gitlab_project: false
     do_cypress: false
     timeout: 10
     max_concurrent: 0
diff --git a/jobs/templates/swh-pypi.yaml b/jobs/templates/swh-pypi.yaml
index 27123f3bb5c1c1250d823d7b809f6a552b6f4bde..0ca2431528a3fa0058952361ae39c996ac6efe29 100644
--- a/jobs/templates/swh-pypi.yaml
+++ b/jobs/templates/swh-pypi.yaml
@@ -5,7 +5,6 @@
     include_bdist: true
     project-type: pipeline
     sandbox: true
-    gitlab_project: false
     properties:
       - build-discarder:
           artifact-num-to-keep: 10
diff --git a/jobs/tools/jenkins-jobs-builder.yaml b/jobs/tools/jenkins-jobs-builder.yaml
index cf9e48dbac59b3813ca8d00aa5b3414e813e15e0..c45ffedae8b924c00b143c15eccc9d077762f2c6 100644
--- a/jobs/tools/jenkins-jobs-builder.yaml
+++ b/jobs/tools/jenkins-jobs-builder.yaml
@@ -3,8 +3,6 @@
     project-type: pipeline
     description: Update jenkins jobs and setup GitLab webhooks
     node: built-in
-    gitlab_url: https://gitlab-staging.swh.network
-    gitlab_connection_name: gitlab-staging
     gitlab_project_name: swh/infra/ci-cd/swh-jenkins-jobs
     properties:
       - build-discarder:
diff --git a/jobs/tools/setup-gitlab-webhooks.groovy.j2 b/jobs/tools/setup-gitlab-webhooks.groovy.j2
index 5976ce5c2e41fdb2101cabbe3d5de589ccb3e344..467f8ccef3a67be9d717f253076cb5428f24bacc 100644
--- a/jobs/tools/setup-gitlab-webhooks.groovy.j2
+++ b/jobs/tools/setup-gitlab-webhooks.groovy.j2
@@ -25,6 +25,8 @@ pipeline {
           setupGitlabWebhook("swh/infra/ci-cd/swh-jenkins-jobs",
                              "jenkins-tools/swh-jenkins-jobs-builder",
                              true, true, false)
+          setupGitlabWebhook("swh/devel/swh-docs", "DDOC/gitlab-builds",
+                             true, true, false)
 
           projects = readYaml(file: 'jobs/swh-packages.yaml')
           for (project in projects) {
diff --git a/jobs/tools/setup-gitlab-webhooks.yaml b/jobs/tools/setup-gitlab-webhooks.yaml
index f5eba6cdc29615caade11f477bdb04bdea1cc017..bd7f94b3281741adf542690bf20e0c6138a0e948 100644
--- a/jobs/tools/setup-gitlab-webhooks.yaml
+++ b/jobs/tools/setup-gitlab-webhooks.yaml
@@ -9,7 +9,7 @@
       - string:
           name: gitlab_url
           description: URL of GitLab instance
-          default: https://gitlab-staging.swh.network
+          default: "{gitlab_url}"
       - string:
           name: jenkins_url
           description: URL of Jenkins instance