diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 97d7147f55e00d5eb2b5ba7183321c737e8b3bfc..eba9ea0588c8f6f0d8c914308a634181329d9aee 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -3,7 +3,6 @@ stages:
   - setup-cluster
   - install-apps
   - test
-  - e2e-test
   - cleanup
 
 ci_test_image:
@@ -65,27 +64,6 @@ testinfra:
     - cd test/
     - py.test -v --connection=ansible --ansible-inventory=./inventory.yml --hosts='ansible://*'
 
-behave:
-  # Needs to run in seperate stage since two concurent jobs
-  # can't fetch the same cache, see
-  # https://gitlab.com/gitlab-org/gitlab-runner/issues/1151
-  stage: e2e-test
-  image: ${CI_REGISTRY_IMAGE}/bootstrap-ci
-  script:
-    # Debug failing CI caches which happened in the past
-    - find . -name behave.ini
-    - ls -al test/behave
-    - grep -v 'keycloak.admin.password' test/behave/behave.ini
-    # Wait for proper LE cert to get served
-    - timeout -t 1800 sh -c 'while ! curl --cacert ./test/letsencrypt_staging_bundle.pem -s https://auth.ci-${CI_PIPELINE_ID}.ci.openappstack.net/auth/ > /dev/null; do date; echo "Waiting for LE cert..."; sleep 5; done'
-    - cd test/behave/
-    - behave -D headless=True --stop
-  artifacts:
-    paths:
-    - test/behave/screenshots/
-    expire_in: 1 month
-    when: on_failure
-
 terminate:
   stage: cleanup
   image: "${CI_REGISTRY_IMAGE}/bootstrap-ci"
diff --git a/ansible/group_vars/cluster/settings.yml.example b/ansible/group_vars/cluster/settings.yml.example
index b46ea3744a5cc28983ddceb333ce58854e671b03..6fce66e6983d29a9def8249681984d176a1e2e8f 100644
--- a/ansible/group_vars/cluster/settings.yml.example
+++ b/ansible/group_vars/cluster/settings.yml.example
@@ -8,12 +8,9 @@ domain: "example.com"
 admin_email: "admin@example.com"
 # Label of this deployment.
 release_name: "test"
-# Keycloak administrator password. If you do not change this value, it gets
-# generated and stored in ./secrets/keycloak_admin_password. You can also choose
+# Nextcloud administrator password. If you do not change this value, it gets
+# generated and stored in `secrets/nextcloud_admin_password`. You can also choose
 # your own password and fill it in here instead.
-keycloak_password: "{{ lookup('password', './secrets/keycloak_admin_password chars=ascii_letters') }}"
-# Nextcloud administrator password. Works the same as keycloak password, except
-# it is stored in `secrets/nextcloud_admin_password`.
 nextcloud_password: "{{ lookup('password', './secrets/nextcloud_admin_password chars=ascii_letters') }}"
 # If this is "true" TLS certificates will be requested at the Let's Encrypt
 # staging server. If this is "false", you use Let's Encrypt's production server.
diff --git a/ansible/roles/setup/files/k8s-config/storage/keycloak-postgres-storage.yml b/ansible/roles/setup/files/k8s-config/storage/keycloak-postgres-storage.yml
deleted file mode 100644
index aaa2932b747dd5f03a225cc6cd4707630c3c8a3a..0000000000000000000000000000000000000000
--- a/ansible/roles/setup/files/k8s-config/storage/keycloak-postgres-storage.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-kind: PersistentVolume
-apiVersion: v1
-metadata:
-  name: keycloak-postgres
-  labels:
-    type: local
-spec:
-  capacity:
-    storage: 1Gi
-  accessModes:
-    - ReadWriteOnce
-  persistentVolumeReclaimPolicy: Recycle
-  hostPath:
-    path: "/var/local/k8s/keycloak-postgres"
-    type: DirectoryOrCreate
diff --git a/ansible/roles/setup/tasks/main.yml b/ansible/roles/setup/tasks/main.yml
index 41dc804c405af1d97c8cdf43d559987985b13581..0660bbbcab3e8e3a22c44cf011a28d5037711abd 100644
--- a/ansible/roles/setup/tasks/main.yml
+++ b/ansible/roles/setup/tasks/main.yml
@@ -143,40 +143,6 @@
     - 'oas'
     - 'oas-apps'
 
-- name: Configure Keycloak realm configmap
-  tags:
-    - keycloak
-  k8s:
-    state: present
-    namespace: 'oas-apps'
-    force: True
-    definition:
-      kind: ConfigMap
-      metadata:
-        name: keycloak-realm
-      data:
-        realm.json: |-
-          {
-            "realm": "OpenAppStack",
-            "enabled": true,
-            "sslRequired": "external",
-            "registrationAllowed": true,
-            "requiredCredentials": [
-              "password"
-            ],
-            "roles": {
-              "realm": [
-                {
-                  "name": "user",
-                  "description": "User privileges"
-                },
-                {
-                  "name": "admin",
-                  "description": "Administrator privileges"
-                }
-              ]
-            }
-          }
 
 - name: Ensure /oas/config/values/apps directory
   tags:
@@ -196,13 +162,10 @@
     mode: '0600'
   with_items:
     - /oas/config/values/apps/traefik.yaml
