<?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>Wartung – Serverküche</title><link>https://serverkueche.de/tags/wartung/</link><description>Wartung – 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>Mon, 27 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://serverkueche.de/tags/wartung/index.xml" rel="self" type="application/rss+xml"/><item><title>Den ganzen Docker-Stack sicher aktuell halten</title><link>https://serverkueche.de/tutorials/docker-stack-aktuell-halten/</link><pubDate>Mon, 27 Jul 2026 00:00:00 +0000</pubDate><author>feedback@serverkueche.de (Serverküche)</author><guid>https://serverkueche.de/tutorials/docker-stack-aktuell-halten/</guid><description>Update-Disziplin für deinen Docker-Stack: Versionen pinnen, mit Diun über Updates informiert werden, sicher einspielen, aufräumen – statt blindem Auto-Update.</description><content:encoded><![CDATA[<p>Ein selbstgehosteter Stack ist nie „fertig&quot;. Container-Images bekommen Sicherheitsupdates,
Apps neue Funktionen – und wer nicht nachzieht, läuft irgendwann verwundbare Software. Dieses
Rezept macht aus „ich sollte mal updaten&quot; eine verlässliche Routine: informiert, kontrolliert
und mit Netz.</p>
<h2 id="was-bauen-wir">Was bauen wir?</h2>
<p>Kein Werkzeug, sondern eine <strong>Update-Disziplin</strong> für deinen kompletten Docker-Stack. Am Ende
hast du: <strong>gepinnte Versionen</strong> (nie wieder <code>latest</code>), einen <strong>Update-Melder</strong> (Diun), der dir
sagt, <em>wann</em> etwas Neues da ist, eine <strong>sichere Einspiel-Routine</strong> mit Backup davor und
Rollback-Plan – und einen Aufräum-Rhythmus, damit dir alte Images nicht die Platte vollmüllen.
Getestet mit <strong>Diun v4.33.0</strong> unter Docker 29 / Compose v5.3.</p>
<p>Die Leitidee ist bewusst <strong>nicht</strong> „alles automatisch aktualisieren&quot;. Bei zustandsbehafteten
Apps (Datenbanken, Nextcloud, Immich) kann ein unbeaufsichtigtes Update mitten in der Nacht
eine Migration auslösen, die schiefgeht – und niemand ist da. Der sichere Weg ist:
<strong>pinnen → benachrichtigen lassen → bewusst einspielen → prüfen</strong>. Blindes Auto-Update behandeln
wir am Ende ehrlich (Schritt 6): Es hat seinen Platz, aber einen kleinen.</p>
<h2 id="voraussetzungen">Voraussetzungen</h2>
<ul>
<li>Grundwissen zu <a href="/tutorials/docker-compose-grundlagen/">Docker Compose</a> – Tags, Volumes und
<code>compose.yaml</code> solltest du lesen können.</li>
<li>Ein laufender Stack, den du pflegen willst (z. B. die Apps hinter deinem
<a href="/tutorials/reverse-proxy-traefik/">Reverse Proxy</a>).</li>
<li>Eine funktionierende <a href="/tutorials/backups-mit-restic/">Backup-Strategie mit Restic</a> – sie ist
das Sicherheitsnetz, das Updates erst entspannt macht.</li>
</ul>
<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/docker-stack-aktuell-halten/" data-content-piece="VPS 2000 G12">
  <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</p>
      <p class="mt-1 text-sm text-slate-600 dark:text-slate-300">8 vCore · 16 GB RAM · 512 GB NVMe</p>
      <p class="mt-1 text-sm font-semibold text-paprika-700 dark:text-paprika-400">ab 19,24 €/Monat</p>
      <p class="mt-2 text-sm text-slate-600 dark:text-slate-400">Ein gepflegter Multi-App-Stack samt Datenbanken fühlt sich auf dem VPS 2000 spürbar wohler.</p>
    </div>
    <a href="https://www.netcup.com/de/server/vps/vps-2000-g12-12m?ref=44083" rel="sponsored noopener" target="_blank"
   data-track-event="Affiliate|netcup: Affiliate-Box|VPS 2000 G12 · {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"><p class="not-prose my-3 flex flex-wrap items-center gap-x-2 gap-y-1 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">
  <span>💶 <strong>5 € Gutschein</strong> für netcup-Neukunden:</span><code data-track-voucher="36nc17844976032"
        class="rounded bg-white px-2 py-0.5 font-mono text-sm font-semibold text-herb-800 dark:bg-slate-800 dark:text-herb-400">36nc17844976032</code>
  <span class="text-xs text-slate-500 dark:text-slate-400">(nur Neukunden, keine Domains)</span>
