<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Authentik – Serverküche</title><link>https://serverkueche.de/tags/authentik/</link><description>Authentik – Neueste Beiträge von Serverküche</description><generator>Hugo</generator><language>de-DE</language><managingEditor>feedback@serverkueche.de (Serverküche)</managingEditor><webMaster>feedback@serverkueche.de (Serverküche)</webMaster><copyright>2026 Serverküche</copyright><lastBuildDate>Thu, 08 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://serverkueche.de/tags/authentik/index.xml" rel="self" type="application/rss+xml"/><item><title>Authentik Teil 2: Apps anbinden – Forward-Auth &amp; OIDC</title><link>https://serverkueche.de/tutorials/authentik-apps-anbinden/</link><pubDate>Thu, 08 Oct 2026 00:00:00 +0000</pubDate><author>feedback@serverkueche.de (Serverküche)</author><guid>https://serverkueche.de/tutorials/authentik-apps-anbinden/</guid><description>Dienste an Authentik anbinden: Forward-Auth mit Traefik für Apps ohne Login und OpenID Connect für alles mit eigener SSO-Unterstützung – jeder Schritt geprüft.</description><content:encoded><![CDATA[<p>Authentik läuft – jetzt kommt der Teil, der den Aufwand rechtfertigt: die Dienste dahinter. Es gibt
genau zwei Wege, und die Wahl hängt allein daran, ob die Anwendung selbst SSO versteht.</p>
<h2 id="was-bauen-wir">Was bauen wir?</h2>
<p>Wir binden zwei Dienste an, jeweils auf dem passenden Weg:</p>
<ul>
<li><strong>Forward-Auth</strong> für eine Anwendung <strong>ohne</strong> eigene Anmeldung. Traefik fragt vor jeder Anfrage bei
Authentik nach, ob der Besucher angemeldet ist. Ist er es nicht, landet er auf der
Authentik-Anmeldeseite; ist er es, bekommt die Anwendung seine Identität <strong>als HTTP-Header</strong>.</li>
<li><strong>OpenID Connect (OIDC)</strong> für Anwendungen <strong>mit</strong> SSO-Unterstützung – Grafana, Nextcloud, Forgejo,
Immich und viele andere. Dort trägst du Client-ID, Secret und die Endpunkte ein, den Rest macht die
App selbst.</li>
</ul>
<p>Alles unten wurde mit <strong>Authentik 2026.8</strong> hinter Traefik v3.7 auf Debian 13 durchgespielt: Am Ende
zeigt der Testdienst die durchgereichten Identitäts-Header, und das OIDC-Discovery-Dokument liefert
echte Endpunkte.</p>
<h3 id="welcher-weg-für-welchen-dienst">Welcher Weg für welchen Dienst?</h3>
<p>Die Entscheidung ist fast immer eindeutig:</p>
<table>
	<thead>
			<tr>
					<th>Dienst</th>
					<th>Unterstützt OIDC?</th>
					<th>Weg</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Nextcloud, Grafana, Forgejo, Immich, Paperless</td>
					<td>ja</td>
					<td><strong>OIDC</strong> – echte Konten, Rollen aus Gruppen</td>
			</tr>
			<tr>
					<td>Uptime Kuma, Dockge, Netdata, ein statisches Dashboard</td>
					<td>nein</td>
					<td><strong>Forward-Auth</strong></td>
			</tr>
			<tr>
					<td>Metrik-Endpunkte, <code>/actuator</code>, interne APIs</td>
					<td>irrelevant</td>
					<td>Forward-Auth <strong>oder</strong> einfacher: nicht öffentlich (VPN)</td>
			</tr>
			<tr>
					<td>Dienste mit Mobil-App (Immich, Nextcloud)</td>
					<td>ja</td>
					<td><strong>nur OIDC</strong> – Forward-Auth bricht die Apps (siehe unten)</td>
			</tr>
	</tbody>
</table>
<p>Kurz: <strong>OIDC, wo möglich; Forward-Auth, wo nötig.</strong> OIDC gibt der Anwendung ein echtes Benutzerkonto
inklusive Rollen; Forward-Auth stellt nur eine Tür davor und schiebt einen Namen im Header durch.</p>
<h2 id="voraussetzungen">Voraussetzungen</h2>
<ul>
<li>Ein <strong>laufendes Authentik</strong> aus <a href="/tutorials/authentik-single-sign-on/">Teil 1</a>, erreichbar unter
<code>https://DEINE_DOMAIN</code></li>
<li>Ein <strong>Traefik v3</strong> mit <code>proxy</code>-Netz (<a href="/tutorials/reverse-proxy-traefik/">Anleitung</a>) – wir arbeiten
hier viel mit Middlewares, die Grundlagen dazu stehen in
<a href="/tutorials/traefik-middlewares-absichern/">Traefik härten</a></li>
<li>Ein Dienst zum Üben. Wir nehmen <code>traefik/whoami</code>, weil er alle empfangenen Header ausgibt – damit
ist sofort sichtbar, ob die Anbindung wirklich greift</li>
</ul>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-herb-400 bg-herb-50 dark:border-herb-700 dark:bg-herb-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">🧑‍🍳</span>Ein oder zwei Domains?
  </p>
  <div class="prose-kitchen text-sm">Wir zeigen den Aufbau <strong>auf einer Domain</strong>: Authentik unter <code>https://DEINE_DOMAIN/</code>, der geschützte
Dienst unter <code>https://DEINE_DOMAIN/whoami</code>. Das ist die sparsame Variante und genau so getestet.
Üblicher ist die Trennung (<code>auth.DEINE_DOMAIN</code> für Authentik, <code>app.DEINE_DOMAIN</code> für den Dienst) – die
Schritte sind identisch, nur <code>external_host</code> und die Traefik-Regeln tragen dann den anderen Hostnamen.</div>
</div>
<h2 id="schritt-für-schritt">Schritt für Schritt</h2>
<h3 id="1-zugang-für-die-automatisierung-ein-api-token">1. Zugang für die Automatisierung: ein API-Token</h3>
<p>Provider und Anwendungen lassen sich vollständig über die API anlegen – praktisch, weil du damit
reproduzierbar arbeitest statt zu klicken. Dafür braucht Authentik einen Token.</p>
<p>Weil du in Teil 1 den Einrichtungsassistenten durchlaufen hast, existiert <code>akadmin</code> bereits. Den
Token legst du deshalb <strong>in der Oberfläche</strong> an: <strong>Verzeichnis → Tokens und App-Passwörter → Token
erstellen</strong>. Als Bezeichner nimm etwas Sprechendes (<code>api-setup</code>), als Absicht <strong>API</strong>, als Benutzer
<code>akadmin</code>. Nach dem Speichern kopierst du den Schlüssel über das Kopiersymbol – er wird nur einmal
angezeigt.</p>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-sky-300 bg-sky-50 dark:border-sky-800 dark:bg-sky-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">ℹ️</span>Bei einer Neuinstallation geht es auch automatisch
  </p>
  <div class="prose-kitchen text-sm"><p>Setzt du Authentik neu auf, kannst du Konto und Token beim <strong>allerersten</strong> Start erzeugen lassen –
ohne Assistenten. Die Variablen gehören an den <strong><code>worker</code></strong>-Dienst (nicht an <code>server</code>) und werden
<strong>nur beim ersten Start gelesen</strong>; bei einer bestehenden Installation bleiben sie wirkungslos.
Statt des Klartext-Passworts empfiehlt Authentik inzwischen einen Hash. Das Passwort nimmt
<code>hash_password</code> <strong>nicht als Argument</strong> – <code>hash_password DEIN_PASSWORT</code> bricht mit
<code>unrecognized arguments</code> ab –, sondern über eine Eingabeaufforderung oder die
Standardeingabe:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker compose run --rm server hash_password</span></span></code></pre></div>
</div>
<p>Am Ende steht eine Zeile, die mit <code>pbkdf2_sha256$</code> beginnt. Sie kommt als
<code>AK_BOOTSTRAP_PASSWORD_HASH</code> in die <code>.env</code>, der Token als <code>AK_BOOTSTRAP_TOKEN</code> (zum Beispiel aus
<code>openssl rand -hex 32</code>). In der <code>compose.yaml</code> sieht der <code>worker</code> dann so aus:</p>
<div class="sk-code">
  <span class="sk-code-head">YAML</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">worker</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">AUTHENTIK_BOOTSTRAP_PASSWORD_HASH</span><span class="p">:</span><span class="w"> </span><span class="l">${AK_BOOTSTRAP_PASSWORD_HASH}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">AUTHENTIK_BOOTSTRAP_TOKEN</span><span class="p">:</span><span class="w"> </span><span class="l">${AK_BOOTSTRAP_TOKEN}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">AUTHENTIK_BOOTSTRAP_EMAIL</span><span class="p">:</span><span class="w"> </span><span class="l">du@DEINE_DOMAIN</span></span></span></code></pre></div>
</div>
<p>Der Hash enthält <code>$</code>-Zeichen: in der <code>.env</code> in <strong>einfache</strong> Anführungszeichen setzen, direkt in der
<code>compose.yaml</code> jedes <code>$</code> als <code>$$</code> schreiben.</p>
</div>
</div>
<p>Prüfe den Token – egal auf welchem Weg er entstanden ist:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s -H <span class="s2">&#34;Authorization: Bearer DEIN_LANGER_ZUFALLS_TOKEN&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  https://DEINE_DOMAIN/api/v3/core/users/me/</span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">{&#34;user&#34;:{&#34;username&#34;:&#34;akadmin&#34;, … &#34;is_superuser&#34;:true, …}}</span></span></code></pre></div>
</div>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-amber-400 bg-amber-50 dark:border-amber-700 dark:bg-amber-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">⚠️</span>Achtung
  </p>
  <div class="prose-kitchen text-sm">Der Bootstrap-Token ist ein <strong>vollwertiger Admin-Zugang ohne zweiten Faktor</strong>. Er gehört in die <code>.env</code>
