diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b6ea9fcc868ce14d93c3bd111a24f8c63bb12f87..833113758812b28c973090ed8559a8a1c5805d8a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,12 +1,12 @@
+---
 # ---------------------------------------------------------------------------------------
 # Global
 # ---------------------------------------------------------------------------------------
----
 
 variables:
   # CI_IMAGES_BASEPATH: Environment variable configure in gitlab
   CI_IMAGES_PATH: ${CI_IMAGES_BASEPATH}/ci-images
-  CI_IMAGES_REVISION: 44965ccdd847f1e077670f49d546047f8ad0110c
+  CI_IMAGES_REVISION: 5dfddb02d67bbd16beb09ff4e31afcd5380f5788
   CI_IMAGE_PYTHON: "${CI_IMAGES_PATH}/python/3.9:${CI_IMAGES_REVISION}"
   CI_IMAGE_YOCTO: "${CI_IMAGES_PATH}/yocto-build/ubuntu-20.04:${CI_IMAGES_REVISION}"
 
@@ -65,22 +65,6 @@ yamllint:
 # ---------------------------------------------------------------------------------------
 # Stage: integrate
 # ---------------------------------------------------------------------------------------
-.ci-test-projects:
-  variables:
-    PROJECT_GROUP:
-      ${CI_PROJECT_ROOT_NAMESPACE}/yocto/infrastructure/ci-test
-    MANIFEST_PROJECT:
-      ${PROJECT_GROUP}/minimal-manifest
-    MANIFEST_BRANCH: primary
-
-.yocto-projects:
-  variables:
-    PROJECT_GROUP:
-      ${CI_PROJECT_ROOT_NAMESPACE}/yocto
-    MANIFEST_PROJECT:
-      ${PROJECT_ROOT}/manifest
-    MANIFEST_BRANCH: dunfell
-
 .integrate:
   stage: integrate
   rules:
@@ -99,49 +83,102 @@ yamllint:
       --submodule=.gitlab-ci
       --revision=${CI_COMMIT_SHA}
       --group=${PROJECT_GROUP}
-      --verbose
       ${MERGE}
 
-integrate-yocto:
-  extends:
-    - .integrate
-    - .yocto-projects
+# Running multiple integration jobs for the same manifest in parallel can lead to race
+# conditions if there are projects integrating a single branch to different manifest
+# branches. Use resource groups to force execution one by one.
+.integrate-ci-test:
+  extends: .integrate
+  resource_group: integrate-ci-test
+  variables:
+    PROJECT_GROUP: ${CI_PROJECT_ROOT_NAMESPACE}/yocto/infrastructure/ci-test
+    MANIFEST_PROJECT: ${PROJECT_GROUP}/minimal-manifest
+
+.integrate-yocto:
+  extends: .integrate
+  resource_group: integrate-yocto
+  variables:
+    PROJECT_GROUP: ${CI_PROJECT_ROOT_NAMESPACE}
+    MANIFEST_PROJECT: ${PROJECT_GROUP}/yocto/manifest
+
+# Jobs
+
+integrate-ci-test:primary:
+  extends: .integrate-ci-test
+  variables:
+    MANIFEST_BRANCH: primary
+
+integrate-ci-test:secondary:
+  extends: .integrate-ci-test
+  variables:
+    MANIFEST_BRANCH: secondary
+
+integrate-yocto:dunfell:
+  extends: .integrate-yocto
+  variables:
+    MANIFEST_BRANCH: dunfell
+
+integrate-yocto:kirkstone:
+  extends: .integrate-yocto
+  variables:
+    MANIFEST_BRANCH: kirkstone
 
-integrate-ci-test:
-  extends:
-    - .integrate
-    - .ci-test-projects
 
 # --------------------------------------------------------------------------------------
 # Stage: build
 # --------------------------------------------------------------------------------------
-build-yocto:
+.build:
   stage: build
-  needs: [integrate-yocto]
   rules:
     - if: $CI_MERGE_REQUEST_IID
       allow_failure: true
+
+.build-ci-test:
+  extends: .build
   trigger:
-    project: seco-ne/yocto/manifest
-    branch: "integrate/${CI_PROJECT_NAME}/${CI_COMMIT_REF_NAME}"
+    project: seco-ne/yocto/infrastructure/ci-test/minimal-manifest
+    branch: "integrate/${CI_PROJECT_NAME}/${CI_COMMIT_REF_NAME}/into/${MANIFEST_BRANCH}"
     strategy: depend
 
