diff --git a/bundles/icinga2/items.py b/bundles/icinga2/items.py
index 137e11a..26caca4 100644
--- a/bundles/icinga2/items.py
+++ b/bundles/icinga2/items.py
@@ -2,48 +2,59 @@
 
 directories = {
     '/etc/icinga2': {
-        'purge': True,
+#        'purge': True,
         'owner': 'nagios',
+        'group': 'nagios',
+        'mode': '0750',
+        'needs': [
+            'pkg_apt:icinga2',
+        ],
     },
     '/etc/icinga2/conf.d': {
-        'purge': True,
+#        'purge': True,
         'owner': 'nagios',
+        'group': 'nagios',
+        'mode': '0750',
     },
     '/etc/icinga2/hosts.d': {
         'purge': True,
         'owner': 'nagios',
+        'group': 'nagios',
+        'mode': '0750',
     },
-    '/etc/icinga2/features.d': {
-        'purge': True,
-        'owner': 'nagios',
-    },
+    # '/etc/icinga2/features.d': {
+    #     'purge': True,
+    #     'owner': 'nagios',
+    #     'group': 'nagios',
+    #     'mode': '0750',
+    # },
 }
 
 files = {
-    '/etc/icinga2/icinga2.conf': {
-        'owner': 'nagios',
-    },
-    '/etc/icinga2/constants.conf': {
-        'owner': 'nagios',
-        'context': {
-            'hostname': node.metadata.get('icinga2/hostname')
-        },
-    },
-    '/etc/icinga2/conf.d/templates.conf': {
-        'source': 'conf.d/templates.conf',
-        'owner': 'nagios',
-    },
-    '/etc/icinga2/features/ido-pgsql.conf': {
-        'source': 'features/ido-pgsql.conf',
-        'content_type': 'mako',
-        'owner': 'nagios',
-        'context': {
-            'db_password': node.metadata.get('postgresql/roles/icinga2/password')
-        },
-        'needs': [
-            'pkg_apt:icinga2-ido-pgsql',
-        ],
-    },
+    # '/etc/icinga2/icinga2.conf': {
+    #     'owner': 'nagios',
+    # },
+    # '/etc/icinga2/constants.conf': {
+    #     'owner': 'nagios',
+    #     'context': {
+    #         'hostname': node.metadata.get('icinga2/hostname')
+    #     },
+    # },
+    # '/etc/icinga2/conf.d/templates.conf': {
+    #     'source': 'conf.d/templates.conf',
+    #     'owner': 'nagios',
+    # },
+    # '/etc/icinga2/features/ido-pgsql.conf': {
+    #     'source': 'features/ido-pgsql.conf',
+    #     'content_type': 'mako',
+    #     'owner': 'nagios',
+    #     'context': {
+    #         'db_password': node.metadata.get('postgresql/roles/icinga2/password')
+    #     },
+    #     'needs': [
+    #         'pkg_apt:icinga2-ido-pgsql',
+    #     ],
+    # },
 }
 
 for other_node in repo.nodes:
