diff --git a/build-pipeline.yml b/build-pipeline.yml
index a3f62820792a5d3e97bde53a7fa6b7ae4049fcc4..38428aa07daf2fc892a18173ce8f53b413d6463a 100644
--- a/build-pipeline.yml
+++ b/build-pipeline.yml
@@ -19,9 +19,7 @@ workflow:
 .initDeployENV: &initDeployENV
   - |
     echo "Initiating deploy.env"
-    if [[ "$BITBAKE_TASK" == "populate_sdk" ]]; then
       echo "SDK_FILE_NAME=$sdk_filename" | tee -a deploy.env
-    else
       echo "DEPLOY_DATE=$DEPLOY_DATE" | tee -a deploy.env
       echo "BOARD=$BOARD" | tee -a deploy.env
       echo "IMAGE_NAME=$IMAGE_NAME" | tee -a deploy.env
@@ -33,7 +31,6 @@ workflow:
       echo "BMAP_FILE_NAME=$bmap_name" | tee -a deploy.env
       echo "BUNDLE_FILE_NAME=$bundle_filename" | tee -a deploy.env
       echo "MD5_SUMS_FILE_NAME=$md5sums_filename" | tee -a deploy.env
-    fi
 
 # -----------------------------------
 # Stage: build
diff --git a/release-pipeline-clea.yml b/release-pipeline-clea.yml
index 37d7120c7f806d6b79d168789a9f4d2c3e3a7176..34e5ab387d55b912aee9043678ee880bb63fd1d3 100644
--- a/release-pipeline-clea.yml
+++ b/release-pipeline-clea.yml
@@ -17,9 +17,13 @@ variables:
   RELEASE_TEMPLATES_DIR: release_templates
   RELEASE_GITLAB_TEMPLATE: release_clea.jinja2
   RELEASE_PAGE_FILE_NAME: release-gitlab.md
+  RELEASE_CONFLUENCE_TECHSRC_TEMPLATE: confluence_secone_tech_page.jinja2
+  RELEASE_CONFLUENCE_TECHSRC_FILE_NAME: release-confluence-secone-techresources.xml
+  RELEASE_CONFLUENCE_TECHSRC_LABELS: confluence_secone_tech_labels.jinja2
   CHANGELOG_FILE: Changelog_${SECO_REMOTE}_${CI_COMMIT_TAG}.md
   DEPLOY_STAGE: deploy
   FILTER_LIST: "TOKEN KEY"
+  CONFLUENCE_URL: "https://secogroup.atlassian.net"
 
 stages:
   - collect
@@ -104,7 +108,8 @@ changelog:deploy:
   timeout: 4h
   tags:
     - azure_deploy
-  needs: ["changelog:generate"]
+  needs:
+    - changelog:generate
   script:
     - *azure_storage_properties
     - echo "Deploying changelog.."
@@ -120,12 +125,13 @@ generate:gitlab-release-page:
   extends: .infrastructure
   rules:
     - if: $CI_PIPELINE_SOURCE == "parent_pipeline" && $CI_COMMIT_TAG
-  needs: ["collect:release-information", "changelog:deploy"]
+  needs:
+    - collect:release-information
+    - changelog:deploy
   script:
     - *azure_storage_properties
     - export CHANGELOG_FILE_NAME="https://${AZURE_STORAGE_ACCOUNT}.blob.core.windows.net/${AZURE_CONTAINER_NAME}/${SECO_REMOTE}/reports/${CHANGELOG_FILE}"
     - set -o allexport && source ${RELEASE_VARIABLES_FILE_NAME} && set +o allexport
-    - *printenv
     - echo "Rendering release page.."
     - cd .gitlab-ci/${RELEASE_TEMPLATES_DIR}
     - ../scripts/render_jinja2_template.py
@@ -135,10 +141,32 @@ generate:gitlab-release-page:
     paths:
       - ${RELEASE_PAGE_FILE_NAME}
 
+generate:confluence-techresources-release-page:
+  stage: prepare
+  extends: .infrastructure
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "parent_pipeline" && $CI_COMMIT_TAG
+  needs:
+    - collect:release-information
+    - generate:gitlab-release-page
+  script:
+    - export RELEASE_DATE=$(date -d "$CI_PIPELINE_CREATED_AT" +"%d %B %Y")
+    - export RELEASE_NAME=Release-${CI_COMMIT_TAG}
+    - export RELEASE_URL=${CI_PROJECT_PATH}/-/releases/${CI_COMMIT_TAG}
+    - echo "Rendering release page.."
+    - cd .gitlab-ci/${RELEASE_TEMPLATES_DIR}
+    - ../scripts/render_jinja2_template.py
+        --template ${RELEASE_CONFLUENCE_TECHSRC_TEMPLATE}
+        > ../../${RELEASE_CONFLUENCE_TECHSRC_FILE_NAME}
+  artifacts:
+    paths:
+      - ${RELEASE_CONFLUENCE_TECHSRC_FILE_NAME}
+
 publish:gitlab-release-page:
   stage: publish
   extends: .infrastructure