mit <code>chmod 600</code>, nicht in ein Skript im Repository – und wenn du ihn nur zum Einrichten brauchst,
lösch ihn danach unter <strong>Verzeichnis → Tokens</strong>.</div>
</div>
<h3 id="2-weg-a-forward-auth-für-dienste-ohne-login">2. Weg A: Forward-Auth für Dienste ohne Login</h3>
<p>Der Proxy-Provider ist Authentiks Antwort auf „diese App hat gar keine Benutzerverwaltung&quot;. Er kommt
in mehreren Betriebsarten; wir nehmen <strong><code>forward_single</code></strong>: eine Anwendung, ein Provider.</p>
<p>Zuerst die IDs der beiden Standard-Flows (aus Teil 1 bekannt):</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">API</span><span class="o">=</span>https://DEINE_DOMAIN/api/v3
</span></span><span class="line"><span class="cl"><span class="nv">TOK</span><span class="o">=</span><span class="s2">&#34;Authorization: Bearer DEIN_LANGER_ZUFALLS_TOKEN&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">AUTHZ</span><span class="o">=</span><span class="k">$(</span>curl -s -H <span class="s2">&#34;</span><span class="nv">$TOK</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$API</span><span class="s2">/flows/instances/?slug=default-provider-authorization-implicit-consent&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="p">|</span> python3 -c <span class="s2">&#34;import json,sys; print(json.load(sys.stdin)[&#39;results&#39;][0][&#39;pk&#39;])&#34;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">INVAL</span><span class="o">=</span><span class="k">$(</span>curl -s -H <span class="s2">&#34;</span><span class="nv">$TOK</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$API</span><span class="s2">/flows/instances/?slug=default-provider-invalidation-flow&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="p">|</span> python3 -c <span class="s2">&#34;import json,sys; print(json.load(sys.stdin)[&#39;results&#39;][0][&#39;pk&#39;])&#34;</span><span class="k">)</span></span></span></code></pre></div>
</div>
<p>Dann den Provider:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s -X POST -H <span class="s2">&#34;</span><span class="nv">$TOK</span><span class="s2">&#34;</span> -H <span class="s2">&#34;content-type: application/json&#34;</span> <span class="s2">&#34;</span><span class="nv">$API</span><span class="s2">/providers/proxy/&#34;</span> -d <span class="s2">&#34;{
</span></span></span><span class="line"><span class="cl"><span class="s2">  \&#34;name\&#34;: \&#34;whoami-forwardauth\&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s2">  \&#34;authorization_flow\&#34;: \&#34;</span><span class="nv">$AUTHZ</span><span class="s2">\&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s2">  \&#34;invalidation_flow\&#34;: \&#34;</span><span class="nv">$INVAL</span><span class="s2">\&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s2">  \&#34;mode\&#34;: \&#34;forward_single\&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s2">  \&#34;external_host\&#34;: \&#34;https://DEINE_DOMAIN\&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">}&#34;</span></span></span></code></pre></div>
</div>
<p>Und die <strong>Anwendung</strong>, die ihn benutzt – sie erzeugt auch die Kachel in der Benutzer-Bibliothek:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s -X POST -H <span class="s2">&#34;</span><span class="nv">$TOK</span><span class="s2">&#34;</span> -H <span class="s2">&#34;content-type: application/json&#34;</span> <span class="s2">&#34;</span><span class="nv">$API</span><span class="s2">/core/applications/&#34;</span> -d <span class="s1">&#39;{
</span></span></span><span class="line"><span class="cl"><span class="s1">  &#34;name&#34;: &#34;whoami (geschützt)&#34;, &#34;slug&#34;: &#34;whoami&#34;, &#34;provider&#34;: 1
</span></span></span><span class="line"><span class="cl"><span class="s1">}&#39;</span></span></span></code></pre></div>
</div>
<p>Die <code>1</code> ist die <code>pk</code> aus der Antwort des Provider-Aufrufs. Danach stehen beide in der Übersicht:</p>
<p><figure class="my-6"><img src="/tutorials/authentik-apps-anbinden/anwendungen_hu_caea8955d7f4d689.webp" srcset="/tutorials/authentik-apps-anbinden/anwendungen_hu_1a744ca96188ab75.webp 480w, /tutorials/authentik-apps-anbinden/anwendungen_hu_caea8955d7f4d689.webp 768w, /tutorials/authentik-apps-anbinden/anwendungen_hu_84772322bbc9e7d7.webp 1200w, /tutorials/authentik-apps-anbinden/anwendungen_hu_658009001869930.webp 1920w" sizes="(min-width: 768px) 768px, 100vw"
    width="768" height="432"
    data-full="/tutorials/authentik-apps-anbinden/anwendungen_hu_48ed49d0fa62dc94.webp"
    alt="Die Anwendungsliste in Authentik mit zwei Einträgen: Grafana über einen OAuth2/OpenID-Anbieter und whoami über einen Proxy Provider" title="Zwei Anwendungen, zwei Wege: OIDC-Anbieter und Proxy Provider"
    loading="lazy" decoding="async" class="rounded-lg"><figcaption class="mt-2 text-sm text-center text-slate-500 italic">Zwei Anwendungen, zwei Wege: OIDC-Anbieter und Proxy Provider</figcaption></figure></p>
<h3 id="3-den-outpost-scharf-schalten--und-die-häufigste-falle">3. Den Outpost scharf schalten – und die häufigste Falle</h3>
<p>Ein Proxy-Provider arbeitet nicht allein: Ein <strong>Outpost</strong> liefert ihn aus. Authentik bringt einen
eingebetteten Outpost mit, der im <code>server</code>-Container läuft – du musst ihm den Provider nur zuweisen:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">OUT</span><span class="o">=</span><span class="k">$(</span>curl -s -H <span class="s2">&#34;</span><span class="nv">$TOK</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$API</span><span class="s2">/outposts/instances/&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="p">|</span> python3 -c <span class="s2">&#34;import json,sys; print(json.load(sys.stdin)[&#39;results&#39;][0][&#39;pk&#39;])&#34;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">curl -s -X PATCH -H <span class="s2">&#34;</span><span class="nv">$TOK</span><span class="s2">&#34;</span> -H <span class="s2">&#34;content-type: application/json&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;</span><span class="nv">$API</span><span class="s2">/outposts/instances/</span><span class="nv">$OUT</span><span class="s2">/&#34;</span> -d <span class="s1">&#39;{&#34;providers&#34;: [1]}&#39;</span></span></span></code></pre></div>
</div>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-paprika-400 bg-paprika-50 dark:border-paprika-700 dark:bg-paprika-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">🔥</span>Ohne authentik_host landen deine Nutzer auf localhost
  </p>
  <div class="prose-kitchen text-sm"><p>Der eingebettete Outpost kennt seine öffentliche Adresse nicht von selbst. Im Test war das Feld
<code>authentik_host</code> <strong>leer</strong>, und die Weiterleitung eines nicht angemeldeten Besuchers ging auf
<code>http://localhost/application/o/authorize/…</code> – für jeden Nutzer eine tote Seite. Einmal setzen, dann
stimmt sie:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s -X PATCH -H <span class="s2">&#34;</span><span class="nv">$TOK</span><span class="s2">&#34;</span> -H <span class="s2">&#34;content-type: application/json&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;</span><span class="nv">$API</span><span class="s2">/outposts/instances/</span><span class="nv">$OUT</span><span class="s2">/&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -d <span class="s1">&#39;{&#34;config&#34;: {&#34;authentik_host&#34;: &#34;https://DEINE_DOMAIN/&#34;}}&#39;</span></span></span></code></pre></div>
</div>
<p>Danach zeigt die Weiterleitung auf <code>https://DEINE_DOMAIN/application/o/authorize/…</code> – vorher und
nachher nachgemessen. In der Oberfläche liegt dasselbe Feld unter
<strong>Anwendungen → Outposts → Bearbeiten</strong>.</p>
</div>
</div>
<h3 id="4-traefik-die-forward-auth-middleware">4. Traefik: die Forward-Auth-Middleware</h3>
<p>Jetzt fehlt die Verbindung. Traefik fragt vor jeder Anfrage beim Outpost nach – das macht die
Middleware <code>forwardauth</code>. Am geschützten Dienst:</p>
<div class="sk-code">
  <span class="sk-code-head">YAML</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">whoami</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">traefik/whoami:v1.12</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">unless-stopped</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">networks</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">proxy]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.enable=true&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.routers.whoami.rule=Host(`DEINE_DOMAIN`) &amp;&amp; PathPrefix(`/whoami`)&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.routers.whoami.entrypoints=websecure&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.routers.whoami.tls.certresolver=le&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.routers.whoami.middlewares=ak-auth&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.services.whoami.loadbalancer.server.port=80&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.middlewares.ak-auth.forwardauth.address=http://server:9000/outpost.goauthentik.io/auth/traefik&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.middlewares.ak-auth.forwardauth.trustForwardHeader=true&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.middlewares.ak-auth.forwardauth.authResponseHeaders=X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name,X-authentik-uid&#34;</span></span></span></code></pre></div>
</div>
<p>Die drei entscheidenden Zeilen:</p>
<ul>
<li><strong><code>address</code></strong> zeigt auf den Outpost – hier über den Container-Namen <code>server</code> im gemeinsamen
Docker-Netz, also <strong>ohne</strong> Umweg über das Internet. Der Pfad <code>/outpost.goauthentik.io/auth/traefik</code>
ist der Traefik-spezifische Prüf-Endpunkt.</li>
<li><strong><code>trustForwardHeader=true</code></strong> gibt die <code>X-Forwarded-*</code>-Header weiter, damit Authentik die
ursprünglich aufgerufene Adresse kennt und korrekt zurückleiten kann.</li>
<li><strong><code>authResponseHeaders</code></strong> ist der eigentliche Nutzen: Diese Header kopiert Traefik aus der Antwort
von Authentik in die Anfrage an deine Anwendung. Fehlt die Zeile, ist der Dienst geschützt, weiß
aber nicht, <strong>wer</strong> da ist.</li>
</ul>
<h3 id="5-prüfen-dass-es-wirklich-schützt">5. Prüfen, dass es wirklich schützt</h3>
<p>Erst ohne Anmeldung – dafür reicht <code>curl</code>:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s -o /dev/null -w <span class="s2">&#34;%{http_code} → %{redirect_url}\n&#34;</span> https://DEINE_DOMAIN/whoami</span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">302 → https://DEINE_DOMAIN/application/o/authorize/?client_id=…&amp;redirect_uri=…%2Foutpost.goauthentik.io%2Fcallback…</span></span></code></pre></div>
</div>
<p>Ein <code>302</code> auf die Authentik-Anmeldung ist das gewünschte Ergebnis. Im Browser erscheint die
Anmeldeseite <strong>vor</strong> dem Dienst:</p>
<p><figure class="my-6"><img src="/tutorials/authentik-apps-anbinden/login-vor-whoami_hu_64711354cb18f01c.webp" srcset="/tutorials/authentik-apps-anbinden/login-vor-whoami_hu_537022e50054a3fb.webp 480w, /tutorials/authentik-apps-anbinden/login-vor-whoami_hu_64711354cb18f01c.webp 768w, /tutorials/authentik-apps-anbinden/login-vor-whoami_hu_3308fea323cb2426.webp 1200w, /tutorials/authentik-apps-anbinden/login-vor-whoami_hu_98e313df300b25bc.webp 1920w" sizes="(min-width: 768px) 768px, 100vw"
    width="768" height="432"
    data-full="/tutorials/authentik-apps-anbinden/login-vor-whoami_hu_2d8883223024e9c6.webp"
    alt="Die Authentik-Anmeldeseite, die beim Aufruf des geschützten Dienstes erscheint" title="Nicht angemeldet: Authentik fragt, bevor der Dienst antwortet"
    loading="lazy" decoding="async" class="rounded-lg"><figcaption class="mt-2 text-sm text-center text-slate-500 italic">Nicht angemeldet: Authentik fragt, bevor der Dienst antwortet</figcaption></figure></p>