diff --git a/bundles/icinga2/metadata.py b/bundles/icinga2/metadata.py
index 20dde32..908c453 100644
--- a/bundles/icinga2/metadata.py
+++ b/bundles/icinga2/metadata.py
@@ -6,6 +6,7 @@ defaults = {
             'icinga2': {},
             'icinga2-ido-pgsql': {},
             'icingacli': {},
+            'monitoring-plugins': {},
         },
         'sources': {
             'deb https://packages.icinga.com/debian icinga-{release} main',
diff --git a/bundles/icingaweb2/README.md b/bundles/icingaweb2/README.md
new file mode 100644
index 0000000..18e3c30
--- /dev/null
+++ b/bundles/icingaweb2/README.md
@@ -0,0 +1,4 @@
+- apply
+- open /icingaweb2/setup in browser
+- fill in values from metadata
+- apply
diff --git a/bundles/icingaweb2/items.py b/bundles/icingaweb2/items.py
index d6ce678..29ce65a 100644
--- a/bundles/icingaweb2/items.py
+++ b/bundles/icingaweb2/items.py
@@ -1,6 +1,36 @@
+directories = {
+    '/etc/icingaweb2': {
+#        'purge': True,
+        'owner': 'www-data',
+        'group': 'icingaweb2',
+        'mode': '2770',
+        'needs': [
+            'pkg_apt:icinga2',
+            'pkg_apt:icingaweb2',
+        ],
+    },
+}
+
+
 files = {
     '/etc/icingaweb2/setup.token': {
         'content': node.metadata.get('icingaweb2/setup_token'),
-        'owner': 'nagios',
+        'owner': 'www-data',
+        'group': 'icingaweb2',
+        'mode': '0660',
     },
 }
+
+for name in [
+    'authentication.ini',
+    'config.ini',
+    'groups.ini',
+    'resources.ini',
+    'roles.ini',
+]:
+    files[f'/etc/icingaweb2/{name}'] = {
+        'content': repo.libs.ini.dumps(node.metadata.get(f'icingaweb2/{name}')),
+        'owner': 'www-data',
+        'group': 'icingaweb2',
+        'mode': '0660',
+    }
diff --git a/bundles/icingaweb2/metadata.py b/bundles/icingaweb2/metadata.py
index fba53df..627350e 100644
--- a/bundles/icingaweb2/metadata.py
+++ b/bundles/icingaweb2/metadata.py
@@ -4,12 +4,68 @@ defaults = {
     'apt': {
         'packages': {
             'icingaweb2': {},
+            'php-ldap': {},
+            'php-json': {},
+            'php-intl': {},
+            'php-xml': {},
+            'php-gd': {},
+            'php-imagick': {},
+            'php-pgsql': {},
         },
         'sources': {
             'deb https://packages.icinga.com/debian icinga-{release} main',
             'deb https://packages.icinga.com/debian icinga-{release}-snapshots main',
         },
     },
+    'icingaweb2': {
+        'authentication.ini': {
+            'icingaweb2': {
+                'backend': 'db',
+                'resource': 'icingaweb2_db',
+            },
+        },
+        'config.ini': {
+            'global': {
+                'show_stacktraces': '1',
+                'show_application_state_messages': '1',
+                #'module_path': '/usr/share/icingaweb2/modules',
+                'config_backend': 'db',
+                'config_resource': 'icingaweb2_db',
+            },
+            'logging': {
+                'log': 'syslog',
+                'level': 'INFO',
+                'application': 'icingaweb2',
+                'facility': 'user',
+            },
+        },
+        'groups.ini': {
+            'icingaweb2': {
+                'backend': 'db',
+                'resource': 'icingaweb2_db',
+            },
+        },
+        'resources.ini': {
+            'icingaweb2_db': {
+                'type': 'db',
+                'db': 'pgsql',
+                'host': 'localhost',
+                'port': '5432',
+                'dbname': 'icingaweb2',
+                'username': 'icingaweb2',
+                'password': str(repo.vault.password_for(f'psql icingaweb2 on {node.name}')),
+                'charset': '',
+                'use_ssl': '0',
+            },
+        },
+        'roles.ini': {
+            'Administrators': {
+                'users': 'root',
+                'permissions': '*',
+                'groups': 'Administrators',
+            },
+        },
+    },
     'postgresql': {
         'databases': {
             'icingaweb2': {
@@ -23,7 +79,7 @@ defaults = {
         },
     },
     'redis': {
-        'icingadb': {},
+        'icingaweb2': {},
     },
 }
 
@@ -41,11 +97,13 @@ def hostname(metadata):
 
 @metadata_reactor.provides(
     'icingaweb2/setup_token',
+    'icingaweb2/root_password',
 )
 def setup_token(metadata):
     return {
         'icingaweb2': {
             'setup_token': sha3_256(metadata.get('id').encode()).hexdigest()[:16],
+            'root_password': str(repo.vault.password_for(f"icingaweb2 root user on {metadata.get('id')}")),
         },
     }
 
diff --git a/bundles/openldap/README.md b/bundles/openldap/README.md
new file mode 100644
index 0000000..959c1a2
--- /dev/null
+++ b/bundles/openldap/README.md
@@ -0,0 +1,5 @@
+dpkg-reconfigure -plow slapd
+
+QqLeyREjjrWgK2kjNQ
+
+ldapsearch -Y EXTERNAL -H ldapi:/// -b "cn=config"
diff --git a/bundles/openldap/items.py b/bundles/openldap/items.py
new file mode 100644
index 0000000..e69de29
diff --git a/bundles/openldap/metadata.py b/bundles/openldap/metadata.py
new file mode 100644
index 0000000..151f735
--- /dev/null
+++ b/bundles/openldap/metadata.py
@@ -0,0 +1,8 @@
+defaults = {
+    'apt': {
+        'packages': {
+            'slapd': {},
+            'ldap-utils': {},
+        },
+    },
+}
diff --git a/bundles/web2ldap/items.py b/bundles/web2ldap/items.py
new file mode 100644
index 0000000..9ccf489
--- /dev/null
+++ b/bundles/web2ldap/items.py
@@ -0,0 +1,63 @@
+from shlex import quote
+
+users = {
+    'web2ldap': {},
+}
+
+directories = {
+    '/opt/web2ldap': {
+        'owner': 'web2ldap',
+    },
+}
+
+actions = {
+    'web2ldap_initialize_venv': {
+        'command': 'sudo -u web2ldap python3 -m venv /opt/web2ldap',
+        'unless': 'test -e /opt/web2ldap/bin/pip3',
+        'needs': [
+            'directory:/opt/web2ldap',
+        ],
+        'triggers': [
+            'svc_systemd:web2ldap.service:restart',
+        ],
+    },
+    'web2ldap_install': {
+        'command': """sudo -u web2ldap /opt/web2ldap/bin/pip3 install web2ldap""",
+        'unless': """sudo -u web2ldap /opt/web2ldap/bin/pip3 list --format=freeze | cut -d '=' -f 1 | grep -q '^web2ldap$'""",
+        'needs': [
+            'action:web2ldap_initialize_venv',
+        ],
+        'triggers': [
+            'svc_systemd:web2ldap.service:restart',
+        ],
+    },
+    'web2ldap_set_cookie_domain': {
+        'command': f"""sed -iE "s/^cookie_domain.*/cookie_domain = '{node.metadata.get('web2ldap/domain')}'/g" /opt/web2ldap/etc/web2ldap/web2ldapcnf/__init__.py""",
+        'unless':  f"""grep -q "^cookie_domain = '{node.metadata.get('web2ldap/domain')}'$" /opt/web2ldap/etc/web2ldap/web2ldapcnf/__init__.py""",
+        'needs': [
+            'action:web2ldap_install',
+        ],
+        'triggers': [
+            'svc_systemd:web2ldap.service:restart',
+        ],
+    },
+    'web2ldap_upgrade_venv': {
+        'command': """sudo -u web2ldap /opt/web2ldap/bin/pip3 list --outdated --format=freeze | cut -d '=' -f 1 | xargs -n1 /opt/web2ldap/bin/pip3 install --upgrade""",
+        'unless':  """sudo -u web2ldap /opt/web2ldap/bin/pip3 list --outdated --format=freeze | wc -l | grep -q '^0$'""",
+        'needs': [
+            'action:web2ldap_install',
+        ],
+        'triggers': [
+            'svc_systemd:web2ldap.service:restart',
+        ],
+    },
+}
+
+svc_systemd = {
+    'web2ldap.service': {
+        'needs': [
+            'action:web2ldap_initialize_venv',
+            'action:web2ldap_upgrade_venv',
+        ],
+    },
+}
diff --git a/bundles/web2ldap/metadata.py b/bundles/web2ldap/metadata.py
new file mode 100644
index 0000000..94a714f
--- /dev/null
+++ b/bundles/web2ldap/metadata.py
@@ -0,0 +1,63 @@
+from importlib.metadata import metadata
+
+
+defaults = {
+    'apt': {
+        'packages': {
+            'libsasl2-dev': {},
+            'python3-dev': {},
+            'libldap2-dev': {},
+            'libssl-dev': {},
+        },
+    },
+}
+
+
+@metadata_reactor.provides(
+    'systemd/units/web2ldap.service',
+)
+def systemd(metadata):
+    return {
+        'systemd': {
+            'units': {
+                'web2ldap.service': {
+                    'Unit': {
+                        'Description': 'gitea',
+                        'After': 'syslog.target',
+                        'After': 'network.target',
+                    },
+                    'Service': {
+                        'User': 'web2ldap',
+                        'WorkingDirectory': '/opt/web2ldap',
+                        'ExecStart': '/opt/web2ldap/bin/web2ldap 127.0.0.1 1760',
+                        'Restart': 'always',
+                        'Environment': [
+                            '"SERVER_NAME=' + metadata.get('web2ldap/domain') + '"',
+                            '"HTTP_HOST=' + metadata.get('web2ldap/domain') + '"',
+                        ],
+                    },
+                    'Install': {
+                        'WantedBy': {'multi-user.target'},
+                    },
+                },
+            },
+        },
+    }
+
+
+@metadata_reactor.provides(
+    'nginx/vhosts',
+)
+def nginx(metadata):
+    return {
+        'nginx': {
+            'vhosts': {
+                metadata.get('web2ldap/domain'): {
+                    'content': 'nginx/proxy_pass.conf',
+                    'context': {
+                        'target': 'http://127.0.0.1:1760',
+                    }
+                },
+            },
+        },
+    }
diff --git a/data/icingaweb2/vhost.conf b/data/icingaweb2/vhost.conf
index dddb457..9d5030b 100644
--- a/data/icingaweb2/vhost.conf
+++ b/data/icingaweb2/vhost.conf
@@ -5,10 +5,11 @@ server {
       listen [::]:443 ssl http2;
 
       server_name ${server_name};
+      root /usr/share/icingaweb2/public;
 
       ssl_certificate /var/lib/dehydrated/certs/${server_name}/fullchain.pem;
       ssl_certificate_key /var/lib/dehydrated/certs/${server_name}/privkey.pem;
-      
+
       location / {
         return 302 /icingaweb2/index.php;
       }
diff --git a/data/nginx/proxy_pass.conf b/data/nginx/proxy_pass.conf
index 16476ca..7d3069f 100644
--- a/data/nginx/proxy_pass.conf
+++ b/data/nginx/proxy_pass.conf
@@ -2,7 +2,7 @@ server {
     listen 443 ssl http2;
     listen [::]:443 ssl http2;
     server_name ${server_name};
-    
+
     ssl_certificate /var/lib/dehydrated/certs/${server_name}/fullchain.pem;
     ssl_certificate_key /var/lib/dehydrated/certs/${server_name}/privkey.pem;
 
diff --git a/nodes/home.server.py b/nodes/home.server.py
index f78b613..741b9bc 100644
--- a/nodes/home.server.py
+++ b/nodes/home.server.py
@@ -19,10 +19,10 @@
 #        'gollum',
         'grafana',
         'icinga2',
-        'icingadb',
         'icingaweb2',
         'influxdb2',
         'mirror',
+        'openldap',
         'postgresql',
         'redis',
         'smartctl',
@@ -31,6 +31,7 @@
         'systemd-swap',
         'raspberrymatic-cert',
         'tasmota-charge',
+        'web2ldap',
         'wireguard',
         'wol-waker',
         'zfs',
@@ -134,6 +135,9 @@
             'threads': 32,
             'ram':  49152,
         },
+        'web2ldap': {
+            'domain': 'web2ldap.sublimity.de',
+        },
         'wireguard': {
             'my_ip': '172.30.0.2/32',
             's2s': {