-    - /oas/config/values/apps/keycloak.yaml
 
 - name: Apply helmfiles
   tags:
     - helmfile
-    - keycloak
   environment:
-    - KEYCLOAK_PASSWORD: "{{ keycloak_password }}"
     - NEXTCLOUD_PASSWORD: "{{ nextcloud_password }}"
   command: '/usr/local/bin/helmfile -b /snap/bin/helm -e oas -f /oas/source/repos/helmfiles/helmfile.d/ apply --suppress-secrets'
diff --git a/test/behave/README.md b/test/behave/README.md
index 31069677b2026a7c79b40a03ac49fe2e9ea19806..c7e083c0cce2cc074c8625917d2c37183750c7c8 100644
--- a/test/behave/README.md
+++ b/test/behave/README.md
@@ -5,5 +5,3 @@
       apk --no-cache add git
       git clone https://code.greenhost.net/openappstack/bootstrap.git
       cd bootstrap/test/behave
-      behave -D keycloak.admin.url=https://auth.ci-20410.ci.openappstack.net/auth/admin/master/console/ \
-             -D keycloak.admin.password=…
diff --git a/test/behave/features/environment.py b/test/behave/features/environment.py
index 55df3d2909130a2505f8a2ffc6a24bad1ce411c8..eb9cac121b913cf7a9c43880c944e63dfc21dac1 100644
--- a/test/behave/features/environment.py
+++ b/test/behave/features/environment.py
@@ -22,13 +22,6 @@ def save_screenshot(context, step):
 def before_all(context):
     """Run at the very beginning."""
     userdata = context.config.userdata
-    context.keycloak = {}
-    context.keycloak['admin'] = {}
-    context.keycloak['admin']['url'] = userdata.get('keycloak.admin.url')
-    context.keycloak['admin']['username'] = \
-        userdata.get('keycloak.admin.username', 'keycloak')
-    context.keycloak['admin']['password'] = \
-        userdata.get('keycloak.admin.password')
 
     headless = userdata.get('headless', 'False')
 
diff --git a/test/behave/features/keycloak.feature b/test/behave/features/keycloak.feature
deleted file mode 100644
index 845f0cf472928de3604ffd9a40cd7ca0f6ad7803..0000000000000000000000000000000000000000
--- a/test/behave/features/keycloak.feature
+++ /dev/null
@@ -1,16 +0,0 @@
-Feature: Test keycloak admin login
-  As an OAS admin
-  I want to be able to login to the keycloak admin console
-
-Scenario: Open keycloak admin console
-  Given the title is not "Log in to Keycloak"
-  When I open the keycloak admin console url
-  Then I wait on element "input#username" for 25000ms to be visible
-  And  I expect that the title is "Log in to Keycloak"
-
-Scenario: Login to keycloak
-  Given the title is "Log in to Keycloak"
-  When I enter the "keycloak" "admin" "username" in the inputfield "input#username"
-  When I enter the "keycloak" "admin" "password" in the inputfield "input#password"
-  And I click on the button "input#kc-login"
-  Then I wait on element "input#displayName" for 25000ms to be visible
diff --git a/test/behave/features/steps/login.py b/test/behave/features/steps/login.py
index a8d1ee722649a6810766b6e897bb3507447d7c3f..4eec789c80a60c5a613d9fae05b3fd4cae9f3429 100644
--- a/test/behave/features/steps/login.py
+++ b/test/behave/features/steps/login.py
@@ -4,13 +4,6 @@ from behave import given, when
 from behave_webdriver.steps import *
 
 
-@when(u'I open the keycloak admin console url')
-@given(u'I open the keycloak admin console url')
-def step_impl(context):
-    """Login to the keycloak admin console."""
-    context.behave_driver.get(context.keycloak['admin']['url'])
-
-
 @when(u'I enter the "{section}" "{user}" "{cred_type}" in the inputfield "{element}"')
 def step_impl(context, section, user, cred_type, element):
     """Enter username/password into login inputfields."""
diff --git a/test/ci-bootstrap.py b/test/ci-bootstrap.py
index 6e14e2ca51ba21ccabefd8952dc8bc6401e7c3af..5ef2493c2d876c7171b5fefa55e6152617195383 100755
--- a/test/ci-bootstrap.py
+++ b/test/ci-bootstrap.py
@@ -262,16 +262,9 @@ def write_behave_config(settings=None):
     if settings is None:
         with open(SETTINGS_FILE) as stream:
             settings = yaml.safe_load(stream)
-    with open('./secrets/keycloak_admin_password', 'r') as stream:
-        keycloak_admin_password = yaml.safe_load(stream)
     behave_config = configparser.ConfigParser()
     behave_config['behave'] = {'stop': True}
     behave_config['behave.userdata'] = {}
-    behave_config['behave.userdata']['keycloak.admin.url'] = \
-        'https://auth.{}/auth/admin/master/console/'.format(settings['domain'])
-    behave_config['behave.userdata']['keycloak.admin.username'] = 'keycloak'
-    behave_config['behave.userdata']['keycloak.admin.password'] = \
-        keycloak_admin_password
     with open('./behave/behave.ini', 'w') as configfile:
         behave_config.write(configfile)