diff --git a/.github/workflows/commit.yaml b/.github/workflows/commit.yaml
new file mode 100644
index 0000000..7a7c573
--- /dev/null
+++ b/.github/workflows/commit.yaml
@@ -0,0 +1,112 @@
+name: Main commit workflow
+
+on:
+  push:
+    branches:
+      - main
+    tags:
+      - v*
+    paths-ignore:
+      - '**.md'
+  pull_request:
+    branches:
+      - main
+    paths-ignore:
+      - '**.md'
+
+env:
+  platforms: linux/amd64
+  registry: core.harbor.onmetal.de
+  image: core.harbor.onmetal.de/onmetal/ipam
+
+jobs:
+  go-lint:
+    runs-on: [ self-hosted, Linux, X64 ]
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+      - name: Setup golang
+        uses: actions/setup-go@v2
+        with:
+          go-version: 1.17
+      - name: Configure git for private modules
+        run: git config --global url."https://${{ secrets.GIT_USER }}:${{ secrets.GIT_PASSWORD }}@github.com".insteadOf "https://github.com"
+      - name: Lint golang sources
+        uses: golangci/golangci-lint-action@v2
+        with:
+          version: v1.42
+          args: -e S1008
+  go-test:
+    runs-on: [ self-hosted, Linux, X64 ]
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+      - name: Setup golang
+        uses: actions/setup-go@v2
+        with:
+          go-version: 1.17
+      - name: Configure git for private modules
+        run: git config --global url."https://${{ secrets.GIT_USER }}:${{ secrets.GIT_PASSWORD }}@github.com".insteadOf "https://github.com"
+      - name: Run tests
+        run: make test
+  kustomize-test:
+    runs-on: [ self-hosted, Linux, X64 ]
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+      - name: Complie manifests
+        uses: azure/k8s-bake@v1
+        with:
+          renderEngine: 'kustomize'
+          kustomizationPath: './config/default/'
+          kubectl-version: 'latest'
+          silent: 'false'
+  docker-build:
+    needs: [ kustomize-test, go-lint, go-test ]
+    runs-on: [ self-hosted, Linux, X64 ]
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+      - name: Get metadata for docker
+        uses: docker/metadata-action@v3
+        id: meta
+        with:
+          images: |
+            ${{ env.image }}
+          tags: |
+            type=schedule
+            type=ref,event=branch
+            type=ref,event=tag
+            type=ref,event=pr
+            type=semver,pattern={{version}}
+            type=sha
+          flavor: |
+            latest=${{ github.ref == 'refs/heads/main' }}
+      - name: Set up Docker Context for Buildx
+        id: buildx-context
+        run: |
+          docker context create builders
+      - name: Set up Docker Buildx
+        timeout-minutes: 5
+        uses: docker/setup-buildx-action@v1
+        with:
+          version: latest
+          endpoint: builders
+      - name: Login to Container Registry
+        uses: docker/login-action@v1
+        with:
+          registry: ${{ env.registry }}
+          username: ${{ secrets.DOCKERHUB_USERNAME }}
+          password: ${{ secrets.DOCKERHUB_TOKEN }}
+      - name: Build and push
+        uses: docker/build-push-action@v2
+        with:
+          context: .
+          platforms: ${{ env.platforms }}
+          push: true
+          tags: ${{ steps.meta.outputs.tags }}
+          labels: ${{ steps.meta.outputs.labels }}
+          build-args: |
+            GOPRIVATE=github.com/onmetal/*
+            GIT_USER=${{ secrets.GIT_USER }}
+            GIT_PASSWORD=${{ secrets.GIT_PASSWORD }}
diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml
deleted file mode 100644
index f0b8d75..0000000
--- a/.github/workflows/docker.yaml
+++ /dev/null
@@ -1,49 +0,0 @@
-name: Release - Docker
-
-on:
-  push:
-    tags:
-      - v*
-
-env:
-  IMAGE_NAME: core.harbor.onmetal.de/onmetal/ipam
-
-jobs:
-  push:
-    runs-on: ubuntu-latest
-    if: github.event_name == 'push'
-    permissions:
-      contents: read
-      packages: write
-
-    steps:
-      - uses: actions/checkout@v2
-
-      - name: Build image
-        run: docker build . --file Dockerfile --tag $IMAGE_NAME --build-arg GOPRIVATE="github.com/onmetal/*" --build-arg GIT_USER=${{ secrets.GIT_USER }} --build-arg GIT_PASSWORD=${{ secrets.GIT_PASSWORD }}
-
-      - name: Log into registry
-        run: echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
-
-      - name: Push image
-        run: |
-          IMAGE_ID=$IMAGE_NAME
-
-          # Change all uppercase to lowercase
-          IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
-
-          # Strip git ref prefix from version
-          VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
-
-          # Strip "v" prefix from tag name
-          [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
-
-          # Use Docker `latest` tag convention
-          [ "$VERSION" == "master" ] && VERSION=latest
-
-          echo IMAGE_ID=$IMAGE_ID
-          echo VERSION=$VERSION
-
-          docker tag $IMAGE_NAME $IMAGE_ID:$VERSION
-          docker push $IMAGE_ID:$VERSION
-          docker push $IMAGE_NAME
diff --git a/.github/workflows/helm.yaml b/.github/workflows/helm.yaml
deleted file mode 100644
index 06835cc..0000000
--- a/.github/workflows/helm.yaml
+++ /dev/null
@@ -1,35 +0,0 @@
-name: Release - Helm
-
-on:
-  push:
-    branches:
-      - main
-
-jobs:
-  release:
-    runs-on: ubuntu-latest
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-        with:
-          fetch-depth: 0
-
-      - name: Configure Git
-        run: |
-          git config user.name "$GITHUB_ACTOR"
-          git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
-
-      - name: Install Helm
-        uses: azure/setup-helm@v1
-        with:
-          version: v3.4.0
-
-      - name: Run chart-releaser
-        uses: goodsmileduck/helm-push-action@v3.3.1
-        env:
-          SOURCE_DIR: 'charts'
-          CHART_FOLDER: 'ipam'
-          FORCE: 'True'
-          CHARTMUSEUM_URL: ' https://core.harbor.onmetal.de/chartrepo/onmetal'
-          CHARTMUSEUM_USER: '${{ secrets.CHARTMUSEUM_USER }}'
-          CHARTMUSEUM_PASSWORD: ${{ secrets.CHARTMUSEUM_PASSWORD }}
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
deleted file mode 100644
index 133c848..0000000
--- a/.github/workflows/tests.yaml
+++ /dev/null
@@ -1,58 +0,0 @@
-name: Tests
-
-on:
-  push:
-    branches:
-      - main
-  pull_request:
-    branches:
-      - main
-
-jobs:
-  go-tests:
-    runs-on: [self-hosted, Linux, X64]
-    steps:
-      - uses: actions/checkout@v2
-
-      - uses: actions/setup-go@v2
-        with:
-          go-version: 1.17
-
-      - name: Configure git for private modules
-        run: git config --global url."https://${{ secrets.GIT_USER }}:${{ secrets.GIT_PASSWORD }}@github.com".insteadOf "https://github.com"
-
-      - name: Test
-        run: make test
-
-  go-lint:
-    runs-on: [self-hosted, Linux, X64]
-    steps:
-      - uses: actions/checkout@v2
-      - uses: actions/setup-go@v2
-        with:
-          go-version: 1.17
-
-      - name: Configure git for private modules
-        run: git config --global url."https://${{ secrets.GIT_USER }}:${{ secrets.GIT_PASSWORD }}@github.com".insteadOf "https://github.com"
-
-      - name: golangci-lint
-        uses: golangci/golangci-lint-action@v2
-        with:
-          version: v1.42
-          args: -e S1008
-
-  helm-lint:
-    runs-on: [self-hosted, Linux, X64]
-    steps:
-      - uses: actions/checkout@v2
-      - uses: actions/setup-go@v2
-        with:
-          go-version: 1.17
-
-      - uses: azure/setup-helm@v1
-        with:
-          version: 'v3.6.2'
-
-      - name: Lint Helm
-        run: |
-          helm lint charts/ipam --strict
\ No newline at end of file