-build-ci-test:
-  stage: build
-  needs: [integrate-ci-test]
-  rules:
-    - if: $CI_MERGE_REQUEST_IID
-      allow_failure: true
+.build-yocto:
+  extends: .build
   trigger:
-    project: seco-ne/yocto/infrastructure/ci-test/minimal-manifest
-    branch: "integrate/${CI_PROJECT_NAME}/${CI_COMMIT_REF_NAME}"
+    project: seco-ne/yocto/manifest
+    branch: "integrate/${CI_PROJECT_NAME}/${CI_COMMIT_REF_NAME}/into/${MANIFEST_BRANCH}"
     strategy: depend
 
+# Jobs
+
+build-ci-test:primary:
+  extends: .build-ci-test
+  needs: ["integrate-ci-test:primary"]
+  variables:
+    MANIFEST_BRANCH: primary
+
+build-ci-test:secondary:
+  extends: .build-ci-test
+  needs: ["integrate-ci-test:secondary"]
+  variables:
+    MANIFEST_BRANCH: secondary
+
+build-yocto:dunfell:
+  extends: .build-yocto
+  needs: ["integrate-yocto:dunfell"]
+  variables:
+    MANIFEST_BRANCH: dunfell
+
+build-yocto:kirkstone:
+  extends: .build-yocto
+  needs: ["integrate-yocto:kirkstone"]
+  variables:
+    MANIFEST_BRANCH: kirkstone
+
+
 # --------------------------------------------------------------------------------------
 # Stage: merge
 # --------------------------------------------------------------------------------------
 .merge:
-  extends: .integrate
   stage: merge
   rules:
     - if: $CI_COMMIT_BRANCH == "master"
@@ -152,35 +189,62 @@ build-ci-test:
 
 merge-ci-test:
   extends:
+    - .integrate-ci-test
     - .merge
-    - .ci-test-projects
+  variables:
+    MANIFEST_BRANCH: primary,secondary
 
 merge-yocto:
   extends:
+    - .integrate-yocto
     - .merge
-    - .yocto-projects
+  variables:
+    MANIFEST_BRANCH: dunfell,kirkstone
+
 
 # --------------------------------------------------------------------------------------
 # Stage: build
 # --------------------------------------------------------------------------------------
-build-master-yocto:
+.build-master:
   stage: build
-  needs: [merge-yocto]
   rules:
     - if: $CI_COMMIT_BRANCH == "master"
       when: manual
+
+.build-master-ci-test:
+  extends: .build-master
+  needs: ["merge-ci-test"]
   trigger:
-    project: seco-ne/yocto/manifest
-    branch: "dunfell"
+    project: seco-ne/yocto/infrastructure/ci-test/minimal-manifest
+    branch: "${MANIFEST_BRANCH}"
     strategy: depend
 
-build-master-ci-test:
-  stage: build
-  needs: [merge-ci-test]
-  rules:
-    - if: $CI_COMMIT_BRANCH == "master"
-      when: manual
+.build-master-yocto:
+  extends: .build-master
+  needs: ["merge-yocto"]
   trigger:
-    project: seco-ne/yocto/infrastructure/ci-test/minimal-manifest
-    branch: "master"
+    project: seco-ne/yocto/manifest
+    branch: "${MANIFEST_BRANCH}"
     strategy: depend
+
+# Jobs
+
+build-master-ci-test:primary:
+  extends: .build-master-ci-test
+  variables:
+    MANIFEST_BRANCH: primary
+
+build-master-ci-test:secondary:
+  extends: .build-master-ci-test
+  variables:
+    MANIFEST_BRANCH: secondary
+
+build-master-yocto:dunfell:
+  extends: .build-master-yocto
+  variables:
+    MANIFEST_BRANCH: dunfell
+
+build-master-yocto:kirkstone:
+  extends: .build-master-yocto
+  variables:
+    MANIFEST_BRANCH: kirkstone
diff --git a/scripts/deploy_gitlab_ci.py b/scripts/deploy_gitlab_ci.py
index c3a1bf0a001916d925fe3070829c33e40804bb08..5f1f0d428a70105a15e2bdf43e6484c5270c6e5b 100755
--- a/scripts/deploy_gitlab_ci.py
+++ b/scripts/deploy_gitlab_ci.py
@@ -69,7 +69,8 @@ def integrate_submodule_into(
         "project": gitlab_project,
         "repo": project_repo,
         "dir": project_dir,
-        "branch": integration_branch_name,
+        "integration_branch": integration_branch_name,
+        "master_branch": branch,
         "commit": integration_commit,
         "message": message,
     }