<p>Nach der Anmeldung landest du wieder auf <code>/whoami</code> – und der Dienst zeigt, was bei ihm ankommt:</p>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET /whoami HTTP/1.1
</span></span><span class="line"><span class="cl">Host: DEINE_DOMAIN
</span></span><span class="line"><span class="cl">X-Authentik-Email: du@DEINE_DOMAIN
</span></span><span class="line"><span class="cl">X-Authentik-Groups: authentik Admins
</span></span><span class="line"><span class="cl">X-Authentik-Name: authentik Default Admin
</span></span><span class="line"><span class="cl">X-Authentik-Uid: d216e191a6f972c39931a005a7998a68…
</span></span><span class="line"><span class="cl">X-Authentik-Username: akadmin
</span></span><span class="line"><span class="cl">X-Forwarded-Proto: https</span></span></code></pre></div>
</div>
<p>Das ist der ganze Trick: Die Anwendung hat keine Zeile Anmeldecode – und weiß trotzdem, wer vor ihr
sitzt. Dienste, die Header-Authentifizierung unterstützen (Grafana mit <code>auth.proxy</code>, Paperless,
Forgejo mit Reverse-Proxy-Auth), können daraus direkt eine Sitzung machen.</p>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-amber-400 bg-amber-50 dark:border-amber-700 dark:bg-amber-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">⚠️</span>Header sind nur so sicher wie ihr Weg
  </p>
  <div class="prose-kitchen text-sm">Wenn eine Anwendung <code>X-Authentik-Username</code> vertraut, darf dieser Header <strong>niemals</strong> von außen kommen
können. Deshalb: Der Dienst ist ausschließlich über Traefik erreichbar (kein veröffentlichter Port),
und die Middleware hängt an <strong>jedem</strong> Router, der zu ihm führt. Ein zweiter Router ohne Middleware ist
eine offene Tür.</div>
</div>
<h3 id="6-die-grenzen-von-forward-auth-kennen">6. Die Grenzen von Forward-Auth kennen</h3>
<p>Bevor du alles hinter Forward-Auth hängst: Der Mechanismus setzt einen <strong>Browser</strong> voraus. Eine nicht
angemeldete Anfrage bekommt eine <code>302</code>-Weiterleitung auf ein Anmeldeformular – und genau daran
scheitert alles, was kein Browser ist:</p>
<ul>
<li><strong>Mobil-Apps</strong> (Nextcloud, Immich, Audiobookshelf) können mit der Weiterleitung nichts anfangen. Sie
sehen HTML statt JSON und melden „Server nicht erreichbar&quot;. Für solche Dienste ist <strong>OIDC</strong> der
einzige sinnvolle Weg – oder du nimmst die App-Pfade gezielt aus.</li>
<li><strong>API-Zugriffe und Skripte</strong> (<code>curl</code>, Backup-Jobs, Webhooks) laufen ins Leere. Wo ein Dienst per
Token angesprochen wird, muss dieser Pfad an der Middleware vorbei – oder du verlagerst den Schutz
auf eine <a href="/tutorials/traefik-middlewares-absichern/">IP-Allowlist</a>.</li>
<li><strong>WebDAV und Sync-Clients</strong> verhalten sich wie APIs, mit demselben Ergebnis.</li>
</ul>
<p>Für solche Ausnahmen legst du einen <strong>zweiten Router</strong> ohne die Auth-Middleware an, der nur den nötigen
Pfad bedient – und schützt ihn anders, etwa mit dem Token der Anwendung selbst. Beispiel für einen
API-Pfad:</p>
<div class="sk-code">
  <span class="sk-code-head">YAML</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.routers.app-api.rule=Host(`DEINE_DOMAIN`) &amp;&amp; PathPrefix(`/api`)&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.routers.app-api.entrypoints=websecure&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.routers.app-api.tls.certresolver=le&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.routers.app-api.middlewares=rate@file&#34;</span></span></span></code></pre></div>
</div>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-amber-400 bg-amber-50 dark:border-amber-700 dark:bg-amber-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">⚠️</span>Achtung
  </p>
  <div class="prose-kitchen text-sm">Jeder solche Ausnahme-Router ist eine bewusste Lücke in deiner Absicherung. Halte den Pfad so eng wie
möglich (<code>/api</code>, nicht <code>/</code>), und schreib dir auf, warum er existiert – in einem halben Jahr weiß das
sonst niemand mehr.</div>
</div>
<p>Zur Vollständigkeit die dritte Betriebsart des Proxy-Providers: <strong><code>forward_domain</code></strong>. Damit bedient
<strong>ein</strong> Outpost alle Dienste einer Domain (<code>*.DEINE_DOMAIN</code>), und Nutzer melden sich einmal für alle an.
Das ist der bequemere Aufbau ab etwa drei Diensten, verlangt aber, dass Authentik und alle Anwendungen
unter derselben übergeordneten Domain liegen, damit das Sitzungs-Cookie geteilt werden kann. Hier
getestet ist <code>forward_single</code> – die Umstellung betrifft nur den Modus im Provider und die
Cookie-Domain.</p>
<h3 id="7-weg-b-openid-connect-für-dienste-mit-eigenem-sso">7. Weg B: OpenID Connect für Dienste mit eigenem SSO</h3>
<p>Hat die Anwendung eine OIDC-Anmeldung, ist das der bessere Weg: Sie führt dann echte Benutzerkonten
inklusive Rollen, statt nur einen Namen im Header zu sehen.</p>
<p>Provider anlegen – wichtig ist die <strong>Redirect-URI</strong>, die genau der Rückadresse der Anwendung
entsprechen muss (Beispiel Grafana):</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s -X POST -H <span class="s2">&#34;</span><span class="nv">$TOK</span><span class="s2">&#34;</span> -H <span class="s2">&#34;content-type: application/json&#34;</span> <span class="s2">&#34;</span><span class="nv">$API</span><span class="s2">/providers/oauth2/&#34;</span> -d <span class="s2">&#34;{
</span></span></span><span class="line"><span class="cl"><span class="s2">  \&#34;name\&#34;: \&#34;grafana-oidc\&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s2">  \&#34;authorization_flow\&#34;: \&#34;</span><span class="nv">$AUTHZ</span><span class="s2">\&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s2">  \&#34;invalidation_flow\&#34;: \&#34;</span><span class="nv">$INVAL</span><span class="s2">\&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s2">  \&#34;client_type\&#34;: \&#34;confidential\&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s2">  \&#34;redirect_uris\&#34;: [{\&#34;matching_mode\&#34;: \&#34;strict\&#34;, \&#34;url\&#34;: \&#34;https://GRAFANA_DOMAIN/login/generic_oauth\&#34;}]
</span></span></span><span class="line"><span class="cl"><span class="s2">}&#34;</span></span></span></code></pre></div>
</div>
<p>Die Antwort enthält <code>client_id</code> und <code>client_secret</code> – beide brauchst du in der Anwendung. Danach wie
oben eine <strong>Anwendung</strong> anlegen, die auf diesen Provider zeigt.</p>
<p>Ob alles sitzt, verrät das Discovery-Dokument. Authentik veröffentlicht es pro Anwendung unter deren
Slug:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s https://DEINE_DOMAIN/application/o/grafana/.well-known/openid-configuration <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="p">|</span> python3 -m json.tool <span class="p">|</span> head -12</span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">&#34;issuer&#34;: &#34;https://DEINE_DOMAIN/application/o/grafana/&#34;,
</span></span><span class="line"><span class="cl">&#34;authorization_endpoint&#34;: &#34;https://DEINE_DOMAIN/application/o/authorize/&#34;,
</span></span><span class="line"><span class="cl">&#34;token_endpoint&#34;: &#34;https://DEINE_DOMAIN/application/o/token/&#34;,
</span></span><span class="line"><span class="cl">&#34;userinfo_endpoint&#34;: &#34;https://DEINE_DOMAIN/application/o/userinfo/&#34;,</span></span></code></pre></div>
</div>
<p>Beachte: Der <strong>Issuer ist anwendungsspezifisch</strong> (er enthält den Slug), die übrigen Endpunkte sind für
alle Anwendungen gleich. Genau diese Adressen – oder die Discovery-URL selbst, wenn die App sie
unterstützt – trägst du in der Anwendung ein. Für Grafana etwa:</p>
<div class="sk-code">
  <span class="sk-code-head">INI</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="na">GF_AUTH_GENERIC_OAUTH_ENABLED</span><span class="o">=</span><span class="s">true</span>