-  needs: ["generate:gitlab-release-page"]
+  needs:
+    - generate:gitlab-release-page
   script:
     - .gitlab-ci/scripts/create_gitlab_release.py
         --gitlab-url ${CI_SERVER_URL}
@@ -147,3 +175,36 @@ publish:gitlab-release-page:
         --name "Release-${CI_COMMIT_TAG}"
         --tag-name ${CI_COMMIT_TAG}
         --description-file=${RELEASE_PAGE_FILE_NAME}
+
+publish:confluence-techresources-release-page:
+  stage: publish
+  extends: .infrastructure
+  variables:
+    CONFLUENCE_SPACE: SECONorthTech
+    CONFLUENCE_PARENT_ID: 2351595548
+  needs:
+    - collect:release-information
+    - generate:confluence-techresources-release-page
+    - publish:gitlab-release-page
+  script:
+    - eval "$(grep '^BOARD=' ${RELEASE_VARIABLES_FILE_NAME})"
+    - export BOARD
+    - cd .gitlab-ci/${RELEASE_TEMPLATES_DIR}
+    - ../scripts/render_jinja2_template.py
+        --template ${RELEASE_CONFLUENCE_TECHSRC_LABELS}
+        > ../../techsrc-confluence-labels.txt
+    - cd ../..
+    - while IFS= read -r line; do
+        [ -n "$line" ] && LABEL_ARGS="${LABEL_ARGS} --label=$line";
+      done < techsrc-confluence-labels.txt
+    - .gitlab-ci/scripts/confluence_create_or_update_page.py
+        --inherit-parent-restrictions
+        --confluence-url="${CONFLUENCE_URL}"
+        --username="${CONFLUENCE_USERNAME}"
+        --token="${CONFLUENCE_TOKEN}"
+        --space-id="${CONFLUENCE_TECHSRC_SPACE}"
+        --parent-id="${CONFLUENCE_TECHSRC_PARENT_ID}"
+        --page-name="Clea OS ${RELEASE_NAME}"
+        --content-file="${RELEASE_CONFLUENCE_TECHSRC_FILE_NAME}"
+        --label="os-release"
+        ${LABEL_ARGS}
diff --git a/release_templates/confluence_secone_tech_labels.jinja2 b/release_templates/confluence_secone_tech_labels.jinja2
new file mode 100644
index 0000000000000000000000000000000000000000..31d7b454c865f6399b651e4917c0439725b150f5
--- /dev/null
+++ b/release_templates/confluence_secone_tech_labels.jinja2
@@ -0,0 +1,21 @@
+{#- Board lables -#}
+{%- if BOARD is defined -%}
+
+{%- set lbs = namespace(labels = []) -%}
+{%- for board in BOARD.split(' ') | sort -%}
+    {%- set label = "" -%}
+    {%- if "e83-d18" in board -%}
+        {%- set label = "modular-vision" -%}
+    {%- elif "santino" in board -%}
+        {%- set label = "santino" -%}
+    {%- endif -%}
+    {%- if label != "" -%}
+        {%- set lbs.labels = lbs.labels + [label] -%}
+    {%- endif -%}
+{%- endfor -%}
+
+{% for label in lbs.labels -%}
+{{ label }}
+{% endfor -%}
+
+{%- endif -%}
diff --git a/release_templates/confluence_secone_tech_page.jinja2 b/release_templates/confluence_secone_tech_page.jinja2
new file mode 100644
index 0000000000000000000000000000000000000000..3e068636e7c823fae126ccb0016e98fb30056191
--- /dev/null
+++ b/release_templates/confluence_secone_tech_page.jinja2
@@ -0,0 +1,47 @@
+{#-
+  See documentation for storage format:
+  https://confluence.atlassian.com/doc/confluence-storage-format-790796544.html
+-#}
+<ac:structured-macro ac:name="details" ac:schema-version="1" data-layout="default" ac:macro-id="4a0ac7ab071a2383a7d0b5134ec5b46b">
+    <ac:rich-text-body>
+        <table data-layout="default">
+            <tbody>
+                <tr>
+                    {#-
+                      Attention: no line breaks allowed within <th>..</th>,
+                      otherwise the field is not correctly parsed by the Page
+                      Properties Report macro.
+                    #}
+                    <th><b>Version</b></th>
+                    <td>{{ RELEASE_NAME }}</td>
+                </tr>
+                <tr>
+                    <th><b>Release Date</b></th>
+                    <td><time datetime="{{ RELEASE_DATE }}"/></td>
+                </tr>
+                <tr>
+                    <th><b>Architecture / Processor</b></th>
+                    <td>
+                        {%- if MACHINE is defined %}
+                        {%- for machine in MACHINE.split(' ') | sort %}
+                        <p>{{ machine }}</p>
+                        {%- endfor %}
+                        {%- endif %}
+                    </td>
+                </tr>
+                {%- if FILES_documentation is defined %}
+                <tr>
+                    <th><b>Documentation</b></th>
+                    <td>
+                        {%- for doc_file in FILES_documentation.split(' ') | sort %}
+                        {%- if doc_file.endswith(".html") %}
+                        <p><a href="{{ doc_file }}">{{ (doc_file | basename | splitext)[0] }}</a></p>
+                        {%- endif %}
+                        {%- endfor %}
+                    </td>
+                </tr>
+                {%- endif %}
+            </tbody>
+        </table>
+    </ac:rich-text-body>
+</ac:structured-macro>
diff --git a/release_templates/release_clea.jinja2 b/release_templates/release_clea.jinja2
index 7e54c9775c26e63a46654384caa2c115c8d044c3..5b7b0ddeee258493399f1377cb8837bed6043fb9 100644
--- a/release_templates/release_clea.jinja2
+++ b/release_templates/release_clea.jinja2
@@ -43,12 +43,15 @@
 
 {%- macro get_file_link(machine_distro, file_type) -%}
     {%- set return_link = namespace(rl = "") -%}
+    {%- set filename = "" -%}
     {%- set machine_underscore = machine_distro | replace("-", "_") -%}
     {#- see https://stackoverflow.com/questions/72654161/dynamically-referencing-a-variable-in-jinja2 -#}
-    {%- set filename = self._TemplateReference__context.resolve(filenames_mapping[file_type] ~ "_FILE_NAME_" ~ machine_underscore) -%}
+    {%- if self._TemplateReference__context.resolve(filenames_mapping[file_type] ~ "_FILE_NAME_" ~ machine_underscore) is defined -%}
+        {%- set filename = self._TemplateReference__context.resolve(filenames_mapping[file_type] ~ "_FILE_NAME_" ~ machine_underscore) -%}
+    {%- endif -%}
     {%- set link_var = self._TemplateReference__context.resolve("FILE_LINKS_" ~ machine_underscore) -%}
     {%- for full_link in self._TemplateReference__context.resolve("FILE_LINKS_" ~ machine_underscore).split(" ") -%}
-        {%- if filename in full_link -%}
+        {%- if filename != "" and filename in full_link -%}
             {%- set return_link.rl = full_link -%}
         {%- endif -%}
     {%- endfor -%}
diff --git a/scripts/confluence_create_or_update_page.py b/scripts/confluence_create_or_update_page.py
new file mode 100755
index 0000000000000000000000000000000000000000..abf73f3c28372cde720c9aa87596b6599a75dc30
--- /dev/null
+++ b/scripts/confluence_create_or_update_page.py
@@ -0,0 +1,200 @@
+#!/usr/bin/env python3
+
+import argparse
+import json
+
+import requests
+from atlassian import Confluence
+from requests.auth import HTTPBasicAuth
+
+
+def str_to_bool(argument):  # Helper, converter for parsing boolean argument values
+    if isinstance(argument, bool):
+        return argument
+    if argument.lower() in {"true", "yes", "1"}:
+        return True
+    elif argument.lower() in {"false", "no", "0"}:
+        return False
+    else:
+        raise argparse.ArgumentTypeError("Boolean value expected.")
+
+
+def main():
+    # Create parser and add possible command line arguments
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument(
+        "--confluence-url",
+        help="""URL to the Confluence instance""",
+        dest="confluence_url",
+    )
+    parser.add_argument(
+        "--username",
+        help="""Username for the Confluence API""",
+        dest="username",
+        required=True,
+    )
+
+    parser.add_argument(
+        "--token",
+        help="""Token for the Confluence API""",
+        dest="token",
+        required=True,
+    )
+
+    parser.add_argument(
+        "--space-id",
+        help="""Confluence space ID or name""",
+        dest="space_id",
+        required=True,
+    )
+
+    parser.add_argument(
+        "--parent-id",
+        help="""ID of the parent page""",
+        dest="parent_id",
+        required=True,
+    )
+
+    parser.add_argument(
+        "--page-name",
+        help="""Name for the new page""",
+        dest="page_name",
+        required=True,
+    )
+
+    parser.add_argument(
+        "--content-file",
+        help="""File to load the content for the new page from""",
+        dest="content_file",
+        required=True,
+    )
+
+    parser.add_argument(
+        "--label",
+        help="""Label to apply to the new page""",
+        dest="label",
+        action="append",
+        required=False,
+    )
+
+    parser.add_argument(
+        "--inherit-parent-restrictions",
+        help="Inherit restrictions from parent page (default: false)",
+        dest="inherit_parent_restrictions",
+        type=str_to_bool,
+        nargs="?",
+        const=True,
+        default=False,
+    )
+
+    # Parse command line arguments
+    args, _ = parser.parse_known_args()
+
+    # Read content file
+    try:
+        with open(args.content_file, "r", encoding="utf8") as content_file:
+            content = content_file.read()
+    except FileNotFoundError:
+        exit(f"ERROR: '{args.content_file}' not found")
+
+    # Connect to confluence
+    confluence = Confluence(
+        url=args.confluence_url,
+        username=args.username,
+        password=args.token,
+    )
+
+    try:
+        # Check if page already exists
+        page = confluence.get_page_by_title(args.space_id, args.page_name)
+
+        if type(page) is dict:
+            # Update existing page
+            page = confluence.update_page(
+                page["id"], args.page_name, content, args.parent_id, full_width=False
+            )
+            print("Updated existing Confluence page:")
+
+        else:
+            # Create new page
+            page = confluence.create_page(
+                args.space_id,
+                args.page_name,
+                content,
+                args.parent_id,
+                representation="storage",
+                type="page",
+                editor="v2",
+                full_width=False,
+            )
+            # Update it immediately with the same contents because sometimes the
+            # "full_width=False" argument does not work, see:
+            # https://community.developer.atlassian.com/t/how-to-set-fixed-width-when-creating-new-page-with-rest-call/53591
+            page = confluence.update_page(
+                page["id"],
+                args.page_name,
+                content,
+                args.parent_id,
+                representation="storage",
+                type="page",
+                full_width=False,
+                minor_edit=True,
+            )
+            print("Created new Confluence page:")
+
+        if type(page) is dict:
+            # Print absolute page URL
+            print(page["_links"]["base"] + page["_links"]["webui"])
+
+            # Set labels for the page
+            for label in args.label:
+                confluence.set_page_label(page["id"], label)
+
+        if args.inherit_parent_restrictions:
+            # Retrieve all restrictions applied to the parent page
+            parent_restrictions = confluence.get_all_restrictions_for_content(
+                args.parent_id
+            )
+            # The retrieved data includes restrictions/permissions as well as references to the parent page.
+            # Before applying it to the new page, we need to remove these old references.
+            # For more details on the data structure, refer to:
+            # https://developer.atlassian.com/cloud/confluence/rest/v1/api-group-content-restrictions/#api-wiki-rest-api-content-id-restriction-get
+
+            # Reset '_links' and '_expandable' fields from pointing to the parent page
+            restrictions_request = {"results": []}
+
+            for key, value in parent_restrictions.items():
+                if key == "_links" or key == "_expandable":
+                    continue
+                if "_links" in value.keys():
+                    value["_links"] = {}
+                if "_expandable" in value.keys():
+                    value["_expandable"] = {}
+
+                restrictions_request["results"].append(value)
+
+            # Directly making this request, as the current Confluence module lacks methods for setting restrictions
+
+            response = requests.request(
+                "PUT",
+                f'{args.confluence_url}/wiki/rest/api/content/{page["id"]}/restriction',
+                headers={
+                    "Accept": "application/json",
+                    "Content-Type": "application/json",
+                },
+                auth=HTTPBasicAuth(args.username, args.token),
+                data=json.dumps(restrictions_request),
+            )
+            if response.ok:
+                print("Page restrictions have been updated.")
+            else:
+                print("Could not update page restrictions !")
+                exit(f"ERROR: {response.text}")
+
+    except requests.exceptions.HTTPError as e:
+        exit(f"ERROR: {e}")
+
+
+if __name__ == "__main__":
+    main()