diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..06731ffc6490924117096cd76bc53f19405e7369
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.venv/
+.pyenv/
+
+*.pyc
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e987d7fdfd9bd9feb053b15f406934cae7345f3e..57b17b8b15badf7a54e8e40b3e584e0b6a45f94a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -37,6 +37,7 @@ workflow:
 
 stages:
   - build
+  - test
   - release
 
 build image:
@@ -66,6 +67,22 @@ build image:
     - echo ${TARGET_ENV}
     - crane cp ${CI_REGISTRY_IMAGE}:${DOCKER_TAG} ${CI_REGISTRY_IMAGE}:${TARGET_ENV}
 
+test image:
+  stage: test
+  image:
+    name: ghcr.io/astral-sh/uv:0.6-python3.12-bookworm
+  needs:
+    - job: build image
+  variables:
+    KEYCLOAK_HOSTNAME: keycloak
+  services:
+    - name: "${CI_REGISTRY_IMAGE}:${DOCKER_TAG}"
+      alias: "$KEYCLOAK_HOSTNAME"
+  before_script:
+    - bash ./tests/healthcheck_for_tests.sh
+  script:
+    - uv run --with pytest --with python-keycloak pytest -v tests
+
 release latest image:
   stage: release
   extends: .tag image
diff --git a/Dockerfile b/Dockerfile
index 56a279bd81c1d8ba74d6e159d005d9de54f221b2..7d3391535518d0ee2c6a0162f3d06d31acb6a7fe 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,7 +3,15 @@ ARG KEYCLOAK_VERSION=26.1
 FROM quay.io/keycloak/keycloak:${KEYCLOAK_VERSION} AS keycloak
 
 COPY import /opt/keycloak/data/import
-
 COPY healthcheck_keycloak.sh /opt/keycloak/bin/
 
 ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
+CMD ["start-dev", "--import-realm"]
+
+ENV KC_HEALTH_ENABLED=true
+ENV KC_METRICS_ENABLED=true
+
+EXPOSE 8080 9000
+
+HEALTHCHECK --interval=10s --timeout=10s --start-period=30s --retries=5 \
+    CMD "/opt/keycloak/bin/healthcheck_keycloak.sh"
diff --git a/tests/healthcheck_for_tests.sh b/tests/healthcheck_for_tests.sh
new file mode 100644
index 0000000000000000000000000000000000000000..3bdfd5be50ae605b7cb4ae5197825faa2e9785cb
--- /dev/null
+++ b/tests/healthcheck_for_tests.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+if [ -z "$KEYCLOAK_HOSTNAME" ]; then
+    echo "KEYCLOAK_HOSTNAME is not set. Exiting ..."
+    exit 1
+fi
+
+DURATION=60
+INTERVAL=5
+
+HEALTH_CHECK_URL="http://${KEYCLOAK_HOSTNAME}:9000/health/live"
+END_TIME=$(( $(date +%s) + DURATION ))
+
+check_health() {
+    curl -s "$HEALTH_CHECK_URL" > /dev/null
+}
+
+while [ $(date +%s) -lt $END_TIME ]; do
+     if check_health; then
+      echo "Health endpoint is responding correctly."
+      exit 0
+    else
+      echo "Health endpoint is not responding. Retrying in $INTERVAL seconds ..."
+      sleep "$INTERVAL"
+    fi
+done
+
+echo "Timeout reached. Health endpoint did not respond within $DURATION seconds."
+exit 1
diff --git a/tests/test_keycloak.py b/tests/test_keycloak.py
new file mode 100644
index 0000000000000000000000000000000000000000..1e32a7d27fd67b2554f06de70f11cde958b00c98
--- /dev/null
+++ b/tests/test_keycloak.py
@@ -0,0 +1,45 @@
+import os
+
+import keycloak
+import pytest
+
+users ={
+    'admin': {
+        'sub': 'c97facc2-92ec-4fa6-80cf-a08ed957255b',
+        'preferred_username': 'admin',
+        'password': 'password',
+        'email': 'admin@example.com',
+        'given_name': 'AdminFN',
+        'family_name': 'AdminLN',
+    },
+    'test': {
+        'sub': '33305763-5e61-4458-94bc-8098e9288a73',
+        'preferred_username': 'test',
+        'password': 'password',
+        'email': 'test@example.com',
+        'given_name': 'TestFN',
+        'family_name': 'TestLN',
+    },
+}
+
+@pytest.fixture(scope='session')
+def keycloak_hostname():
+    return os.environ.get('KEYCLOAK_HOSTNAME', 'localhost')
+
+@pytest.fixture(scope='session')
+def nomad_public(keycloak_hostname):
+    return keycloak.KeycloakOpenID(
+        server_url=f'http://{keycloak_hostname}:8080',
+        realm_name='fairdi_nomad_test',
+        client_id='nomad_public',
+    )
+
+@pytest.mark.parametrize('user', users.values(), ids=users.keys())
+def test_user(nomad_public, user):
+    tokens = nomad_public.token(user['preferred_username'], user['password'])
+    access_token = tokens.get('access_token')
+    token_info = nomad_public.decode_token(access_token)
+
+    ignored_keys = {'password'}
+    for key in user.keys() - ignored_keys:
+        assert token_info.get(key) == user[key]