</span></span><span class="line"><span class="cl"><span class="na">GF_AUTH_GENERIC_OAUTH_NAME</span><span class="o">=</span><span class="s">Authentik</span>
</span></span><span class="line"><span class="cl"><span class="na">GF_AUTH_GENERIC_OAUTH_CLIENT_ID</span><span class="o">=</span><span class="s">DEINE_CLIENT_ID</span>
</span></span><span class="line"><span class="cl"><span class="na">GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET</span><span class="o">=</span><span class="s">DEIN_CLIENT_SECRET</span>
</span></span><span class="line"><span class="cl"><span class="na">GF_AUTH_GENERIC_OAUTH_SCOPES</span><span class="o">=</span><span class="s">openid profile email</span>
</span></span><span class="line"><span class="cl"><span class="na">GF_AUTH_GENERIC_OAUTH_AUTH_URL</span><span class="o">=</span><span class="s">https://DEINE_DOMAIN/application/o/authorize/</span>
</span></span><span class="line"><span class="cl"><span class="na">GF_AUTH_GENERIC_OAUTH_TOKEN_URL</span><span class="o">=</span><span class="s">https://DEINE_DOMAIN/application/o/token/</span>
</span></span><span class="line"><span class="cl"><span class="na">GF_AUTH_GENERIC_OAUTH_API_URL</span><span class="o">=</span><span class="s">https://DEINE_DOMAIN/application/o/userinfo/</span></span></span></code></pre></div>
</div>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-herb-400 bg-herb-50 dark:border-herb-700 dark:bg-herb-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">🧑‍🍳</span>Gruppen als Rollen
  </p>
  <div class="prose-kitchen text-sm">Die meisten Anwendungen können aus einem Feld im Token Rollen ableiten – Grafana etwa über
<code>role_attribute_path</code>. Damit steuerst du Admin-Rechte zentral über Authentik-Gruppen, statt sie in
jeder App zu pflegen. Genau deshalb der Rat aus Teil 1: Berechtigungen an <strong>Gruppen</strong> hängen, nicht an
einzelne Benutzer.</div>
</div>
<h3 id="8-wer-darf-was-zugriff-einschränken">8. Wer darf was? Zugriff einschränken</h3>
<p>Solange an einer Anwendung <strong>keine</strong> Richtlinie hängt, darf sie jeder angemeldete Benutzer nutzen. Für
den Regelfall bindest du eine Gruppe an die Anwendung: <strong>Anwendungen → deine Anwendung →
Richtlinien / Gruppen / Benutzer-Bindungen</strong>, dort die Gruppe auswählen.</p>
<p>Erst danach verschwindet die Kachel in der Benutzer-Bibliothek für alle anderen – und der Zugriff ist
gesperrt. Prüfen lässt sich das am ehrlichsten mit einem zweiten Testkonto, das <strong>nicht</strong> in der
Gruppe ist: Es soll die Anwendung weder sehen noch aufrufen können.</p>
<h2 id="wenn-es-nicht-funktioniert">Wenn es nicht funktioniert</h2>
<div class="troubleshoot not-prose">
<p><strong>Das Skript bricht beim Ermitteln der Flows mit <code>IndexError: list index out of range</code> ab.</strong> Das
passiert nur direkt nach einer frischen Installation: <code>/-/health/ready/</code> meldet schon <code>200</code>, während
der <code>worker</code> die mitgelieferten Abläufe noch einspielt. Im Test gab es in diesem Moment erst einen
Flow, 20 bis 30 Sekunden später alle 15. Kurz warten und neu starten.</p>
<p><strong>Der Aufruf leitet auf <code>http://localhost/application/o/authorize/…</code> weiter.</strong> Der eingebettete
Outpost kennt seine öffentliche Adresse nicht: Das Feld <code>authentik_host</code> ist leer. Setz es auf
<code>https://DEINE_DOMAIN/</code> (Schritt 3) – vorher landet jeder Nutzer auf einer toten Seite.</p>
<p><strong>Traefik antwortet mit 500 oder es kommt gar keine Weiterleitung.</strong> Dann erreicht die Middleware den
Outpost nicht. Die <code>address</code> muss aus Traefiks Sicht auflösbar sein – also der <strong>Container-Name</strong> im
gemeinsamen Netz (<code>http://server:9000/outpost.goauthentik.io/auth/traefik</code>), nicht <code>localhost</code>. Prüfe
im Traefik-Ordner mit <code>docker compose exec traefik wget -qO- http://server:9000/-/health/live/</code>, ob
die Verbindung steht.</p>
<p><strong>Der Dienst ist geschützt, kennt aber den Benutzer nicht.</strong> Es fehlt <code>authResponseHeaders</code> an der
Middleware. Ohne diese Liste verwirft Traefik die Identitäts-Header aus Authentiks Antwort – die
Anmeldung funktioniert, die Anwendung sieht aber nichts.</p>
<p><strong>Nach der Anmeldung landet man in einer Weiterleitungsschleife.</strong> Meist passt <code>external_host</code> nicht
zur tatsächlich aufgerufenen Adresse (http statt https, mit oder ohne Subdomain). Er muss exakt die
URL sein, unter der Nutzer den Dienst aufrufen.</p>
<p><strong>Die OIDC-Anmeldung bricht mit „redirect_uri mismatch&quot; ab.</strong> Authentik prüft die Rückadresse strikt.
Trag im Provider genau die URI ein, die die Anwendung schickt – bei Grafana etwa
<code>https://GRAFANA_DOMAIN/login/generic_oauth</code>. Ein fehlender Schrägstrich reicht zum Scheitern.</p>
<p><strong>Alle angemeldeten Nutzer kommen überall rein.</strong> Ohne Bindung an eine Gruppe ist eine Anwendung für
jeden angemeldeten Benutzer offen. Häng eine Gruppen-Bindung an die Anwendung (Schritt 8) und prüfe
mit einem Konto, das nicht in der Gruppe ist.</p>

</div>
<p class="mt-3 text-sm text-slate-500 dark:text-slate-400">
  Dein Problem ist nicht dabei?
  <a href="/fehler/" class="font-medium text-paprika-700 hover:underline dark:text-paprika-400">Alle Fehlermeldungen durchsuchen →</a>
</p>

<h2 id="wartung--backups">Wartung &amp; Backups</h2>
<p><strong>Nach jedem Authentik-Update stichprobenartig prüfen.</strong> Ein Update kann Flows und Provider
verändern; die Anbindungen selbst sind robust, aber der schnelle Test lohnt: <code>curl</code> auf den
geschützten Pfad muss weiterhin <code>302</code> liefern, und das Discovery-Dokument muss deine Endpunkte
zeigen. Zwei Kommandos, die verhindern, dass eine Anmeldung wochenlang unbemerkt kaputt ist.</p>
<p><strong>Client-Secrets gehören in den Passwortmanager.</strong> Sie stehen in der Anwendung
(Umgebungsvariablen) <strong>und</strong> in Authentik. Wird eines neu erzeugt, muss die andere Seite mitgezogen
werden – notiere beim Anlegen, welche App welches Secret nutzt, sonst suchst du später in beiden
Systemen.</p>
<p><strong>Die Datenbank ist jetzt noch wichtiger.</strong> Provider, Anwendungen und Gruppen-Bindungen liegen alle in
PostgreSQL. Der <code>pg_dump</code> aus <a href="/tutorials/authentik-single-sign-on/">Teil 1</a> ist damit kein
Nice-to-have mehr: Ohne ihn baust du nach einem Ausfall jede Anbindung neu. Mit
<a href="/tutorials/backups-mit-restic/">Restic</a> täglich wegsichern.</p>
<p><strong>Den Notausgang offen halten.</strong> Jeder Dienst hinter Forward-Auth ist nur so erreichbar wie Authentik.
Behalte in wichtigen Anwendungen ein lokales Konto – und häng <strong>keine</strong> Werkzeuge hinter SSO, die du
zur Reparatur brauchst; das gilt besonders für <a href="/tutorials/dockge-compose-verwalten/">Dockge</a> und
ähnliche Verwaltungsoberflächen. Ein <a href="/tutorials/uptime-kuma-monitoring/">Monitoring</a> auf
<code>/-/health/ready/</code> merkt den Ausfall vor deinen Nutzern.</p>
<p><strong>Tokens aufräumen.</strong> Der Bootstrap-Token aus Schritt 1 ist ein Admin-Zugang ohne zweiten Faktor. Wenn
die Einrichtung steht, lösch ihn unter <strong>Verzeichnis → Tokens</strong> – und leg für spätere Automatisierung
einen eigenen Token mit begrenzten Rechten an.</p>
]]></content:encoded></item><item><title>Authentik: Single Sign-On für alle deine Dienste (Teil 1)</title><link>https://serverkueche.de/tutorials/authentik-single-sign-on/</link><pubDate>Tue, 06 Oct 2026 00:00:00 +0000</pubDate><author>feedback@serverkueche.de (Serverküche)</author><guid>https://serverkueche.de/tutorials/authentik-single-sign-on/</guid><description>Authentik 2026.8 mit Docker und Traefik aufsetzen: eigener Identity-Provider mit Postgres statt Redis – Ersteinrichtung, Flows und Betrieb ohne Aussperren.</description><content:encoded><![CDATA[<p>Mit jedem neuen Dienst wächst die Liste der Passwörter: Nextcloud, Immich, Grafana, das Wiki – jedes
mit eigenen Konten. Authentik dreht das um: <strong>ein</strong> Konto, <strong>eine</strong> Anmeldung, zentral verwaltet.
Dieser erste Teil baut den Identity-Provider auf und erklärt sein Modell. Teil 2 hängt dann die
Dienste daran.</p>
<h2 id="was-bauen-wir">Was bauen wir?</h2>
<p>Am Ende läuft <strong>Authentik 2026.8.3</strong> hinter deinem Traefik unter <code>https://DEINE_DOMAIN</code> – als eigener
Identity-Provider mit PostgreSQL im internen Netz. Du hast ein Administratorkonto, kennst den Aufbau
aus Flows, Stages und Providern und kannst Benutzer und Gruppen anlegen.</p>
<p>Zwei Dinge, die viele Anleitungen im Netz noch anders zeigen und die dir Arbeit sparen:</p>
<ul>
<li><strong>Redis ist nicht mehr nötig.</strong> Aktuelle Authentik-Versionen legen Cache und Websocket-Kanäle in
PostgreSQL ab – im Startlog sichtbar als Migrationen <code>django_postgres_cache</code> und
<code>django_channels_postgres</code>. Der Stack besteht nur noch aus <strong>PostgreSQL, server und worker</strong>.</li>
<li><strong>Der Ressourcenbedarf ist überschaubar.</strong> Gemessen im Leerlauf direkt nach der Einrichtung:
server 312 MiB, worker 197 MiB, PostgreSQL 42 MiB – zusammen rund <strong>550 MiB RAM</strong>. Die oft
genannten „mindestens 2 GB&quot; sind eine Empfehlung mit Reserve, kein Startwert.</li>
</ul>
<h3 id="warum-authentik--und-wann-etwas-anderes">Warum Authentik – und wann etwas anderes?</h3>
<p>Die drei üblichen Kandidaten unterscheiden sich vor allem im Gewicht:</p>
<ul>
<li><strong>Authentik</strong> bringt eine vollständige Weboberfläche mit Benutzerverwaltung, Gruppen,
Ereignisprotokoll und Protokollen von OIDC bis LDAP mit. Das ist viel Funktion – und der Grund,
warum es eine Datenbank und einen Worker braucht.</li>
<li><strong>Authelia</strong> ist deutlich leichter, wird über YAML-Dateien konfiguriert und hat keine
Benutzerverwaltung im Browser. Wenn du zwei bis drei Dienste absichern willst und Konfiguration
gern in Dateien hältst, ist es die schlankere Wahl.</li>
<li><strong>Keycloak</strong> ist das Schwergewicht aus der Java-Welt, mächtig im Unternehmenskontext, aber mit
entsprechendem Ressourcen- und Lernaufwand.</li>
</ul>
<p>Für einen selbst betriebenen Stack mit einer Handvoll Diensten und dem Wunsch, Benutzer im Browser
zu verwalten, ist Authentik der beste Kompromiss. Wer nur eine Handvoll statischer Nutzer und
keine Lust auf eine Datenbank hat, ist mit Authelia schneller fertig.</p>
<h2 id="voraussetzungen">Voraussetzungen</h2>
<ul>
<li>Ein Server mit Debian 13 und Docker (getestet: netcup VPS, Docker 29.6.1, Compose v5.3.1)</li>
<li><strong>Mindestens 2 GB RAM</strong> für den Server insgesamt – Authentik nimmt rund 550 MiB, aber
Datenbank-Wartung und Lastspitzen bei vielen Anmeldungen brauchen Luft</li>
<li>Ein <strong>laufender Traefik</strong> samt <code>proxy</code>-Netz (<a href="/tutorials/reverse-proxy-traefik/">Anleitung</a>)</li>
<li>Eine eigene Subdomain, z. B. <code>auth.DEINE_DOMAIN</code>
(<a href="/tutorials/domain-mit-server-verbinden/">Domain verbinden</a>)</li>
<li>Verständnis von <a href="/tutorials/docker-netzwerke-verstehen/">Docker-Netzwerken</a> – wir trennen hier
bewusst öffentliches und internes Netz</li>
</ul>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-amber-400 bg-amber-50 dark:border-amber-700 dark:bg-amber-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">⚠️</span>Authentik wird zum Generalschlüssel
  </p>
  <div class="prose-kitchen text-sm">Ein Identity-Provider bündelt Risiko: Wer ihn übernimmt, übernimmt <strong>alle</strong> angebundenen Dienste.