@@ -118,7 +119,7 @@ def main():
     )
     parser.add_argument(
         "--manifest-branch",
-        help="""manifest branch to integrate changes into""",
+        help="""manifest branch to integrate changes into (can be a comma-separated list)""",
         dest="manifest_branch",
         required=True,
     )
@@ -177,6 +178,8 @@ def main():
             datefmt="%H:%M:%S",
         )
 
+    manifest_branches = args.manifest_branch.split(",")
+
     gitlab = Gitlab(args.gitlab_url, private_token=args.token)
     group = gitlab.groups.get(args.group)
 
@@ -184,44 +187,60 @@ def main():
     # Create integration branches and commits with updates
     # submodule in all projects
     # =======================================================
-    project_integration = {}
-    projects = get_integrating_projects(
-        args.manifest_project, args.manifest_branch, group
-    )
-    # Update submodule in all 'child' project
+    projects = []
+    for manifest_branch in manifest_branches:
+        print(
+            "Searching for projects in %s that are configured for automatic integration into %s:%s"
+            % (args.group, args.manifest_project, manifest_branch)
+        )
+        for p in get_integrating_projects(
+            args.manifest_project, manifest_branch, group
+        ):
+            if p not in projects:
+                projects.append(p)
+
+    # Update submodule in all integrating projects
+    project_integrations = []
     for p in projects:
-        print("Create integration commit in", p["project"])
+        print("Create integration commit in %s:%s" % (p["project"], p["branch"]))
 
-        res = integrate_submodule_into(
+        integration = integrate_submodule_into(
             gitlab, p["project"], args.submodule, args.revision, p["branch"]
         )
         # Store in the list if commit is set (meaning there was an update or
         #   an exising integration branch)
-        if res["commit"] is not None:
-            project_integration[p["project"]] = res
-
-    print("Create integration commit in", args.manifest_project)
-    # Update submodule in manifest project
-    manifest_project = integrate_submodule_into(
-        gitlab,
-        args.manifest_project,
-        args.submodule,
-        args.revision,
-        args.manifest_branch,
-        commit_and_push=False,
-        force_clone=True,
-    )
+        if integration["commit"] is not None:
+            project_integrations.append(integration)
+
+    manifest_projects = []
+    for manifest_branch in manifest_branches:
+        print(
+            "Create integration commit in %s:%s"
+            % (args.manifest_project, manifest_branch),
+        )
+        # Update submodule in manifest project
+        manifest_projects.append(
+            integrate_submodule_into(
+                gitlab,
+                args.manifest_project,
+                args.submodule,
+                args.revision,
+                manifest_branch,
+                commit_and_push=False,
+                force_clone=True,
+            )
+        )
 
-    branch = args.manifest_branch
-    if branch is None:
-        branch = manifest_project["project"].default_branch
     # =======================================================
     # Create and merge merge_requests if needed
     # =======================================================
     if args.merge:
-        # Get source merge request ( the one in the gitlab-ci repo)
+        # Get source merge request (the one in the gitlab-ci repo)
+        branch = manifest_projects[0]["master_branch"]
+        if branch is None:
+            branch = manifest_projects[0]["project"].default_branch
         submodule_project_path, _ = get_submodule_project_path_and_revision(
-            manifest_project["project"], args.submodule, branch
+            manifest_projects[0]["project"], args.submodule, branch
         )
         submodule_project = common.get_project(gitlab, submodule_project_path)
         mrs = get_merge_requests(
@@ -237,11 +256,10 @@ def main():
             )
         source_mr = mrs[0]
 
-        for p in project_integration:
-            integration = project_integration[p]
+        for integration in project_integrations:
             logging.debug("Create MR in %s", integration["project"].name)
             mr = create_integration_merge_request(
-                integration["project"], integration["branch"], source_mr
+                integration["project"], integration["integration_branch"], source_mr
             )
             integration["mr"] = mr
             # Now merge
@@ -255,10 +273,10 @@ def main():
                 integration["project"], mr, rebase=True
             )
             # if this has rebased the integration commit needs to be adapted:
-            project_integration[p]["commit"] = integration_commit
+            integration["commit"] = integration_commit
             # Save the target branch here, as the source branch gets deleted
             # during merge
