diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 000000000..7d422503e
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,4 @@
+[run]
+branch = True
+relative_files = True
+source = karapace
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index d376a1e19..da2839a3d 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -37,7 +37,13 @@ jobs:
 
     - run: make install version
     - run: make unit-tests
-    - run: make integration-tests PYTEST_ARGS="--random-order"
+      env:
+        COVERAGE_FILE: ".coverage.${{ matrix.python-version }}"
+        PYTEST_ARGS: "--cov=karapace --cov-append"
+    - run: make integration-tests
+      env:
+        COVERAGE_FILE: ".coverage.${{ matrix.python-version }}"
+        PYTEST_ARGS: "--cov=karapace --cov-append --random-order"
 
     - name: Archive logs
       uses: actions/upload-artifact@v4
@@ -45,3 +51,41 @@ jobs:
       with:
         name: karapace-integration-test-logs-${{ matrix.python-version }}
         path: /tmp/ci-logs
+    - name: Archive coverage file
+      uses: actions/upload-artifact@v4
+      with:
+        name: "coverage-${{ matrix.python-version }}"
+        path: ".coverage.${{ matrix.python-version }}"
+
+  coverage:
+    name: Coverage report
+    runs-on: ubuntu-latest
+    needs: tests
+    permissions:
+      pull-requests: write
+      contents: write
+    steps:
+    - uses: actions/checkout@v4
+
+    - name: Download coverage
+      id: download_coverage
+      uses: actions/download-artifact@v4
+      with:
+        pattern: coverage-*
+        merge-multiple: true
+
+    - run: make karapace/version.py
+
+    - name: Post coverage comment
+      id: post_coverage_comment
+      uses: py-cov-action/python-coverage-comment-action@v3
+      with:
+        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        MERGE_COVERAGE_FILES: true
+
+    - name: Store PR comment to be posted
+      uses: actions/upload-artifact@v4
+      if: steps.post_coverage_comment.outputs.COMMENT_FILE_WRITTEN == 'true'
+      with:
+        name: python-coverage-comment-action
+        path: python-coverage-comment-action.txt
diff --git a/requirements/requirements-dev.in b/requirements/requirements-dev.in
index 464a9f064..73dfa6bad 100644
--- a/requirements/requirements-dev.in
+++ b/requirements/requirements-dev.in
@@ -7,6 +7,7 @@ pdbpp
 # testing
 filelock
 pytest
+pytest-cov
 pytest-xdist[psutil]
 pytest-timeout
 pytest-random-order
diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt
index 9cd55ec2d..5691d5db7 100644
--- a/requirements/requirements-dev.txt
+++ b/requirements/requirements-dev.txt
@@ -52,6 +52,8 @@ configargparse==1.7
     # via locust
 confluent-kafka==2.3.0
     # via -r requirements.txt
+coverage[toml]==7.5.3
+    # via pytest-cov
 cramjam==2.8.3
     # via
     #   -r requirements.txt
@@ -176,9 +178,12 @@ pyrepl==0.9.0
 pytest==8.2.2
     # via
     #   -r requirements-dev.in
+    #   pytest-cov
     #   pytest-random-order
     #   pytest-timeout
     #   pytest-xdist
+pytest-cov==5.0.0
+    # via -r requirements-dev.in
 pytest-random-order==1.1.1
     # via -r requirements-dev.in
 pytest-timeout==2.3.1
@@ -227,6 +232,7 @@ tenacity==8.3.0
     # via -r requirements.txt
 tomli==2.0.1
     # via
+    #   coverage
     #   locust
     #   pytest
 typing-extensions==4.12.1