</p></div>
</div>

<h2 id="schritt-für-schritt">Schritt für Schritt</h2>
<h3 id="schritt-1-nie-wieder-latest--versionen-bewusst-pinnen">Schritt 1: Nie wieder <code>latest</code> – Versionen bewusst pinnen</h3>
<p>Der häufigste Fehler steht in unzähligen Anleitungen: <code>image: nextcloud:latest</code>. Das Problem:
<code>latest</code> ist ein bewegliches Ziel. Ein <code>docker compose pull</code> kann dir jederzeit ungefragt eine
neue Hauptversion samt Breaking Changes ziehen – du weißt nie, was du gerade fährst, und ein
Rollback ist kaum möglich.</p>
<p>Pinne stattdessen jeden Dienst auf einen <strong>festen Tag</strong>. Zwei sinnvolle Stufen:</p>
<ul>
<li><strong>Major-Tag</strong> (<code>postgres:18</code>, <code>nextcloud:34-apache</code>): bekommt automatisch Patch- und
Minor-Updates derselben Hauptversion beim nächsten <code>pull</code>, aber <strong>nie</strong> einen Major-Sprung.
Guter Kompromiss für die meisten Dienste.</li>
<li><strong>Exakte Version</strong> (<code>vaultwarden/server:1.37.1</code>): volle Kontrolle, nichts ändert sich ohne
dein Zutun. Ideal für heikle, zustandsbehaftete Apps.</li>
</ul>
<p>Was du <strong>gerade</strong> fährst, zeigt dir Compose pro Projekt:</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> ~/DEINE_APP <span class="o">&amp;&amp;</span> docker compose images</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">CONTAINER           REPOSITORY          TAG                 PLATFORM            IMAGE ID            SIZE                CREATED
</span></span><span class="line"><span class="cl">diun-demo-whoami    traefik/whoami      v1.11.0             linux/amd64         200689790a0a        3.04MB              17 months ago</span></span></code></pre></div>
</div>
<p>Steht in der <code>TAG</code>-Spalte irgendwo <code>latest</code>, ist das dein erster Kandidat zum Pinnen. Trag den
konkreten Tag in die <code>compose.yaml</code> ein – das ist die Grundlage für alles Weitere.</p>
<p>Welche Version aktuell <strong>stabil</strong> ist, schlägst du an der Quelle nach, nicht im Bauchgefühl:
die <strong>Tags-Übersicht auf Docker Hub</strong> (bzw. GHCR) des Images oder die <strong>Release-Notes</strong> auf
GitHub. Für offizielle Images geht das auch per API:</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 <span class="s2">&#34;https://hub.docker.com/v2/repositories/library/postgres/tags/?page_size=20&#34;</span> <span class="p">|</span> grep -oE <span class="s1">&#39;&#34;name&#34;:&#34;[0-9.]+&#34;&#39;</span></span></span></code></pre></div>
</div>
<p>Nimm die höchste stabile Version derselben Reihe, die du fahren willst – Vorabversionen
(<code>rc</code>, <code>beta</code>) bleiben außen vor.</p>
<h3 id="schritt-2-sehen-was-veraltet-ist--der-update-melder-diun">Schritt 2: Sehen, was veraltet ist – der Update-Melder Diun</h3>
<p>Pinnen heißt: Updates kommen nicht mehr von allein. Also brauchst du jemanden, der dir
<strong>Bescheid gibt</strong>, wenn ein neues Image da ist. Genau das macht <strong>Diun</strong> (Docker Image Update
Notifier) – es aktualisiert <strong>nichts</strong>, es beobachtet nur und benachrichtigt. Leg ihm einen
eigenen Ordner an:</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">mkdir -p ~/diun <span class="o">&amp;&amp;</span> <span class="nb">cd</span> ~/diun</span></span></code></pre></div>
</div>
<p>Die <code>compose.yaml</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">diun</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">crazymax/diun:4.33.0</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">serve</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="s2">&#34;TZ=Europe/Berlin&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;DIUN_WATCH_SCHEDULE=0 8 * * *&#34;</span><span class="w">          </span><span class="c"># täglich 8 Uhr</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;DIUN_PROVIDERS_DOCKER=true&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT=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">./data:/data</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:ro</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></span></code></pre></div>
</div>
<ul>
<li><strong><code>DIUN_PROVIDERS_DOCKER=true</code></strong> lässt Diun deine laufenden Container über den Docker-Socket
entdecken.</li>
<li><strong><code>WATCHBYDEFAULT=false</code></strong> heißt: Diun beobachtet nur Container, die du <strong>ausdrücklich</strong>
markierst – so bekommst du keine Flut zu Dingen, die dich nicht interessieren.</li>
<li><strong><code>DIUN_WATCH_SCHEDULE</code></strong> ist ein Cron-Ausdruck; einmal täglich reicht völlig und schont die
Registry-Rate-Limits.</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>Der Docker-Socket ist kein harmloser Lese-Zugriff
  </p>
  <div class="prose-kitchen text-sm">Wir mounten den Socket hier mit <code>:ro</code>, und das klingt beruhigend – ist es aber nur halb. Das <code>:ro</code>