Deshalb gilt hier strenger als sonst: eigene Subdomain, TLS, ein sehr starkes Administrator-Passwort,
ein zweiter Faktor – und zeitnahe Updates. Wenn du nur zwei Dienste betreibst, ist SSO womöglich mehr
Risiko als Gewinn; der Nutzen beginnt etwa ab einer Handvoll Anwendungen.</div>
</div>
<div class="not-prose my-6 overflow-hidden rounded-xl border border-paprika-200 bg-paprika-50 dark:border-paprika-800 dark:bg-paprika-900/20"
     data-track-content data-content-name="Affiliate-Box · /tutorials/authentik-single-sign-on/" data-content-piece="VPS 2000 G12.5">
  <div class="flex items-center justify-between border-b border-paprika-200 bg-paprika-100 px-4 py-1.5 text-xs font-semibold uppercase tracking-wide text-paprika-700 dark:border-paprika-800 dark:bg-paprika-900/40 dark:text-paprika-300">
    <span>🍳 Empfehlung</span>
    <span title="Mit * markierte Links sind Affiliate-Links.">Anzeige</span>
  </div>
  <div class="flex flex-col gap-4 p-4 sm:flex-row sm:items-center sm:justify-between">
    <div>
      <p class="text-lg font-bold text-slate-900 dark:text-white">VPS 2000 G12.5</p>
      <p class="mt-1 text-sm text-slate-600 dark:text-slate-300">8 vCore · 16 GB RAM · 256 GB SSD</p>
      <p class="mt-1 text-sm font-semibold text-paprika-700 dark:text-paprika-400">ab 26,92 €/Monat</p>
      <p class="mt-2 text-sm text-slate-600 dark:text-slate-400">Für Authentik plus die Dienste dahinter ist der VPS 2000 die entspanntere Wahl.</p>
    </div>
    <a href="https://www.netcup.com/de/server/vps/vps-2000-g12.5-24m-eu?ref=44083#vps-2000-g12.5-12m-eu" rel="sponsored noopener" target="_blank"
   data-track-event="Affiliate|netcup: Affiliate-Box|VPS 2000 G12.5 · {page}"
   class="inline-flex shrink-0 items-center justify-center rounded-lg bg-paprika-600 px-5 py-2.5 font-semibold text-white transition-colors hover:bg-paprika-700">
  Zu netcup →
</a>

  </div><div class="px-4 pb-4"><div class="not-prose my-3 rounded-lg border border-herb-500/40 bg-herb-50 px-3 py-2 text-sm text-slate-700 dark:bg-herb-900/20 dark:text-slate-200"
     data-voucher-api="vps-2000" data-voucher-msg-empty="Gerade ist für diesen Tarif kein Code frei – nimm den 5-€-Code." data-voucher-msg-limit="Für heute sind keine weiteren Codes drin – nimm den 5-€-Code oder versuch es morgen." data-voucher-msg-error="Der Code ließ sich gerade nicht abrufen – nimm den 5-€-Code.">
  <p class="flex flex-wrap items-center gap-x-2 gap-y-1"><span>💶 <span class="font-semibold">1 Monat gratis</span>:</span>
    <button type="button" data-voucher-reveal data-copy-title="Zum Kopieren klicken"
            class="cursor-pointer rounded bg-white px-2 py-0.5 font-mono text-sm font-semibold text-herb-800 hover:ring-2 hover:ring-herb-500/40 dark:bg-slate-800 dark:text-herb-400">Code anzeigen</button>
    <button type="button" data-voucher-next hidden
            class="rounded border border-herb-500/40 px-2 py-0.5 text-xs text-herb-800 hover:bg-white dark:text-herb-400 dark:hover:bg-slate-800">anderer Code</button>
  </p>
  <p class="mt-1 text-xs text-slate-500 dark:text-slate-400">
    Einmal einlösbar, gilt für VPS 2000 G12.5. Im leeren Warenkorb einlösen – der Code legt den Server selbst hinein.
    <a href="https://www.netcup.com/de/checkout/warenkorb?ref=44083" rel="sponsored noopener" target="_blank"
       data-track-event="Affiliate|netcup: Gutschein einlösen|VPS 2000 G12.5 · {page}"
       class="font-medium text-herb-800 underline underline-offset-2 hover:text-herb-900 dark:text-herb-400">Im Warenkorb einlösen →</a>
  </p><p class="mt-2 flex flex-wrap items-center gap-x-2 gap-y-1 border-t border-herb-500/30 pt-2">
    <span>💶 <span class="font-semibold">5 € Gutschein</span> für netcup-Neukunden:</span>
    <button type="button" data-voucher-code data-track-voucher="36nc17844976032"
            title="Zum Kopieren klicken" class="cursor-pointer rounded bg-white px-2 py-0.5 font-mono text-sm font-semibold text-herb-800 hover:ring-2 hover:ring-herb-500/40 dark:bg-slate-800 dark:text-herb-400">36nc17844976032</button>
    <span class="text-xs text-slate-500 dark:text-slate-400">immer gültig · nicht für Domains und VPS Lite</span>
  </p><p class="mt-1 text-xs"><a href="/netcup-gutschein/" class="font-medium text-herb-800 underline underline-offset-2 hover:text-herb-900 dark:text-herb-400">Alle netcup-Gutscheine →</a></p>
</div></div>
</div>