-            project_integration[p]["branch"] = mr.target_branch
+            integration["integration_branch"] = mr.target_branch
 
             if not merged:
                 sys.exit(
@@ -275,89 +293,87 @@ def main():
     # Now create the integration commit in the manifest
     # for all subprojects at once
     # =======================================================
-    manifest_file_abs = os.path.join(
-        manifest_project["repo"].working_tree_dir, args.manifest_file
-    )
-    logging.debug("Read manifest from: %s", manifest_file_abs)
-    with open(manifest_file_abs, "r", encoding="utf8") as fp:
-        manifest = fp.read()
-    logging.debug(manifest)
-    srcrev_file_abs = os.path.join(
-        manifest_project["repo"].working_tree_dir, args.srcrev_file
-    )
-    logging.debug("Read manifest from: %s", srcrev_file_abs)
-    with open(srcrev_file_abs, "r", encoding="utf8") as fp:
-        srcrev = fp.read()
-    logging.debug(srcrev)
-
-    for p in project_integration:
-        integration = project_integration[p]
-        logging.debug(
-            "Update %s to %s", integration["project"].name, integration["commit"]
+    for manifest_project in manifest_projects:
+        manifest_file_abs = os.path.join(
+            manifest_project["repo"].working_tree_dir, args.manifest_file
         )
-
-        new_manifest = update_manifest(
-            manifest, integration["project"], integration["commit"]
+        logging.debug("Read manifest from: %s", manifest_file_abs)
+        with open(manifest_file_abs, "r", encoding="utf8") as fp:
+            manifest = fp.read()
+        logging.debug(manifest)
+        srcrev_file_abs = os.path.join(
+            manifest_project["repo"].working_tree_dir, args.srcrev_file
         )
-        if new_manifest is not None:
-            manifest = new_manifest
-            logging.debug(manifest)
-            continue
-
-        # get BB_RECIPE_NAME from the projects .gitlab-ci.yml
-        # Use direct read from gitlab as we have not checked out
-        # the repo if the branch is already up to date
+        logging.debug("Read manifest from: %s", srcrev_file_abs)
+        with open(srcrev_file_abs, "r", encoding="utf8") as fp:
+            srcrev = fp.read()
+        logging.debug(srcrev)
+
+        for integration in project_integrations:
+            logging.debug(
+                "Update %s to %s", integration["project"].name, integration["commit"]
+            )
 
-        gitlab_ci_yml = common.get_repository_file_raw(
-            integration["project"], ".gitlab-ci.yml", ref=integration["branch"]
-        )
-        project_keys = read_keys_from_gitlab_ci_yml(gitlab_ci_yml)
+            new_manifest = update_manifest(
+                manifest, integration["project"], integration["commit"]
+            )
+            if new_manifest is not None:
+                manifest = new_manifest
+                logging.debug(manifest)
+                continue
+
+            # get BB_RECIPE_NAME from the projects .gitlab-ci.yml
+            # Use direct read from gitlab as we have not checked out
+            # the repo if the branch is already up to date
+
+            gitlab_ci_yml = common.get_repository_file_raw(
+                integration["project"],
+                ".gitlab-ci.yml",
+                ref=integration["integration_branch"],
+            )
+            project_keys = read_keys_from_gitlab_ci_yml(gitlab_ci_yml)
 
-        new_srcrev = update_srcrev(
-            srcrev, project_keys["recipe"], integration["commit"]
+            new_srcrev = update_srcrev(
+                srcrev, project_keys["recipe"], integration["commit"]
+            )
+            if new_srcrev is not None:
+                srcrev = new_srcrev
+                logging.debug(srcrev)
+            else:
+                logging.debug("Project %s not found in xml or srcrev file", p)
+
+        # Write manifest
+        with open(manifest_file_abs, "w", encoding="utf8") as fp:
+            fp.write(manifest)
+        manifest_project["repo"].git.add(args.manifest_file)
+        logging.debug(manifest)
+        with open(srcrev_file_abs, "w", encoding="utf8") as fp:
+            fp.write(srcrev)
+        manifest_project["repo"].git.add(args.srcrev_file)
+        logging.debug(srcrev)
+
+        # ========================================================
+        # Squash all commits on the integration branch to one
+        # ========================================================
+        manifest_project["repo"].remotes.origin.fetch(manifest_project["master_branch"])
+        manifest_master = manifest_project["project"].branches.get(
+            manifest_project["master_branch"]
         )
-        if new_srcrev is not None:
-            srcrev = new_srcrev
-            logging.debug(srcrev)
-        else:
-            logging.debug("Project %s not found in xml or srcrev file", p)
-
-    # Write manifest
-    with open(manifest_file_abs, "w", encoding="utf8") as fp:
-        fp.write(manifest)
-    manifest_project["repo"].git.add(args.manifest_file)
-    logging.debug(manifest)
-    with open(srcrev_file_abs, "w", encoding="utf8") as fp:
-        fp.write(srcrev)
-    manifest_project["repo"].git.add(args.srcrev_file)
-    logging.debug(srcrev)
-
-    # ========================================================
-    # Squash all commits on the integration branch to one
-    # ========================================================
-    manifest_project["repo"].remotes.origin.fetch(branch)
-    manifest_master = manifest_project["project"].branches.get(branch)
-    manifest_project["repo"].git.reset("--soft", manifest_master.commit["id"])
-
-    # ========================================================
-    # Now commit and push the changes to the manifest repo
-    # ========================================================
-    # Make an API request to create the gitlab.user object
-    gitlab = manifest_project["project"].manager.gitlab
-    gitlab.auth()
-    integration_commit = common.commit_and_push(
-        manifest_project["project"],
-        manifest_project["repo"],
-        manifest_project["message"],
-        gitlab.user.username,
-        gitlab.user.email,
-    )
-
-    print(
-        "Successfully create integration commit {} in {}".format(
-            integration_commit, args.manifest_project
+        manifest_project["repo"].git.reset("--soft", manifest_master.commit["id"])
+
+        # ========================================================
+        # Now commit and push the changes to the manifest repo
+        # ========================================================
+        # Make an API request to create the gitlab.user object
+        gitlab = manifest_project["project"].manager.gitlab
+        gitlab.auth()
+        integration_commit = common.commit_and_push(
+            manifest_project["project"],
+            manifest_project["repo"],
+            manifest_project["message"],
+            gitlab.user.username,
+            gitlab.user.email,
         )
-    )
 
     if not args.merge:
         sys.exit(0)
@@ -365,34 +381,36 @@ def main():
     # ============================================
     # Create merge requests for the manifest
     # ============================================
-
-    logging.debug("Create MR in %s", manifest_project["project"].name)
-    manifest_project["mr"] = create_integration_merge_request(
-        manifest_project["project"], manifest_project["branch"], source_mr
-    )
-    # =================================================
-    # Now merge it
-    # =================================================
-    # The manifest needs to be merged at last
-    mr = manifest_project["mr"]
-    logging.debug("Merge %s!%s", args.manifest_project, mr.iid)
-
-    # Wait until GitLab has checked merge status
-    common.wait_until_merge_status_is_set(manifest_project["project"], mr)
-
-    # Attempt to merge
-    merged = accept_merge_request(manifest_project["project"], mr, rebase=True)
-
-    if not merged:
-        sys.exit(
-            "Integration MR could not be merged. You have two possibilities to fix "
-            "this:\n"
-            "  1. Checkout the MR and rebase it on the current master manually, or\n"
-            "  2. Delete the MR (Edit -> Delete in the MR UI)\n"
-            "In either case restart this job afterwards in order to get it merged."
+    for manifest_project in manifest_projects:
+        logging.debug("Create MR in %s", manifest_project["project"].name)
+        manifest_project["mr"] = create_integration_merge_request(
+            manifest_project["project"],
+            manifest_project["integration_branch"],
+            source_mr,
         )
+        # =================================================
+        # Now merge it
+        # =================================================
+        # The manifest needs to be merged at last
+        mr = manifest_project["mr"]
+        logging.debug("Merge %s!%s", args.manifest_project, mr.iid)
 
-    print("Successfully merged")
+        # Wait until GitLab has checked merge status
+        common.wait_until_merge_status_is_set(manifest_project["project"], mr)
+
+        # Attempt to merge
+        merged = accept_merge_request(manifest_project["project"], mr, rebase=True)
+
+        if not merged:
+            sys.exit(
+                "Integration MR could not be merged. You have two possibilities to fix "
+                "this:\n"
+                "  1. Checkout the MR and rebase it on the current master manually, or\n"
+                "  2. Delete the MR (Edit -> Delete in the MR UI)\n"
+                "In either case restart this job afterwards in order to get it merged."
+            )
+
+        print("Successfully merged")
 
 
 if __name__ == "__main__":