verhindert bloß, dass die <em>Datei</em> <code>docker.sock</code> überschrieben wird; über die Docker-API dahinter
lässt sich trotzdem alles anstellen: Container mit <code>--privileged</code> starten, das Host-Dateisystem
einhängen, kurz <strong>Root auf dem Host</strong>. Wer Zugriff auf den Socket hat, ist praktisch root-äquivalent
– unabhängig vom <code>:ro</code>. Für einen reinen Melder wie Diun ist das ein bewusst eingegangenes Risiko;
wer es entschärfen will, hängt Diun nicht direkt an den Socket, sondern an einen vorgeschalteten
<strong><a href="https://github.com/Tecnativa/docker-socket-proxy">docker-socket-proxy</a></strong>, der nur die wenigen
Endpunkte (Container auflisten, Images lesen) durchreicht, die Diun wirklich braucht.</div>
</div>
<p>Markiere die Container, die Diun im Auge behalten soll, per Label in <strong>deren</strong> <code>compose.yaml</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="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;diun.enable=true&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;diun.watch_repo=true&#34;</span></span></span></code></pre></div>
</div>
<p><code>diun.enable=true</code> schaltet die Überwachung ein. <code>diun.watch_repo=true</code> lässt Diun das <strong>ganze
Repository</strong> nach neueren Tags absuchen (z. B. ob zu deinem <code>postgres:18</code> schon ein <code>19</code>
existiert) – ohne dieses Label prüft Diun nur, ob dein <em>exakter</em> Tag ein neues Image bekommen
hat. Starte Diun und sieh beim ersten Lauf zu:</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 up -d <span class="o">&amp;&amp;</span> docker compose logs -f diun</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">INF Found 1 image(s) to analyze provider=docker
</span></span><span class="line"><span class="cl">INF New image found      image=docker.io/traefik/whoami:v1.11.0 provider=docker
</span></span><span class="line"><span class="cl">INF New image found      image=docker.io/traefik/whoami:v1.12.0 provider=docker
</span></span><span class="line"><span class="cl">INF New image found      image=docker.io/traefik/whoami:latest-arm64 provider=docker
</span></span><span class="line"><span class="cl">INF Jobs completed       added=97 failed=0 skipped=0 unchanged=0 updated=0</span></span></code></pre></div>
</div>
<p>Der <strong>erste</strong> Lauf ist nur die Bestandsaufnahme: Diun schreibt jeden gefundenen Tag als „New
image found&quot; in seine Datenbank (<code>added</code>). Dass für <strong>einen</strong> überwachten Container 97 Einträge
herauskommen, liegt an <code>watch_repo</code> – Diun holt dann das Manifest für <em>jeden</em> Tag des
Repositories, also auch für Architektur-Varianten wie <code>latest-arm64</code> oder <code>v1.10.0-armv7</code>.</p>
<p>Zwei Dinge folgen daraus, die man leicht falsch erwartet:</p>
<ul>
<li>
<p><strong>Der erste Lauf benachrichtigt nicht.</strong> Die Option <code>watch.firstCheckNotif</code> steht per Default
auf <code>false</code>, und das gilt pro Image-Tag. Alles, was Diun zum ersten Mal sieht, landet
stillschweigend in der Datenbank. Gemeldet wird erst, wenn sich später etwas <strong>ändert</strong>.</p>
</li>
<li>
<p><strong><code>watch_repo</code> kostet Registry-Anfragen.</strong> 97 Manifest-Abfragen pro Lauf reißen das
Docker-Hub-Limit ohne Login (100 Anfragen pro IPv4-Adresse bzw. IPv6-<code>/64</code>-Netz) fast
alleine. Beim zweiten Lauf desselben Tages lief unser Test genau dort hinein –
<code>StatusCode: 429</code> und <code>failed=36</code> im Log. Wie viel Budget noch übrig ist – und über welches
Zeitfenster die Registry gerade rechnet –, sagt sie dir selbst:</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">TOKEN</span><span class="o">=</span><span class="k">$(</span>curl -s <span class="s2">&#34;https://auth.docker.io/token?service=registry.docker.io&amp;scope=repository:ratelimitpreview/test:pull&#34;</span> <span class="p">|</span> cut -d<span class="s1">&#39;&#34;&#39;</span> -f4<span class="k">)</span>
</span></span><span class="line"><span class="cl">curl -sI -H <span class="s2">&#34;Authorization: Bearer </span><span class="nv">$TOKEN</span><span class="s2">&#34;</span> https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest <span class="p">|</span> grep -i <span class="s2">&#34;^ratelimit&#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">ratelimit-limit: 100;w=3600
</span></span><span class="line"><span class="cl">ratelimit-remaining: 98;w=3600</span></span></code></pre></div>
</div>
<p><code>w</code> ist die Fensterbreite in Sekunden. Docker <strong>dokumentiert</strong> ein Sechs-Stunden-Fenster;
unser Test-VPS bekam im August 2026 durchweg <code>w=3600</code>, also eine Stunde. Verlass dich
deshalb auf den Header statt auf eine gemerkte Zahl – und beachte, dass diese Abfrage
selbst eine Anfrage kostet. Grenz die Tags außerdem auf das ein, was dich wirklich
interessiert – echte Release-Tags:</p>
</li>
</ul>
<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">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;diun.enable=true&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;diun.watch_repo=true&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;diun.include_tags=^v1\\.1[12]\\.\\d+$$&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;diun.sort_tags=semver&#34;</span></span></span></code></pre></div>
</div>
<p><code>include_tags</code> ist ein regulärer Ausdruck. Zwei Fallstricke stecken in der Schreibweise: Die
Backslashes müssen im YAML <strong>doppelt</strong> stehen, und das <code>$</code> am Ende wird als <code>$$</code> geschrieben –
sonst versucht Compose, eine Umgebungsvariable einzusetzen. Mit diesem Label bleiben von den 97
Einträgen genau die beiden echten Release-Tags übrig:</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">INF Found 1 image(s) to analyze provider=docker
</span></span><span class="line"><span class="cl">INF New image found      image=docker.io/traefik/whoami:v1.11.0 provider=docker
</span></span><span class="line"><span class="cl">INF New image found      image=docker.io/traefik/whoami:v1.12.0 provider=docker
</span></span><span class="line"><span class="cl">INF Jobs completed       added=2 failed=0 skipped=0 unchanged=0 updated=0</span></span></code></pre></div>
</div>
<p>Damit die Meldung nicht nur im Log steht, hängst du eine <strong>Benachrichtigung</strong> dran. Diun kann
E-Mail, Telegram, ntfy, Gotify und viele weitere. Für Telegram – den Bot hast du vielleicht
schon im <a href="/tutorials/uptime-kuma-monitoring/">Uptime-Kuma-Tutorial</a> eingerichtet – reichen zwei
Zeilen im <code>environment</code>-Block:</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;DIUN_NOTIF_TELEGRAM_TOKEN=DEIN_BOT_TOKEN&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;DIUN_NOTIF_TELEGRAM_CHATIDS=DEINE_CHAT_ID&#34;</span></span></span></code></pre></div>
</div>
<p>Alle weiteren Kanäle stehen in der <a href="https://crazymax.dev/diun/config/notif/">Diun-Dokumentation</a>. So
sieht die Meldung dann aus – hier über
<a href="/tutorials/ntfy-push-benachrichtigungen/" data-track-event="Navigation|Tutorial-Link|/tutorials/ntfy-push-benachrichtigungen/ ← /tutorials/docker-stack-aktuell-halten/">ntfy</a> im Browser, sobald zu
einem deiner gepinnten Images eine neuere Version erscheint:</p>
<p><figure class="my-6"><img src="/tutorials/docker-stack-aktuell-halten/diun-ntfy-benachrichtigung_hu_d67ae1a519b2a0b3.webp" srcset="/tutorials/docker-stack-aktuell-halten/diun-ntfy-benachrichtigung_hu_8129696206d8b55a.webp 480w, /tutorials/docker-stack-aktuell-halten/diun-ntfy-benachrichtigung_hu_d67ae1a519b2a0b3.webp 768w, /tutorials/docker-stack-aktuell-halten/diun-ntfy-benachrichtigung_hu_44140d0b16dd867d.webp 1200w, /tutorials/docker-stack-aktuell-halten/diun-ntfy-benachrichtigung_hu_38eee30b194c873.webp 1920w" sizes="(min-width: 768px) 768px, 100vw"
    width="768" height="432"
    data-full="/tutorials/docker-stack-aktuell-halten/diun-ntfy-benachrichtigung_hu_4c9d8af228cf5fcf.webp"
    alt="Die ntfy-Weboberfläche zeigt eine Diun-Benachrichtigung mit dem Titel „docker.io/traefik/whoami:v1.12.0 is available&quot; und dem Hinweistext, dass der Tag über den Docker-Provider auf docker.io verfügbar ist" title="Diuns Update-Meldung: zum gepinnten whoami v1.11.0 ist v1.12.0 erschienen"
    loading="lazy" decoding="async" class="rounded-lg"><figcaption class="mt-2 text-sm text-center text-slate-500 italic">Diuns Update-Meldung: zum gepinnten whoami v1.11.0 ist v1.12.0 erschienen</figcaption></figure></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>Die Benachrichtigung testen, ohne wochenlang zu warten
  </p>
  <div class="prose-kitchen text-sm">Weil der erste Lauf absichtlich stumm bleibt, weiß man nach dem Einrichten nicht, ob der