<h2 id="schritt-für-schritt">Schritt für Schritt</h2>
<h3 id="1-verzeichnisse-und-geheimnisse-anlegen">1. Verzeichnisse und Geheimnisse anlegen</h3>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p /opt/authentik/<span class="o">{</span>media,certs,custom-templates<span class="o">}</span> <span class="o">&amp;&amp;</span> <span class="nb">cd</span> /opt/authentik
</span></span><span class="line"><span class="cl">chmod <span class="m">755</span> media certs custom-templates</span></span></code></pre></div>
</div>
<p>Das <code>chmod</code> ist kein Schönheitsfehler. Hast du deinen Server gehärtet und eine strengere umask
gesetzt – etwa <code>UMASK 027</code> in <code>/etc/login.defs</code>, wie sie das Lynis-Audit <span class="text-sm text-slate-500 dark:text-slate-400">(Tutorial folgt voraussichtlich im Oktober)</span> empfiehlt –, erzeugt <code>mkdir</code>
<code>drwxr-x---</code>. Der Authentik-Container läuft als Benutzer 1000 und kommt in solche Verzeichnisse
nicht hinein. Mit <code>chmod 755</code> funktioniert es unabhängig von der umask:</p>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ohne chmod (drwxr-x---)   /if/flow/initial-setup/  -&gt;  HTTP 500
</span></span><span class="line"><span class="cl">mit  chmod (drwxr-xr-x)   /if/flow/initial-setup/  -&gt;  HTTP 200</span></span></code></pre></div>
</div>
<p>Tückisch daran: Der Gesundheitsendpunkt <code>/-/health/ready/</code> antwortet in <strong>beiden</strong> Fällen
mit 200. Der Container gilt als gesund, während die Oberfläche nur Fehler ausliefert.</p>
<p>Authentik braucht zwei Geheimnisse: ein Datenbank-Passwort und einen <code>SECRET_KEY</code>, mit dem Sitzungen
und Token signiert werden. Beide erzeugst du zufällig und legst sie in eine <code>.env</code>:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;PG_PASS=</span><span class="k">$(</span>openssl rand -base64 <span class="m">36</span> <span class="p">|</span> tr -d <span class="s1">&#39;\n=+/&#39;</span><span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;AUTHENTIK_SECRET_KEY=</span><span class="k">$(</span>openssl rand -base64 <span class="m">60</span> <span class="p">|</span> tr -d <span class="s1">&#39;\n&#39;</span><span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span> &gt; .env
</span></span><span class="line"><span class="cl">chmod <span class="m">600</span> .env</span></span></code></pre></div>
</div>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-paprika-400 bg-paprika-50 dark:border-paprika-700 dark:bg-paprika-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">🔥</span>Der SECRET_KEY darf sich nie ändern
  </p>
  <div class="prose-kitchen text-sm">Mit dem <code>AUTHENTIK_SECRET_KEY</code> sind alle Sitzungen und Token signiert. Geht er verloren oder wird er
ersetzt, sind alle Anmeldungen ungültig – im schlimmsten Fall kommst du nicht mehr in die Oberfläche.
Er gehört ins Backup und in den <a href="/tutorials/vaultwarden-passwortmanager/">Passwortmanager</a>, <strong>bevor</strong>
du weitermachst.</div>
</div>
<h3 id="2-compose-datei-mit-getrennten-netzen">2. Compose-Datei mit getrennten Netzen</h3>
<p>Der Aufbau folgt dem Muster aus
<a href="/tutorials/docker-netzwerke-verstehen/">Docker-Netzwerke verstehen</a>: Nur der <code>server</code> hängt im
öffentlichen <code>proxy</code>-Netz, Datenbank und <code>worker</code> liegen im <strong>internen</strong> Netz ohne Internetzugang.
Lege <code>/opt/authentik/compose.yaml</code> an und ersetze <code>DEINE_DOMAIN</code>:</p>
<div class="sk-code">
  <span class="sk-code-head">YAML</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">postgresql</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">postgres:16-alpine</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">unless-stopped</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">healthcheck</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">test</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;CMD-SHELL&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">interval</span><span class="p">:</span><span class="w"> </span><span class="l">30s</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">timeout</span><span class="p">:</span><span class="w"> </span><span class="l">5s</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">retries</span><span class="p">:</span><span class="w"> </span><span class="m">5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">start_period</span><span class="p">:</span><span class="w"> </span><span class="l">20s</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">POSTGRES_DB</span><span class="p">:</span><span class="w"> </span><span class="l">authentik</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">POSTGRES_USER</span><span class="p">:</span><span class="w"> </span><span class="l">authentik</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">POSTGRES_PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="l">${PG_PASS}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">database:/var/lib/postgresql/data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">networks</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">intern]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">server</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">ghcr.io/goauthentik/server:2026.8.3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">unless-stopped</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="l">server</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">depends_on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">postgresql</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">condition</span><span class="p">:</span><span class="w"> </span><span class="l">service_healthy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">AUTHENTIK_SECRET_KEY</span><span class="p">:</span><span class="w"> </span><span class="l">${AUTHENTIK_SECRET_KEY}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">AUTHENTIK_POSTGRESQL__HOST</span><span class="p">:</span><span class="w"> </span><span class="l">postgresql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">AUTHENTIK_POSTGRESQL__NAME</span><span class="p">:</span><span class="w"> </span><span class="l">authentik</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">AUTHENTIK_POSTGRESQL__USER</span><span class="p">:</span><span class="w"> </span><span class="l">authentik</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">AUTHENTIK_POSTGRESQL__PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="l">${PG_PASS}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">AUTHENTIK_ERROR_REPORTING__ENABLED</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;false&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS</span><span class="p">:</span><span class="w"> </span><span class="m">172.16.0.0</span><span class="l">/12</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./media:/media</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./custom-templates:/templates</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">shm_size</span><span class="p">:</span><span class="w"> </span><span class="l">512mb</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">networks</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">proxy, intern]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.enable=true&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.routers.authentik.rule=Host(`DEINE_DOMAIN`)&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.routers.authentik.entrypoints=websecure&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.routers.authentik.tls.certresolver=le&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.services.authentik.loadbalancer.server.port=9000&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">worker</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">ghcr.io/goauthentik/server:2026.8.3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">unless-stopped</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="l">worker</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">user</span><span class="p">:</span><span class="w"> </span><span class="l">root</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">depends_on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">postgresql</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">condition</span><span class="p">:</span><span class="w"> </span><span class="l">service_healthy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">AUTHENTIK_SECRET_KEY</span><span class="p">:</span><span class="w"> </span><span class="l">${AUTHENTIK_SECRET_KEY}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">AUTHENTIK_POSTGRESQL__HOST</span><span class="p">:</span><span class="w"> </span><span class="l">postgresql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">AUTHENTIK_POSTGRESQL__NAME</span><span class="p">:</span><span class="w"> </span><span class="l">authentik</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">AUTHENTIK_POSTGRESQL__USER</span><span class="p">:</span><span class="w"> </span><span class="l">authentik</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">AUTHENTIK_POSTGRESQL__PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="l">${PG_PASS}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">AUTHENTIK_ERROR_REPORTING__ENABLED</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;false&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/var/run/docker.sock:/var/run/docker.sock</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./media:/media</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./certs:/certs</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./custom-templates:/templates</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">shm_size</span><span class="p">:</span><span class="w"> </span><span class="l">512mb</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">networks</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">intern]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">database</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">networks</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">proxy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">external</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">intern</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">internal</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span></span></span></code></pre></div>
</div>
<p>Vier Punkte, die hier zählen:</p>
<ul>
<li>
<p><strong><code>loadbalancer.server.port=9000</code></strong> – Authentik lauscht intern auf 9000 (HTTP). Der eigene
HTTPS-Port 9443 bleibt ungenutzt: TLS macht Traefik.</p>
</li>
<li>
<p><strong><code>shm_size: 512mb</code></strong> stammt aus der offiziellen Referenz und verhindert Speicherfehler bei
größeren Datenbankabfragen.</p>
</li>
<li>
<p><strong><code>user: root</code> plus Docker-Socket am worker</strong> sind nötig, damit Authentik später eigene
„Outposts&quot; als Container verwalten kann. Der Socket ist hier bewusst <strong>nicht</strong> <code>:ro</code> eingebunden:
Authentik muss Container anlegen, beenden und entfernen, und das geht nur mit Schreibzugriff.
Wer die Outpost-Verwaltung nicht braucht, lässt den Socket weg.</p>
<p>Schreibzugriff auf den Docker-Socket ist allerdings faktisch Root auf dem Host. Authentik
empfiehlt deshalb selbst, einen <strong>Docker-Socket-Proxy</strong> davorzusetzen, der nur die nötigen
API-Aufrufe durchlässt (Container anlegen, lesen, beenden, entfernen, Version abfragen). Auf
einem Server, der mehr tut als Authentik zu betreiben, ist das die Mühe wert.</p>
</li>
<li>
<p><strong><code>AUTHENTIK_ERROR_REPORTING__ENABLED: &quot;false&quot;</code></strong> schaltet die Fehlerübermittlung an das Projekt
ab. Standard ist sie <strong>aktiv</strong>; für datenschutzfreundlichen Betrieb gehört sie aus.</p>
</li>
<li>
<p><strong><code>AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS</code></strong> ist seit <strong>2026.8</strong> Pflicht hinter einem Reverse Proxy.
Authentik wertet <code>X-Forwarded-Proto</code>, <code>X-Forwarded-Host</code> und <code>X-Forwarded-For</code> nur noch aus, wenn
die Verbindung aus einem hier genannten Netz kommt – das verhindert gefälschte Proxy-Header. Trag
das Docker-Netz ein, aus dem Traefik anfragt (Docker vergibt standardmäßig aus <code>172.16.0.0/12</code>).
Fehlt der Eintrag, baut Authentik Weiterleitungen auf <code>http://</code> statt <code>https://</code> und die Oberfläche
hängt in einer Endlos-Ladeanzeige. Die genaue Adresse deines Traefik findest du mit
<code>docker network inspect proxy</code>.</p>
</li>
</ul>
<h3 id="3-starten-und-auf-die-migrationen-warten">3. Starten und auf die Migrationen warten</h3>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> /opt/authentik <span class="o">&amp;&amp;</span> docker compose up -d</span></span></code></pre></div>
</div>
<p>Beim <strong>ersten</strong> Start legt Authentik sein Datenbankschema an – über 70 Migrationen, das dauert.
Praktisch prüfbar ist der Fortschritt am Health-Endpunkt:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s -o /dev/null -w <span class="s2">&#34;%{http_code}\n&#34;</span> https://DEINE_DOMAIN/-/health/ready/</span></span></code></pre></div>
</div>
<p>Solange die Migrationen laufen, antwortet er mit <code>404</code>; ist Authentik bereit, mit <code>200</code>. Auf dem
VPS 1000 dauerte das zuletzt knapp <strong>zwei Minuten</strong>. Der Einrichtungs-Flow kommt noch ein paar
Sekunden später – der <code>worker</code> spielt erst danach die mitgelieferten Abläufe ein; im Test war
er 15 Sekunden nach dem ersten <code>200</code> erreichbar. Parallel der Zustand der Container:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker compose ps --format <span class="s2">&#34;{{.Name}}\t{{.Status}}&#34;</span></span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">authentik-postgresql-1   Up 27 seconds (healthy)
</span></span><span class="line"><span class="cl">authentik-server-1       Up 21 seconds (health: starting)
</span></span><span class="line"><span class="cl">authentik-worker-1       Up 21 seconds (healthy)</span></span></code></pre></div>
</div>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-herb-400 bg-herb-50 dark:border-herb-700 dark:bg-herb-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">🧑‍🍳</span>Tipp
  </p>
  <div class="prose-kitchen text-sm"><code>docker logs -f authentik-server-1</code> zeigt die Migrationen einzeln und danach den Start des
