diff --git a/templates/module-release.yml b/templates/module-release.yml
new file mode 100644
index 0000000000000000000000000000000000000000..dcc7725b7dda395b08a4524eb47bb073520e635b
--- /dev/null
+++ b/templates/module-release.yml
@@ -0,0 +1,56 @@
+spec:
+  inputs:
+    # Job and Stage name
+    as:
+      default: 'module-release'
+      description: 'Defines the name of this job.'
+    stage:
+      default: 'release'
+      description: 'Defines the stage that this job will belong to.'
+
+    root_dir:
+      default: ${CI_PROJECT_DIR}
+      description: 'Root directory for the OpenTofu module.'
+
+    module_name:
+      default: ${CI_PROJECT_NAME}
+      description: |
+        Name of the module that should be published.
+        Must comply with the naming restrictions of the module registry.
+
+    module_system:
+      default: local
+      description: |
+        System that this module can be used with.
+        e.g. 'local' or 'azurerm'
+    
+    module_version:
+      description: |
+        Version of the module that should be published.
+        e.g. '0.1.0'
+      default: ${CI_COMMIT_TAG}
+
+---
+
+'$[[ inputs.as ]]':
+  stage: $[[ inputs.stage ]]
+  image: curlimages/curl:8.8.0
+  variables:
+    TAR_FILENAME: /tmp/${CI_PROJECT_NAME}-${CI_COMMIT_REF_NAME}.tgz
+    TF_ROOT: $[[ inputs.root_dir ]]
+    UPLOAD_URL:
+      ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/terraform/modules/$[[ inputs.module_name ]]/$[[ inputs.module_system ]]/$[[ inputs.module_version ]]/file
+  script:
+    - >-
+      tar -vczf "$TAR_FILENAME"
+      -C "$TF_ROOT"
+      --exclude=./.git
+      --exclude=./.terraform*
+      .
+    - >-
+      curl
+      --fail-with-body
+      --location
+      --header "JOB-TOKEN: ${CI_JOB_TOKEN}"
+      --upload-file "$TAR_FILENAME"
+      "$UPLOAD_URL"
diff --git a/tests/integration-tests/ModuleRelease.gitlab-ci.yml b/tests/integration-tests/ModuleRelease.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..77db9b05e033ca0d28538b45ccb36b988d9ad40b
--- /dev/null
+++ b/tests/integration-tests/ModuleRelease.gitlab-ci.yml
@@ -0,0 +1,41 @@
+variables:
+  MODULE_SYSTEM: local
+  MODULE_VERSION: 0.0.0-$CI_COMMIT_SHA
+
+include:
+  - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/module-release@$CI_COMMIT_SHA
+    inputs:
+      root_dir: $TEST_TF_ROOT/modules/random-pet
+      module_system: $MODULE_SYSTEM
+      module_version: $MODULE_VERSION
+
+stages: [release, destroy]
+
+# Required to run everything immediately, instead of manually.
+
+module-release:
+  rules: [{when: always}]
+
+delete-module:
+  stage: destroy
+  image: curlimages/curl:latest
+  needs: [module-release]
+  rules: [{when: always}]
+  script:
+    - >-
+      PACKAGE_ID=$(
+      curl
+      --header "JOB-TOKEN: ${CI_JOB_TOKEN}"
+      --fail-with-body
+      "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages?package_type=terraform_module&package_version=${MODULE_VERSION}"
+      | sed -E 's/\[\{"id":"?([^,"]*)"?.*/\1/'
+      )
+    - echo deleting package $PACKAGE_ID
+    - >-
+      curl
+      --request DELETE
+      --header "JOB-TOKEN: ${CI_JOB_TOKEN}"
+      --fail-with-body
+      "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/${PACKAGE_ID}"
+
+
diff --git a/tests/integration.gitlab-ci.yml b/tests/integration.gitlab-ci.yml
index ca0369b60c6531be1b47a082ae0b61b0b39213cf..6411f291aceba82f8f69fce3d2ccea981eb8864d 100644
--- a/tests/integration.gitlab-ci.yml
+++ b/tests/integration.gitlab-ci.yml
@@ -12,4 +12,5 @@ component:
       - PIPELINE_NAME: [Defaults]
       - PIPELINE_NAME: [JobTemplates]
       - PIPELINE_NAME: [TestJob]
+      - PIPELINE_NAME: [ModuleRelease]
       - PIPELINE_NAME: [Destroy]