Meldeweg überhaupt funktioniert – im schlimmsten Fall merkst du das erst, wenn du eine wichtige
Meldung verpasst hast. Setz deshalb <strong>einmalig</strong> <code>DIUN_WATCH_FIRSTCHECKNOTIF=true</code> in Diuns
<code>environment</code>, lösche <code>./data</code> und starte neu: Dann schickt Diun auch für die Bestandsaufnahme
Benachrichtigungen – genau eine pro gefundenem Tag. Kanal geprüft, Variable danach wieder
entfernen (sonst rauscht dir der nächste <code>watch_repo</code>-Lauf den Posteingang voll).</div>
</div>
<h3 id="schritt-3-ein-update-sicher-einspielen-die-routine">Schritt 3: Ein Update sicher einspielen (die Routine)</h3>
<p>Diun meldet ein Update – jetzt kommt die eigentliche Disziplin. <strong>Nie</strong> einfach blind ziehen.
Die feste Reihenfolge für jeden Dienst:</p>
<p><strong>1. Release-Notes lesen.</strong> Bei einem Major-Sprung (z. B. Nextcloud 34 → 35) ist das Pflicht:
Gibt es Breaking Changes, Migrationsschritte, entfernte Optionen? Zwei Minuten hier sparen
Stunden Fehlersuche.</p>
<p><strong>2. Vorher sichern.</strong> Ein Update ist der klassische Moment, an dem etwas kaputtgeht. Mach ein
<a href="/tutorials/backups-mit-restic/">Restic-Backup</a> der Daten (bei großen Sprüngen zusätzlich einen
<a href="/tutorials/netcup-snapshots-scp/">netcup-Snapshot</a>). Dann ist ein Fehlschlag nur ein Rücksetzer,
kein Datenverlust.</p>
<p><strong>3. Tag anheben und ziehen.</strong> Setz den neuen Tag in der <code>compose.yaml</code> und hol das Image:</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 pull</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"> Image traefik/whoami:v1.12.0 Pulled</span></span></code></pre></div>
</div>
<p><strong>4. Neu starten.</strong> Compose ersetzt nur den betroffenen Container, die Volumes bleiben:</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 up -d</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"> Container diun-demo-whoami Recreated
</span></span><span class="line"><span class="cl"> Container diun-demo-whoami Started</span></span></code></pre></div>
</div>
<p><strong>5. Prüfen.</strong> Läuft der Container sauber (<code>docker compose ps</code>, <code>docker compose logs</code>), und tut
die App noch, was sie soll? Zustandsbehaftete Apps führen jetzt evtl. eine Datenbank-Migration
aus – wirf einen Blick ins Log, bis sie durch ist.</p>
<p><strong>6. Rollback im Kopf haben.</strong> Geht etwas schief, setzt du den Tag in der <code>compose.yaml</code> zurück
auf die alte Version und machst erneut <code>docker compose up -d</code>. Weil deine Daten im <strong>Volume</strong>
liegen (nicht im Container), ist das bei den meisten Apps ein sauberer Rücksprung. Nur wenn die
neue Version die <strong>Datenbank schon migriert</strong> hat, hilft kein Tag-Rollback mehr – dann brauchst
du das Backup aus Schritt 2. Genau dafür ist es da.</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>Eine App nach der anderen
  </p>
  <div class="prose-kitchen text-sm">Aktualisiere nicht den ganzen Stack auf einmal. Geh Dienst für Dienst vor und prüfe jeweils, ob