Webservers. Erst dann lohnt der Blick in den Browser – vorher siehst du nur Fehlerseiten.</div>
</div>
<h3 id="4-administratorkonto-anlegen">4. Administratorkonto anlegen</h3>
<p>Jetzt der Schritt, an dem viele stolpern: Ruf die <strong>Wurzel-Adresse</strong> auf, also
<code>https://DEINE_DOMAIN/</code>. Von dort leitet Authentik in den Einrichtungs-Flow.</p>
<p>Rufst du den Flow direkt auf (<code>/if/flow/initial-setup/</code>), verweigert Authentik den Dienst mit
„Anfrage wurde verweigert – Access the authentik setup by navigating to https://…/&quot;. Das ist kein
Fehler in deiner Konfiguration, sondern Absicht.</p>
<p><figure class="my-6"><img src="/tutorials/authentik-single-sign-on/setup_hu_fa3f6f8159df2dab.webp" srcset="/tutorials/authentik-single-sign-on/setup_hu_18c7fc076c1653f9.webp 480w, /tutorials/authentik-single-sign-on/setup_hu_fa3f6f8159df2dab.webp 768w, /tutorials/authentik-single-sign-on/setup_hu_ac0943ac27866ae7.webp 1200w, /tutorials/authentik-single-sign-on/setup_hu_9f4e09e69965626c.webp 1920w" sizes="(min-width: 768px) 768px, 100vw"
    width="768" height="432"
    data-full="/tutorials/authentik-single-sign-on/setup_hu_13cac606eb75928.webp"
    alt="Der Einrichtungsdialog von Authentik mit Feldern für E-Mail-Adresse und Passwort des ersten Administratorkontos" title="Ersteinrichtung: E-Mail und Passwort für das Administratorkonto"
    loading="lazy" decoding="async" class="rounded-lg"><figcaption class="mt-2 text-sm text-center text-slate-500 italic">Ersteinrichtung: E-Mail und Passwort für das Administratorkonto</figcaption></figure></p>
<p>Trag die E-Mail-Adresse und ein <strong>sehr</strong> langes Passwort ein. Zwei Dinge, die man wissen sollte:</p>
<ul>
<li>Der Benutzername des erzeugten Kontos lautet <strong><code>akadmin</code></strong> – nicht deine E-Mail-Adresse.</li>
<li>Die Oberfläche erscheint in der Sprache deines Browsers; bei einem deutschen Browser also gleich
auf Deutsch.</li>
</ul>
<p>Nach dem Absenden landest du in der <strong>Benutzer-Oberfläche</strong> (<code>/if/user/</code>) – der Ansicht, die später
alle deine Nutzer sehen: eine Kachel-Bibliothek der für sie freigegebenen Anwendungen.</p>
<h3 id="5-die-administrator-oberfläche">5. Die Administrator-Oberfläche</h3>
<p>Die Verwaltung liegt unter <code>https://DEINE_DOMAIN/if/admin/</code> – oder über den Knopf oben rechts:</p>
<p><figure class="my-6"><img src="/tutorials/authentik-single-sign-on/dashboard_hu_909686b69dd89f8e.webp" srcset="/tutorials/authentik-single-sign-on/dashboard_hu_4793c27e7aa5e960.webp 480w, /tutorials/authentik-single-sign-on/dashboard_hu_909686b69dd89f8e.webp 768w, /tutorials/authentik-single-sign-on/dashboard_hu_d8aad4621963b704.webp 1200w, /tutorials/authentik-single-sign-on/dashboard_hu_5e3fc4298cfabc8.webp 1920w" sizes="(min-width: 768px) 768px, 100vw"
    width="768" height="432"
    data-full="/tutorials/authentik-single-sign-on/dashboard_hu_2fe50f72eaed77e4.webp"
    alt="Die Authentik-Administrationsübersicht mit System Status OK, der installierten Version, einem Arbeiter, Outpost-Status und den letzten Ereignissen" title="Administrationsübersicht: Systemzustand, Version, Arbeiter und letzte Ereignisse"
    loading="lazy" decoding="async" class="rounded-lg"><figcaption class="mt-2 text-sm text-center text-slate-500 italic">Administrationsübersicht: Systemzustand, Version, Arbeiter und letzte Ereignisse</figcaption></figure></p>
<p>Vier Kacheln beantworten die typischen Betriebsfragen:</p>
<ul>
<li><strong>System Status</strong> – „Alles funktioniert&quot; heißt: Datenbank erreichbar, keine offenen Warnungen.</li>
<li><strong>Version</strong> – die laufende Version. Steht dort „Neueste Version unbekannt&quot;, konnte Authentik den
Versions-Check nicht durchführen; bei abgeschottetem ausgehendem Verkehr ist das normal.</li>
<li><strong>Arbeiter</strong> – muss <strong>mindestens 1</strong> sein. Steht hier <code>0</code>, läuft der <code>worker</code> nicht, und dann
passiert vieles stillschweigend nicht: keine E-Mails, keine Zertifikatsrotation, keine geplanten
Aufgaben.</li>
<li><strong>Letzte Ereignisse</strong> – jede Anmeldung und Änderung mit Benutzer, Zeit und Client-IP. Das ist dein
Audit-Log und die erste Anlaufstelle bei „wer hat das geändert?&quot;.</li>
</ul>
<h3 id="6-das-modell-verstehen-flows-stages-provider-anwendungen">6. Das Modell verstehen: Flows, Stages, Provider, Anwendungen</h3>
<p>Authentik ist kein Login-Formular, sondern ein Baukasten. Wer diese Begriffe kennt, findet sich in
der Oberfläche sofort zurecht – und versteht die Fehlermeldungen.</p>
<p><figure class="my-6"><img src="/tutorials/authentik-single-sign-on/flows_hu_76e9055b3ada4c20.webp" srcset="/tutorials/authentik-single-sign-on/flows_hu_7f3427548a274836.webp 480w, /tutorials/authentik-single-sign-on/flows_hu_76e9055b3ada4c20.webp 768w, /tutorials/authentik-single-sign-on/flows_hu_800cab57a83cb007.webp 1200w, /tutorials/authentik-single-sign-on/flows_hu_c7e01c8459d43a80.webp 1920w" sizes="(min-width: 768px) 768px, 100vw"
    width="768" height="432"
    data-full="/tutorials/authentik-single-sign-on/flows_hu_e1363ec695d438e2.webp"
    alt="Die Flow-Übersicht in Authentik mit den Standard-Flows für Authentifizierung, Autorisierung, Invalidierung, Registrierung und Stage-Konfiguration" title="Die mitgelieferten Flows – jeder ist eine Kette von Stages"
    loading="lazy" decoding="async" class="rounded-lg"><figcaption class="mt-2 text-sm text-center text-slate-500 italic">Die mitgelieferten Flows – jeder ist eine Kette von Stages</figcaption></figure></p>
<ul>
<li><strong>Stage</strong> – ein einzelner Schritt: Passwort abfragen, TOTP-Code prüfen, Zustimmung einholen,
Benutzer schreiben. Ein Stage kann nichts allein.</li>
<li><strong>Flow</strong> – eine <strong>Kette von Stages</strong> für einen Zweck. Authentik bringt fertige mit, sichtbar in der
Liste: <code>default-authentication-flow</code> (Anmeldung, 4 Stages), <code>default-invalidation-flow</code>
(Abmeldung), <code>default-source-enrollment</code> (Registrierung über eine externe Quelle), dazu Flows zum
Einrichten von TOTP und statischen Wiederherstellungscodes.</li>
<li><strong>Richtlinie (Policy)</strong> – eine Bedingung, die entscheidet, ob ein Stage greift oder ein Zugriff
erlaubt ist: „nur Mitglieder dieser Gruppe&quot;, „nur aus diesem Netz&quot;, „nur mit bestätigter E-Mail&quot;.</li>
<li><strong>Provider</strong> – die technische Schnittstelle zu <strong>einem</strong> Dienst: OAuth2/OpenID Connect, SAML, LDAP
oder der <strong>Proxy-Provider</strong> für Anwendungen ohne eigenes Login.</li>
<li><strong>Anwendung</strong> – die Klammer: verbindet einen Provider mit Berechtigungen und erzeugt die Kachel in
der Benutzer-Bibliothek.</li>
<li><strong>Outpost</strong> – eine ausgelagerte Komponente, die Authentik selbst betreibt (etwa für den
Proxy-Provider). Genau dafür braucht der worker den Docker-Socket.</li>
</ul>
<p>Für den Alltag heißt das: Die Standard-Flows fasst du <strong>nicht</strong> an. Du legst pro Dienst einen
Provider plus eine Anwendung an – und genau das ist der Inhalt von
<a href="/tutorials/authentik-apps-anbinden/">Teil 2</a>.</p>
<p>Was dabei beim Anmelden tatsächlich passiert, lässt sich an den Standard-Flows ablesen. Ruft ein
Benutzer einen geschützten Dienst auf, läuft der <code>default-authentication-flow</code> mit seinen vier
Stages:</p>
<ol>
<li><strong>Identifikation</strong> – wer bist du? (Benutzername oder E-Mail, je nach Einstellung)</li>
<li><strong>Passwort</strong> – die Prüfung gegen das gespeicherte Passwort</li>
<li><strong>Authenticator-Validierung</strong> – gibt es einen zweiten Faktor, wird er hier verlangt</li>
<li><strong>Benutzer-Login</strong> – die Sitzung wird angelegt</li>
</ol>
<p>Danach übernimmt der <strong>Autorisierungs</strong>-Flow: Er entscheidet, ob dieser Benutzer diese Anwendung
nutzen darf (<code>default-provider-authorization-explicit-consent</code> fragt einmal um Zustimmung,
<code>…-implicit-consent</code> überspringt die Rückfrage). Beim Abmelden greift der <strong>Invalidierungs</strong>-Flow.</p>
<p>Wenn später etwas nicht klappt, ist genau diese Reihenfolge die Suchhilfe: Bleibt der Nutzer beim
Passwort hängen, ist es ein Problem im Authentifizierungs-Flow. Kommt er durch, sieht die Anwendung
aber nicht, liegt es an der Autorisierung – also an Berechtigungen oder Richtlinien.</p>
<h3 id="7-benutzer-und-gruppen-anlegen">7. Benutzer und Gruppen anlegen</h3>
<p>Unter <strong>Verzeichnis → Benutzer</strong> erzeugst du Konten. Wichtig ist die Trennung:</p>
<ul>
<li><strong>Benutzer</strong> haben Benutzernamen, Namen, E-Mail und optional Attribute.</li>
<li><strong>Gruppen</strong> bündeln Benutzer – und <strong>an Gruppen</strong> hängst du später die Berechtigungen für
Anwendungen. Lege also gleich Gruppen an (<code>familie</code>, <code>admins</code>), statt jedem Benutzer einzeln Rechte
zu geben. Sonst pflegst du bei zehn Diensten zehnmal dieselbe Liste.</li>
</ul>
<p>Das Konto <code>akadmin</code> nutzt du danach nur noch für Verwaltung; für den Alltag legst du dir ein normales
Konto an. Das ist dieselbe Trennung wie „nicht als root arbeiten&quot;.</p>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-herb-400 bg-herb-50 dark:border-herb-700 dark:bg-herb-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">🧑‍🍳</span>Zweiten Faktor für den Administrator
  </p>
  <div class="prose-kitchen text-sm">Authentik bringt TOTP mit – der Flow <code>default-authenticator-totp-setup</code> steht in der Liste aus
