diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..92957c7137c9b4b7c6f594dc55200d24b36b0c02
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,204 @@
+image: maven:3.9.9-eclipse-temurin-21-alpine
+
+variables:
+  MAVEN_DEPLOY_CLI_OPTS: "--no-transfer-progress -DskipTests"
+  MAVEN_CLI_OPTS: >-
+    -s .m2/ci_settings.xml --batch-mode
+    -Dmaven.plugin.validation=brief
+  DOCKER_HOST: tcp://docker:2375
+  DOCKER_TLS_CERTDIR: ""
+  DOCKER_DRIVER: overlay2
+
+services:
+  - docker:dind
+
+cache:
+  paths:
+    - .m2/repository/
+    - target/
+
+stages:
+  - build
+  - test
+  - publish
+
+build:
+  stage: build
+  script:
+    - mvn clean compile $MAVEN_CLI_OPTS
+  artifacts:
+    paths:
+      - target/*.jar
+
+test:
+  stage: test
+  script:
+    - mvn test $MAVEN_CLI_OPTS
+
+verify:
+  stage: test
+  script:
+    - mvn verify $MAVEN_CLI_OPTS
+
+test-helm:
+  stage: test
+  before_script:
+    - apk add --no-cache helm git
+    - helm plugin install https://github.com/helm-unittest/helm-unittest.git
+  script:
+    - ./run_helm_test.sh
+
+.get-version:
+  before_script:
+    - export PROJECT_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout $MAVEN_CLI_OPTS)
+    - export PROJECT_ARTIFACTID=$(mvn help:evaluate -Dexpression=project.artifactId -q -DforceStdout $MAVEN_CLI_OPTS)
+    - export NEXUS_IMAGE_PREFIX="docker.ozg-sh.de/${PROJECT_ARTIFACTID}"
+    - export MERGE_REQUEST_TAG="MR-${CI_MERGE_REQUEST_IID}-${PROJECT_VERSION}"
+    - export SNAPSHOT_TAG="${CI_COMMIT_BRANCH}-${PROJECT_VERSION}"
+
+.publish-helm-chart:
+  before_script:
+    - apk add --no-cache helm curl
+  script:
+    - helm package --version=${HELM_CHART_VERSION} src/main/helm
+    - curl -u ${NEXUS_USER}:${NEXUS_PASSWORD} https://nexus.ozg-sh.de/service/rest/v1/components?repository=${NEXUS_HELM_REPO_NAME} -F file=@${PROJECT_ARTIFACTID}-${HELM_CHART_VERSION}.tgz
+
+# Merge request jobs
+push-merge-request-image-gitlab:
+  stage: publish
+  extends: .get-version
+  script:
+    - export IMAGE_TAGS="${CI_REGISTRY_IMAGE}:${MERGE_REQUEST_TAG},${CI_REGISTRY_IMAGE}:${MERGE_REQUEST_TAG}-${CI_COMMIT_SHORT_SHA}"
+    - mvn deploy -Pgitlab-deploy $MAVEN_DEPLOY_CLI_OPTS $MAVEN_CLI_OPTS
+  rules:
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+  when: manual
+
+push-merge-request-image-nexus:
+  stage: publish
+  extends: .get-version
+  script:
+    - export IMAGE_TAGS="${NEXUS_IMAGE_PREFIX}:${MERGE_REQUEST_TAG},${NEXUS_IMAGE_PREFIX}:${MERGE_REQUEST_TAG}-${CI_COMMIT_SHORT_SHA}"
+    - mvn deploy -Pnexus-deploy $MAVEN_DEPLOY_CLI_OPTS $MAVEN_CLI_OPTS
+  rules:
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+  when: manual
+
+push-merge-request-helm-nexus:
+  stage: publish
+  variables:
+    NEXUS_HELM_REPO_NAME: ozg-base-apps-snapshot
+  before_script:
+    - !reference [.get-version, before_script]
+    - !reference [.publish-helm-chart, before_script]
+  script:
+    - export HELM_CHART_VERSION=${PROJECT_VERSION}-MR-${CI_MERGE_REQUEST_IID}
+    - !reference [.publish-helm-chart, script]
+    - export HELM_CHART_VERSION=${PROJECT_VERSION}-MR-${CI_MERGE_REQUEST_IID}-${CI_COMMIT_SHORT_SHA}
+    - !reference [.publish-helm-chart, script]
+  rules:
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+  when: manual
+  artifacts:
+    paths:
+      - "*.tgz"
+
+# Snapshot jobs
+push-snapshot-image-gitlab:
+  stage: publish
+  extends: .get-version
+  script:
+    - export IMAGE_TAGS="${CI_REGISTRY_IMAGE}:${SNAPSHOT_TAG},${CI_REGISTRY_IMAGE}:${SNAPSHOT_TAG}-${CI_COMMIT_SHORT_SHA},${CI_REGISTRY_IMAGE}:snapshot-latest"
+    - mvn deploy -Pgitlab-deploy $MAVEN_DEPLOY_CLI_OPTS $MAVEN_CLI_OPTS
+  only:
+    - main
+
+push-snapshot-image-nexus:
+  stage: publish
+  extends: .get-version
+  script:
+    - export IMAGE_TAGS="${NEXUS_IMAGE_PREFIX}:${SNAPSHOT_TAG},${NEXUS_IMAGE_PREFIX}:${SNAPSHOT_TAG}-${CI_COMMIT_SHORT_SHA},${NEXUS_IMAGE_PREFIX}:snapshot-latest"
+    - mvn deploy -Pnexus-deploy $MAVEN_DEPLOY_CLI_OPTS $MAVEN_CLI_OPTS
+  only:
+    - main
+
+push-snapshot-helm-nexus:
+  stage: publish
+  variables:
+    NEXUS_HELM_REPO_NAME: ozg-base-apps-snapshot
+  before_script:
+    - !reference [.get-version, before_script]
+    - !reference [.publish-helm-chart, before_script]
+  script:
+    - export HELM_CHART_VERSION=${PROJECT_VERSION}-main
+    - !reference [ .publish-helm-chart, script ]
+    - export HELM_CHART_VERSION=${PROJECT_VERSION}-main-${CI_COMMIT_SHORT_SHA}
+    - !reference [.publish-helm-chart, script]
+  only:
+    - main
+  artifacts:
+    paths:
+      - "*.tgz"
+
+gitops-snapshot:
+  variables:
+    GIT_NAME: gitlab-ci
+    GIT_EMAIL: lsh-git@rz.uni-kiel.de
+    GITOPS_ENVIRONMENT: dev
+    VALUES_FILE_NAME: afm-adapter-values.yaml
+    VALUES_FILE: ${GITOPS_ENVIRONMENT}/application/values/${VALUES_FILE_NAME}
+  stage: publish
+  before_script:
+    - apk add --no-cache git yq  # yq-go in newer versions of alpine (from 3.20 on)
+    - git config user.name '${GIT_NAME}'
+    - git config user.email '${GIT_EMAIL}'
+    - !reference [.get-version, before_script]
+  script:
+    - git clone https://${GITEA_USER}:${GITEA_TOKEN}@git.ozg-sh.de/ozgcloud-devops/gitops.git
+    - cd gitops
+    - yq '.afm_adapter.image.tag = ${SNAPSHOT_TAG}-${CI_COMMIT_SHORT_SHA}' ${VALUES_FILE}
+    - yq '.afm_adapter.helm.version = ${PROJECT_VERSION}-main-${CI_COMMIT_SHORT_SHA}' ${VALUES_FILE}
+    - git add ${VALUES_FILE}
+    - git commit -m 'code.sh GitLab CI rollout ${environment} ${PROJECT_ARTIFACTID} version ${SNAPSHOT_TAG}-${CI_COMMIT_SHORT_SHA}'
+    - git push https://${GITEA_USER}:${GITEA_TOKEN}@git.ozg-sh.de/ozgcloud-devops/gitops.git
+  # only:
+  #  - main
+  rules:
+    when: never  # waiting for gitea access token
+
+# Release jobs
+push-release-image-gitlab:
+  stage: publish
+  extends: .get-version
+  script:
+    - mvn versions:set -DnewVersion=${CI_COMMIT_TAG} $MAVEN_CLI_OPTS
+    - export IMAGE_TAGS="${CI_REGISTRY_IMAGE}:release-${CI_COMMIT_TAG},${CI_REGISTRY_IMAGE}:latest"
+    - mvn deploy -Pgitlab-deploy $MAVEN_DEPLOY_CLI_OPTS $MAVEN_CLI_OPTS
+  rules:
+    - if: $CI_COMMIT_TAG
+
+push-release-image-nexus:
+  stage: publish
+  extends: .get-version
+  script:
+    - mvn versions:set -DnewVersion=${CI_COMMIT_TAG} $MAVEN_CLI_OPTS
+    - export IMAGE_TAGS="${NEXUS_IMAGE_PREFIX}:release-${CI_COMMIT_TAG},${NEXUS_IMAGE_PREFIX}:latest"
+    - mvn deploy -Pnexus-deploy $MAVEN_DEPLOY_CLI_OPTS $MAVEN_CLI_OPTS
+  rules:
+    - if: $CI_COMMIT_TAG
+
+push-release-helm-nexus:
+  stage: publish
+  variables:
+    NEXUS_HELM_REPO_NAME: ozg-base-apps
+  before_script:
+    - !reference [.get-version, before_script]
+    - !reference [.publish-helm-chart, before_script]
+  script:
+    - export HELM_CHART_VERSION=${PROJECT_VERSION}
+    - !reference [.publish-helm-chart, script]
+  rules:
+    - if: $CI_COMMIT_TAG
+  artifacts:
+    paths:
+      - "*.tgz"
diff --git a/.m2/ci_settings.xml b/.m2/ci_settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..06b0cb517d6b244915cb2e8a44a717a77176d09e
--- /dev/null
+++ b/.m2/ci_settings.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<settings xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 http://maven.apache.org/xsd/settings-1.2.0.xsd"
+          xmlns="http://maven.apache.org/SETTINGS/1.2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <localRepository>.m2/repository</localRepository>
+
+    <servers>
+        <server>
+            <id>ozg-nexus</id>
+            <username>${env.NEXUS_USER}</username>
+            <password>${env.NEXUS_PASSWORD}</password>
+        </server>
+        <server>
+            <id>ozg-snapshots-nexus</id>
+            <username>${env.NEXUS_USER}</username>
+            <password>${env.NEXUS_PASSWORD}</password>
+        </server>
+        <server>
+            <id>codesh-gitlab-maven</id>
+            <configuration>
+                <httpHeaders>
+                    <property>
+                        <name>Job-Token</name>
+                        <value>${CI_JOB_TOKEN}</value>
+                    </property>
+                </httpHeaders>
+            </configuration>
+        </server>
+    </servers>
+    <profiles>
+        <profile>
+            <id>ci-default</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <repositories>
+                <repository>
+                    <id>codesh-gitlab-maven</id>
+                    <url>${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/maven</url>
+                </repository>
+                <repository>
+                    <id>ozg-nexus</id>
+                    <name>ozg-releases</name>
+                    <url>https://nexus.ozg-sh.de/repository/ozg-releases/</url>
+                </repository>
+                <repository>
+                    <id>ozg-snapshots-nexus</id>
+                    <name>ozg-snapshots</name>
+                    <url>https://nexus.ozg-sh.de/repository/ozg-snapshots/</url>
+                    <snapshots>
+                        <enabled>true</enabled>
+                    </snapshots>
+                </repository>
+            </repositories>
+        </profile>
+    </profiles>
+</settings>
diff --git a/src/main/helm/Chart.yaml b/src/main/helm/Chart.yaml
index 63f41e33d74056096e8c7066e712b1485de937d9..9a4ddff7dd452533dfcb589a70624c173d0c903f 100644
--- a/src/main/helm/Chart.yaml
+++ b/src/main/helm/Chart.yaml
@@ -26,6 +26,6 @@ apiVersion: v1
 appVersion: "1.1"
 description: A Helm chart for Xta-Adapter
 name: xta-adapter
-version: 0.0.0-MANAGED-BY-JENKINS
+version: 0.0.0-MANAGED-BY-CI
 icon: https://simpleicons.org/icons/helm.svg