alles läuft, bevor du zum nächsten gehst. Bricht dann etwas, weißt du sofort, welches Update
schuld war.</div>
</div>
<h3 id="schritt-4-die-basis-nicht-vergessen--host-engine-versteckte-images">Schritt 4: Die Basis nicht vergessen – Host, Engine, versteckte Images</h3>
<p>Deine Apps sind nur die halbe Miete. Genauso wichtig:</p>
<ul>
<li>
<p><strong>Betriebssystem &amp; Docker-Engine.</strong> Die OS- und Sicherheitspakete von Debian spielst du am besten
<a href="/tutorials/automatische-updates-unattended-upgrades/">automatisch mit unattended-upgrades</a> ein.
Die <strong>Docker-Engine bleibt dabei bewusst außen vor</strong>: unattended-upgrades zieht per Default nur die
Debian-Quellen (<code>origin=Debian</code>), <strong>nicht</strong> das Docker-APT-Repo (<code>origin=Docker</code>,
<code>download.docker.com</code>). Die Engine wird also nicht im Hintergrund aktualisiert und startet dir nachts
nicht unerwartet alle Container neu – denn ein Engine-Update reißt laufende Container <strong>kurz mit</strong>.
Diesen Moment bestimmst du besser selbst und aktualisierst die Engine gezielt von Hand, wenn es passt:</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">sudo apt update <span class="o">&amp;&amp;</span> sudo apt upgrade docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin</span></span></code></pre></div>
</div>
<p>Prüfe danach mit <code>docker compose ps</code>, dass alle Stacks wieder oben sind.</p>
<p>Ist dir ein kurzer Container-Neustart in der Nacht egal, kannst du die Engine auch <strong>mit</strong>
unattended-upgrades mitziehen lassen: Erlaube in <code>/etc/apt/apt.conf.d/50unattended-upgrades</code> das
Docker-Repo als zusätzliches Origins-Pattern. Wichtig ist <code>archive=</code> – das Docker-Repo setzt kein
<code>Codename</code>-Feld, das oft empfohlene <code>codename=${distro_codename}</code> würde ins Leere laufen und Docker
bliebe trotz Eintrag außen vor:</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">&#34;origin=Docker,archive=${distro_codename}&#34;;</span></span></code></pre></div>
</div>
</li>
<li>
<p><strong>Versteckte Images.</strong> In vielen Stacks stecken Datenbanken, Caches und Hilfsdienste
(<code>postgres</code>, <code>redis</code>, <code>mariadb</code>, <code>gotenberg</code> …), an die niemand denkt. Diun überwacht sie
automatisch mit, sobald der jeweilige Container das <code>diun.enable</code>-Label trägt – gib es also
<strong>allen</strong> langlebigen Diensten, nicht nur der sichtbaren Haupt-App.</p>
</li>
<li>
<p><strong>Erweiterungen deines Reverse Proxys.</strong> Auch gepinnte Plugin-Versionen (etwa der
<a href="/tutorials/crowdsec-einrichten/">CrowdSec-Bouncer</a>) oder Traefik selbst wollen bewusst
nachgezogen werden.</p>
</li>
</ul>
<h3 id="schritt-5-aufräumen--speicher-zurückholen">Schritt 5: Aufräumen – Speicher zurückholen</h3>
<p>Jedes Update lässt das <strong>alte</strong> Image liegen. Nach ein paar Monaten summiert sich das. Was du
verbrauchst, zeigt:</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 system df</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">TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
</span></span><span class="line"><span class="cl">Images          26        4         14.75GB   14.16GB (95%)
</span></span><span class="line"><span class="cl">Containers      4         4         45.06kB   0B (0%)
</span></span><span class="line"><span class="cl">Local Volumes   0         0         0B        0B
</span></span><span class="line"><span class="cl">Build Cache     36        0         828.2MB   828.2MB</span></span></code></pre></div>
</div>
<p>Ungenutzte, <strong>unbenannte</strong> („dangling&quot;) Images entfernt der sichere Standardbefehl:</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 image prune</span></span></code></pre></div>
</div>
<p>Achtung: Das entfernt nur dangling Images. Eine <strong>alte, noch getaggte</strong> Version (wie
<code>whoami:v1.11.0</code> nach dem Update auf <code>v1.12.0</code>) gilt nicht als dangling und bleibt liegen. Die
holst du dir gezielt zurück:</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 rmi traefik/whoami:v1.11.0</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">Untagged: traefik/whoami:v1.11.0
</span></span><span class="line"><span class="cl">Deleted: sha256:200689790a0a0ea48ca45992e0450bc26ccab5307375b41c84dfc4f2475937ab</span></span></code></pre></div>
</div>
<p>Wer radikaler aufräumen will, nimmt <code>docker image prune -a</code> (entfernt <strong>alle</strong> Images, die
gerade kein laufender Container nutzt). Das ist mächtig, zieht aber beim nächsten Start alles
neu – setz es bewusst ein, nicht im Cron.</p>
<p>Das sichere <code>docker image prune -f</code> (nur dangling) kannst du dagegen bedenkenlos regelmäßig
laufen lassen – etwa wöchentlich per systemd-Timer oder Cron. So bleibt zwischen deinen
Update-Terminals gar nicht erst Müll liegen, und du gewinnst den Speicher automatisch zurück.</p>
<h3 id="schritt-6-auto-update--wann-es-okay-ist-und-wann-nicht">Schritt 6: Auto-Update – wann es okay ist (und wann nicht)</h3>
<p>Bleibt die Frage: Warum nicht alles vollautomatisch? Das bekannteste Werkzeug dafür ist
<strong>Watchtower</strong> – das zieht neue Images und startet Container von selbst neu. Zwei Haken:</p>
<ul>
<li><strong>Der Upstream ist verwaist.</strong> Das offizielle <code>containrrr/watchtower</code> hat seit <strong>v1.7.1
(11.11.2023)</strong> kein Release mehr bekommen – und das Repository wurde <strong>Ende 2025 (17.12.2025)
archiviert</strong>, also endgültig eingemottet. Wer es nutzt, greift besser zu einem gepflegten
Fork wie <code>nickfedor/watchtower</code>.</li>
<li><strong>Auto-Update ist für zustandsbehaftete Apps gefährlich.</strong> Eine Datenbank oder Nextcloud
nachts unbeaufsichtigt über einen Major-Sprung zu heben, ist das Gegenteil von „sicher&quot;.</li>
</ul>
<p>Sinnvoll ist Auto-Update höchstens für <strong>unkritische, zustandslose</strong> Dienste – und selbst dann
lieber im <strong>Monitor-Modus</strong> (<code>WATCHTOWER_MONITOR_ONLY=true</code>), der nur meldet statt aktualisiert.
Für alles andere schlägt die bewusste Routine aus Schritt 3 jedes Automatik-Werkzeug. Genau
deshalb ist <strong>Diun (melden) + manuelles Update</strong> hier die Empfehlung, nicht Watchtower
(handeln).</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>Sichere Automatisierung für Git-Nutzer: Renovate
  </p>
  <div class="prose-kitchen text-sm">Wenn deine <code>compose.yaml</code>-Dateien in einem Git-Repo liegen, gibt es einen dritten Weg, der