Schritt 6. Registriere für <code>akadmin</code> einen zweiten Faktor über die Benutzer-Oberfläche
(<strong>Einstellungen → MFA-Geräte</strong>), bevor du Dienste anbindest. Ein Identity-Provider mit einfachem
Passwortschutz erfüllt seinen Zweck nur zur Hälfte. Wie TOTP grundsätzlich funktioniert, steht in
<a href="/tutorials/ssh-2fa-totp/">SSH mit 2FA</a>.</div>
</div>
<h3 id="8-authentik-selbst-härten">8. Authentik selbst härten</h3>
<p>Der Identity-Provider ist ab jetzt das lohnendste Ziel auf deinem Server. Vier Maßnahmen, die
zusammen wenig Aufwand kosten:</p>
<ul>
<li><strong>Rate-Limit und Security-Header davorschalten.</strong> Authentik hat eine eigene Reputationsprüfung,
aber die erste Bremse gehört an den Reverse Proxy. Häng die Middlewares aus
<a href="/tutorials/traefik-middlewares-absichern/">Traefik härten</a> an den Router – ein Rate-Limit macht
Passwort-Raten unattraktiv, bevor die Anfrage Authentik überhaupt erreicht.</li>
<li><strong>Die Administrationsoberfläche einschränken.</strong> <code>/if/admin/</code> brauchst du selten und selten von
unterwegs. Eine IP-Allowlist oder ein Zugang nur über
<a href="/tutorials/wireguard-vpn-einrichten/">WireGuard</a> ist hier angemessen – ohne den normalen
Anmeldeweg für deine Nutzer einzuschränken.</li>
<li><strong>Zweiten Faktor verpflichtend machen</strong>, mindestens für Administratoren. In Authentik hängt das am
Authenticator-Validierungs-Stage des Anmelde-Flows.</li>
<li><strong>Fehlerübermittlung aus lassen</strong> – im Compose oben schon gesetzt. Standardmäßig schickt Authentik
Fehlerberichte an das Projekt; für einen Dienst, der alle Anmeldedaten deiner Umgebung sieht,
gehört das abgeschaltet.</li>
</ul>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-amber-400 bg-amber-50 dark:border-amber-700 dark:bg-amber-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">⚠️</span>Kein SSO vor den Notausgang
  </p>
  <div class="prose-kitchen text-sm">Verlockend, aber gefährlich: Authentik <strong>vor</strong> Dienste zu hängen, die du im Notfall brauchst, um
Authentik zu reparieren. Ein Dockge oder Portainer hinter Forward-Auth ist eine schöne Idee – bis
Authentik selbst kaputt ist und du dich nirgends mehr anmelden kannst. Für solche Werkzeuge bleibt
SSH oder ein lokales Konto der Notausgang.</div>
</div>
<h2 id="wenn-es-nicht-funktioniert">Wenn es nicht funktioniert</h2>
<div class="troubleshoot not-prose">
<p><strong>Die Oberfläche liefert <code>Internal Server Error</code>, der Container gilt aber als gesund.</strong> Dann
kann Authentik seine Vorlagenverzeichnisse nicht lesen. Typisch für Server mit <code>UMASK 027</code>:
<code>mkdir</code> erzeugt dort <code>drwxr-x---</code> – der Container läuft als Benutzer 1000 und kommt nicht
hinein. Im Log steht <code>PermissionError: [Errno 13] Permission denied: '/templates/if/error.html'</code>. Behebe es mit <code>chmod 755 media certs custom-templates</code> im
Stack-Verzeichnis und starte neu.</p>
<p><strong>Die Seite antwortet minutenlang mit 404, obwohl die Container laufen.</strong> Beim ersten Start laufen
über 70 Datenbank-Migrationen; solange gibt auch <code>/-/health/ready/</code> ein <code>404</code> zurück. Auf dem VPS 1000 war das
nach knapp zwei Minuten vorbei, der Einrichtungs-Flow 15 Sekunden später. <code>docker logs -f authentik-server-1</code> zeigt den Fortschritt.</p>
<p><strong>Beim Aufruf des Setup-Flows kommt „Anfrage wurde verweigert&quot;.</strong> Der Einrichtungs-Flow lässt sich
nicht direkt über <code>/if/flow/initial-setup/</code> aufrufen. Geh auf die Wurzel-Adresse
<code>https://DEINE_DOMAIN/</code>, von dort leitet Authentik korrekt weiter.</p>
<p><strong>Die Kachel „Arbeiter&quot; zeigt 0.</strong> Der <code>worker</code>-Container läuft nicht oder erreicht die Datenbank
nicht. Ohne ihn bleiben E-Mails, geplante Aufgaben und Outpost-Verwaltung liegen – ohne
Fehlermeldung in der Oberfläche. Prüfe <code>docker compose ps</code> und die Logs des Workers.</p>
<p><strong>Traefik antwortet mit 404.</strong> Es fehlt meist
<code>traefik.http.services.authentik.loadbalancer.server.port=9000</code>. Der Container lauscht auf 9000;
Port 9443 ist Authentiks eigenes HTTPS, das wir bewusst nicht nutzen.</p>
<p><strong>Nach einem Neuaufsetzen sind alle Sitzungen ungültig oder die Anmeldung schlägt fehl.</strong> Dann hat
sich der <code>AUTHENTIK_SECRET_KEY</code> geändert – etwa weil die <code>.env</code> neu erzeugt wurde. Der Schlüssel muss
über die Lebensdauer der Installation gleich bleiben; stell ihn aus dem Backup wieder her.</p>
<p><strong>Der Benutzername passt nicht.</strong> Das beim Setup erzeugte Konto heißt <code>akadmin</code>, nicht wie deine
E-Mail-Adresse. Wer sich mit der Adresse anmelden will, muss das im Identifikations-Stage des
Anmelde-Flows erlauben.</p>

</div>
<p class="mt-3 text-sm text-slate-500 dark:text-slate-400">
  Dein Problem ist nicht dabei?
  <a href="/fehler/" class="font-medium text-paprika-700 hover:underline dark:text-paprika-400">Alle Fehlermeldungen durchsuchen →</a>
</p>

<h2 id="wartung--backups">Wartung &amp; Backups</h2>
<p><strong>Updates sind hier Pflichtprogramm.</strong> Das Projekt veröffentlicht häufig – in der Reihe 2026.5 kamen
mehrere Korrekturversionen innerhalb weniger Wochen. Weil Authentik der Generalschlüssel für alle
angebundenen Dienste ist, willst du Sicherheitskorrekturen zeitnah haben:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> /opt/authentik <span class="o">&amp;&amp;</span> docker compose pull <span class="o">&amp;&amp;</span> docker compose up -d</span></span></code></pre></div>
</div>
<p><strong>Vorher lesen, dann aktualisieren.</strong> Authentik nutzt datumsbasierte Versionen (<code>2026.8.3</code>).
Zwischen <strong>Minor</strong>-Versionen (2026.2 → 2026.5) gibt es regelmäßig Umstellungen, die Handarbeit
brauchen; die Release-Notes nennen sie ausdrücklich. Springe nie über mehrere Minor-Versionen auf
einmal, und mach vorher ein Backup: Die Datenbank wird beim Start migriert, und das ist nicht ohne
Weiteres umkehrbar.</p>
<p><strong>Was ins Backup gehört:</strong></p>
<ul>
<li>
<p><strong><code>.env</code></strong> – enthält <code>SECRET_KEY</code> und Datenbank-Passwort. Ohne den Schlüssel ist der Rest wertlos.</p>
</li>
<li>
<p><strong>Die PostgreSQL-Datenbank</strong> – dort stecken Benutzer, Gruppen, Flows, Provider und Anwendungen.
Sichere sie <strong>nicht</strong> als Dateikopie des Volumes, sondern als Dump:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker compose <span class="nb">exec</span> -T postgresql pg_dump -U authentik authentik <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="p">|</span> gzip &gt; /opt/backup/authentik-<span class="k">$(</span>date +%F<span class="k">)</span>.sql.gz</span></span></code></pre></div>
</div>
</li>
<li>
<p><strong><code>media/</code></strong> – hochgeladene Logos und Symbole. Klein, aber ärgerlich zu rekonstruieren.</p>
</li>
</ul>
<p>Den Dump nimmt dann <a href="/tutorials/backups-mit-restic/">Restic</a> mit. Warum kein Volume-Kopie: Eine
laufende Datenbank kopiert man nicht dateiweise – dabei entstehen inkonsistente Sicherungen, die
erst beim Wiederherstellen auffallen.</p>
<p><strong>Den Ausfall mitdenken.</strong> Wenn Authentik steht, kommt niemand mehr in die angebundenen Dienste – das
ist die Kehrseite von Single Sign-On. Zwei Vorkehrungen kosten wenig: In jedem angebundenen Dienst
ein <strong>lokales Notfall-Konto</strong> behalten, das ohne SSO funktioniert, und Authentik in die
<a href="/tutorials/uptime-kuma-monitoring/">Überwachung</a> aufnehmen – am besten mit einem Check auf
<code>/-/health/ready/</code>, der genau das prüft, was zählt.</p>
<p><strong>Nutzer und Gruppen aufräumen.</strong> Ein Identity-Provider wächst mit: Gäste, alte Konten, Testnutzer.
Nimm dir zweimal im Jahr die Benutzerliste vor und <strong>deaktiviere</strong>, was nicht mehr gebraucht wird –
deaktivieren ist besser als löschen, weil sonst die Ereignisprotokolle Lücken bekommen.</p>
]]></content:encoded></item></channel></rss>