Automatik und Kontrolle verbindet: <strong>Renovate</strong> (oder Dependabot) erkennt die gepinnten
<code>image:</code>-Tags und öffnet automatisch einen <strong>Pull Request</strong>, der den Tag anhebt – mitsamt Link
zu den Release-Notes. Das Update passiert also nicht heimlich am Server, sondern als <strong>Änderung,
die du prüfst und mergst</strong>, bevor du sie mit <code>docker compose up -d</code> ausrollst. So bleibt der
sichere „bewusst einspielen&quot;-Schritt erhalten, nur das lästige Nachschauen entfällt.</div>
</div>
<h2 id="wenn-es-nicht-funktioniert">Wenn es nicht funktioniert</h2>
<div class="troubleshoot not-prose">
<p><strong><code>docker compose pull</code> zieht kein neues Image, obwohl es eine neue Version gibt.</strong> Dein Tag zeigt
auf eine feste Version (<code>:1.37.1</code>) oder auf einen Major-Tag (<code>:18</code>), unter dem es nur ein neues
<em>Major</em> gäbe. <code>pull</code> holt nur, was <strong>derselbe Tag</strong> inzwischen zeigt. Für einen Versionssprung
musst du den Tag in der <code>compose.yaml</code> selbst anheben.</p>
<p><strong>Die App startet nach dem Update nicht mehr oder wirft Datenbank-Fehler.</strong> Meist ein Breaking
Change oder eine fehlgeschlagene Migration. Prüfe <code>docker compose logs</code>, gleiche mit den
Release-Notes ab. Tag auf die alte Version zurücksetzen und <code>up -d</code>; hat die neue Version die Daten
schon migriert, das Backup aus Schritt 3 einspielen.</p>
<p><strong>Diun meldet nichts, obwohl Updates existieren.</strong> Prüfe, dass die Container das Label
<code>diun.enable=true</code> tragen und Diun den Socket lesen kann (<code>DIUN_PROVIDERS_DOCKER=true</code>, Socket
gemountet). Für neuere <strong>Versions-Tags</strong> braucht der Container zusätzlich <code>diun.watch_repo=true</code> –
ohne das sieht Diun nur Änderungen am exakt gepinnten Tag. Und: Was Diun beim <strong>ersten</strong> Mal
sieht, wird nur in die Datenbank geschrieben, nicht gemeldet (<code>watch.firstCheckNotif</code> ist
<code>false</code>) – zum Testen des Meldewegs einmalig <code>DIUN_WATCH_FIRSTCHECKNOTIF=true</code> setzen.</p>
<p><strong>Die Festplatte läuft voll, obwohl du regelmäßig <code>docker image prune</code> machst.</strong> <code>docker image prune</code> räumt nur dangling Images ab, nicht die alten <strong>getaggten</strong> Versionen. Entferne sie gezielt
mit <code>docker rmi &lt;image&gt;:&lt;tag&gt;</code> oder – mit Bedacht – <code>docker image prune -a</code>. Auch der Build-Cache
(<code>docker builder prune</code>) kann wachsen.</p>
<p><strong>Beim Ziehen kommt <code>toomanyrequests</code> / ein Docker-Hub-Rate-Limit.</strong> <code>diun.watch_repo=true</code> auf
vielen Images fragt viele Tags ab. Setz das Watch-Intervall seltener (z. B. einmal täglich) und
grenze mit <code>diun.include_tags</code> auf relevante Versionen ein, statt das ganze Repo abzuklopfen.</p>

</div>

<h2 id="wartung--backups">Wartung &amp; Backups</h2>
<ul>
<li><strong>Der Rhythmus.</strong> Diun meldet → du liest die Release-Notes → Backup → Update → prüfen. In der
Praxis ist das <strong>einmal im Monat</strong> ein überschaubarer Termin; kritische Sicherheitslücken
ziehst du sofort nach. Ehrlich: Ganz ohne Handarbeit geht sicheres Selfhosting nicht – aber
15 Minuten im Monat sind der Deal.</li>
<li><strong>Schnelldreher zuerst.</strong> Manche Projekte veröffentlichen häufig und mit Breaking Changes –
<a href="/tutorials/immich-fotos-selbst-hosten/">Immich</a>, mailcow, <a href="/tutorials/crowdsec-einrichten/">CrowdSec</a>,
<a href="/tutorials/monitoring-grafana-prometheus/">Grafana</a> und <a href="/tutorials/nextcloud-eigene-cloud/">Nextcloud</a>.
Die schaust du dir zuerst und öfter an, ruhige Kandidaten (Datenbanken, Redis) seltener.</li>
<li><strong>Backups sind der Kern dieses Rezepts.</strong> Ein Update ohne Backup ist ein Vabanquespiel; mit
<a href="/tutorials/backups-mit-restic/">Restic</a> im Rücken wird jedes Update entspannt. Vor großen
Sprüngen zusätzlich einen <a href="/tutorials/netcup-snapshots-scp/">Snapshot</a> – der holt den ganzen
Server in einem Rutsch zurück.</li>
<li><strong>Versioniere deine <code>compose.yaml</code>-Dateien.</strong> Wer seine Compose-Dateien in einem Git-Repo
hält, kann nicht nur Daten, sondern auch die <strong>Konfiguration</strong> auf jeden früheren Stand
zurückrollen – und sieht in der Historie genau, welcher Tag wann angehoben wurde.</li>
<li><strong>Halte auch die Werkzeuge aktuell.</strong> Diun selbst und deine Reverse-Proxy-Plugins gehören mit
in die Update-Runde – sonst pflegt sich der Pfleger nicht.</li>
</ul>
]]></content:encoded></item></channel></rss>