<?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>selfhosted on private Homepage von Rainer Rose</title>
    <link>https://www.rainerrose.de/tags/selfhosted/</link>
    <description>Recent content in selfhosted on private Homepage von Rainer Rose</description>
    <generator>Hugo</generator>
    <language>de</language>
    <copyright>Copyright © 1998-2026 Rainer Rose. All Rights Reserved.
</copyright>
    <lastBuildDate>Sun, 22 Feb 2026 18:21:03 +0100</lastBuildDate><atom:link href="https://www.rainerrose.de/tags/selfhosted/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Paperless-ngx: Major-Upgrade oder Wechsel der Datenbank in Docker</title>
      <link>https://www.rainerrose.de/docs/howto/paperless-postgres-mariadb-upgrades/</link>
      <pubDate>Sun, 22 Feb 2026 18:21:03 +0100</pubDate>
      <guid>https://www.rainerrose.de/docs/howto/paperless-postgres-mariadb-upgrades/</guid>
      <description><![CDATA[<!-- Anreißer -->
<p>Durch Freunde wurde ich auf das Document Management System <code>paperless</code> aufmerksam, dass im derzeit aktuellen Fork <a href="https://tools.rainerrose.de/redirect.php?id=56" target="_blank" rel="noopener noreferrer">paperless-ngx<i class="fas fa-external-link-square-alt ms-1"></i></a> weiterlebt.
In Verbindung mit einem Einzugsscanner ist es ein wahr gewordener Traum, der mich sehr in meinem Alltag entlastet.</p>
<p>Das Aktualisieren von <em>paperless-ngx</em> (im Folgenden nur noch <em>paperless</em> genannt) unter <em>Docker</em> ist zwar in der <a href="https://tools.rainerrose.de/redirect.php?id=57" target="_blank" rel="noopener noreferrer">Dokumentation<i class="fas fa-external-link-square-alt ms-1"></i></a> recht gut beschrieben, aber das Updaten der Datenbank, finde ich zu kompliziert, weil es nur auf die offizielle <em>PostgreSQL</em>-Dokumentation verweist.</p>
<p>Hier müsste ich mit den CLI-Tools wie <code>pg_dumpall</code> und <code>pg_upgrade</code>  hantieren, was arbeiten im <em>Docker</em>-Container notwendig macht und vermutlich auch doppelte <em>Docker</em>-Container-Instanzen nötig macht. Eine automagische Migration unterstützen die <em>postgres</em>-images nach meinem Wissen derzeit nicht. Also habe ich eine andere Möglichkeit gesucht und auch gefunden!</p>]]></description>
      <content:encoded><![CDATA[<!-- Anreißer -->
<p>Durch Freunde wurde ich auf das Document Management System <code>paperless</code> aufmerksam, dass im derzeit aktuellen Fork <a href="https://tools.rainerrose.de/redirect.php?id=56" target="_blank" rel="noopener noreferrer">paperless-ngx<i class="fas fa-external-link-square-alt ms-1"></i></a> weiterlebt.
In Verbindung mit einem Einzugsscanner ist es ein wahr gewordener Traum, der mich sehr in meinem Alltag entlastet.</p>
<p>Das Aktualisieren von <em>paperless-ngx</em> (im Folgenden nur noch <em>paperless</em> genannt) unter <em>Docker</em> ist zwar in der <a href="https://tools.rainerrose.de/redirect.php?id=57" target="_blank" rel="noopener noreferrer">Dokumentation<i class="fas fa-external-link-square-alt ms-1"></i></a> recht gut beschrieben, aber das Updaten der Datenbank, finde ich zu kompliziert, weil es nur auf die offizielle <em>PostgreSQL</em>-Dokumentation verweist.</p>
<p>Hier müsste ich mit den CLI-Tools wie <code>pg_dumpall</code> und <code>pg_upgrade</code>  hantieren, was arbeiten im <em>Docker</em>-Container notwendig macht und vermutlich auch doppelte <em>Docker</em>-Container-Instanzen nötig macht. Eine automagische Migration unterstützen die <em>postgres</em>-images nach meinem Wissen derzeit nicht. Also habe ich eine andere Möglichkeit gesucht und auch gefunden!</p>
<p><code>paperless</code> liefert die beiden Tools <code>document_exporter</code> und <code>document_importer</code> mit, die ein <strong>vollständiges</strong> Backup und Wiederherstellung (Restore) der kompletten Instanz bereitstellt.</p>
<p>Mit diesen beiden Tools lässt sich ein Major-Upgrade bewerkstelligen. Wie ich heute rausfand ist sogar der Wechsel der Datenbank-Engine ohne allzu große Umstände möglich.</p>

<h2 id="überblick" data-numberify>Überblick<a class="anchor ms-1" href="#überblick"></a></h2>
<p>Der grobe Pfad für ein Major-Upgrade ist daher:</p>
<ul>
<li>Zuerst ein vollständiges Backup anfertigen.</li>
<li>Alle Daten inklusive Volumes wegwerfen.</li>
<li>Die <em>PostgreSQL</em>-Version im <code>docker-compose.yml</code> anpassen.</li>
<li>Schlussendlich alle Daten wieder importieren.</li>
</ul>
<p>Das hat bisher anstandslos geklappt.</p>
<p>Der Wechsel auf eine andere Datenbank-Engine wird weiter unten beschrieben.</p>

<h2 id="voraussetzungen" data-numberify>Voraussetzungen<a class="anchor ms-1" href="#voraussetzungen"></a></h2>
<p>Es setzt voraus, dass die Daten außerhalb der Docker-Umgebung landen. Das <code>export</code> Volume ist also als Verzeichnis unterhalb der <code>docker-compose.yml</code> so wie in den <a href="https://tools.rainerrose.de/redirect.php?id=58" target="_blank" rel="noopener noreferrer">Beispielen<i class="fas fa-external-link-square-alt ms-1"></i></a> definiert.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="w">      </span>- <span class="l">./export:/usr/src/paperless/export</span><span class="w">
</span></span></span></code></pre></div><p>Wie ich ein Datenbank-Upgrade im Detail durchführe, beschreibe ich im Folgenden.</p>
<p>Zuerst sollte sichergestellt sein, dass keine Importe ins <em>Paperless</em> stattfinden. Da ich alleine auf dem System arbeite und keinen E-Mail-Import verwende, kann ich das gut sicherstellen.</p>

<h2 id="backup" data-numberify>Backup<a class="anchor ms-1" href="#backup"></a></h2>
<p>Als ersten Schritt fertige ich ein Backup an. Dies geht wunderbar mit dem bereits erwähnten <code>document_exporter</code> folgendermaßen:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">docker compose <span class="nb">exec</span> -T webserver document_exporter ../export -z -zn full-backup
</span></span></code></pre></div><p>Hier sollte dann eine Datei namens <code>full-backup.zip</code> außerhalb des Docker-Umgebung entstanden sein. Dies enthält alle relevanten Informationen für ein Desaster-Recovery.</p>

<blockquote class="alert alert-success" role="alert">
    <p class="alert-heading fw-bold">
      <i class="fas fa-lightbulb me-2"></i>Tipp
    </p>
    <p>Der zusätzliche Parameter <code>--no-progress-bar</code> sorgt für weniger Ausgaben in Backup-Scripten.</p>
</blockquote>

<h2 id="zurück-auf-null" data-numberify>Zurück auf Null<a class="anchor ms-1" href="#zurück-auf-null"></a></h2>
<p>Danach fahre ich alle Container herunter und lösche gleichzeitig alle <em>Docker</em> Volumes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">docker compose down --volumes
</span></span></code></pre></div><p>Die Ausgabe sollte ungefähr so aussehen:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="ln">1</span><span class="cl">[+] down 8/8
</span></span><span class="line"><span class="ln">2</span><span class="cl"> ✔ Container paperless-webserver-1 Removed   5.6s
</span></span><span class="line"><span class="ln">3</span><span class="cl"> ✔ Container paperless-broker-1    Removed   0.4s
</span></span><span class="line"><span class="ln">4</span><span class="cl"> ✔ Container paperless-db-1        Removed   0.3s
</span></span><span class="line"><span class="ln">5</span><span class="cl"> ✔ Volume paperless_media          Removed   0.2s
</span></span><span class="line"><span class="ln">6</span><span class="cl"> ✔ Volume paperless_pgdata         Removed   0.0s
</span></span><span class="line"><span class="ln">7</span><span class="cl"> ✔ Volume paperless_data           Removed   0.0s
</span></span><span class="line"><span class="ln">8</span><span class="cl"> ✔ Volume paperless_redisdata      Removed   0.0s
</span></span><span class="line"><span class="ln">9</span><span class="cl"> ✔ Network paperless_default       Removed                     
</span></span></code></pre></div>
<h2 id="neue-version-wählen" data-numberify>Neue Version wählen<a class="anchor ms-1" href="#neue-version-wählen"></a></h2>
<p>Nun kann ich in der <code>docker-compose.yml</code>-Datei das Datenbank-Image auf die gewünschte Version der Datenbank hochziehen.</p>
<p>Für PostgreSQL gibt es <a href="https://tools.rainerrose.de/redirect.php?id=59" target="_blank" rel="noopener noreferrer">hier im Repo eine Vorlage<i class="fas fa-external-link-square-alt ms-1"></i></a> an der ich mich orientiert habe. Im Mai 2025 war es die Version 17.
Also änderte ich den Eintrag <code>docker-compose.yml</code> von der Version 16.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="w">  </span><span class="nt">db</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">docker.io/library/postgres:16</span><span class="w">
</span></span></span></code></pre></div><p>auf die Version 17</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="w">  </span><span class="nt">db</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">docker.io/library/postgres:17</span><span class="w">
</span></span></span></code></pre></div><p>Es empfiehlt sich eh ab und zu da mal einzuschauen, ob nicht z.B. eine neue redis-Version unterstützt wird.
Ich selbst stütze mich nur auf die Haupt (Major-Versionen). Bei Minor- oder Patch-Updates pulle ich das jeweilige neue Image und starte alle Docker-Container neu, das reicht mir und hat sich bei mir bewährt.</p>

<blockquote class="alert alert-warning" role="alert">
    <p class="alert-heading fw-bold">
      <i class="fas fa-exclamation-circle me-2"></i>Hinweis
    </p>
    <p>Aktuell ist im Repo die <em>PostgreSQL</em>-Version 18 angegeben. Leider wirft der Docker-Container <code>db-1</code> einiges an Fehlermeldungen und startet sich immer wieder neu. Eine Lösung dazu beschreibe ich weiter unten; hier soll es jetzt erst einmal mit dem Upgrade von 16 auf 17 weiter gehen; schließlich hat das Update von 15 auf 16 vor einiger Zeit genauso geklappt.</p>
</blockquote>

<h2 id="erster-start" data-numberify>Erster Start<a class="anchor ms-1" href="#erster-start"></a></h2>
<p>Nach dem Ändern der Major-Version (bzw. des Tags) vom <em>postgres</em>-Image, können die Docker-Container von <em>paperless</em> schon wieder gestartet werden. Sofern das Image lokal noch nicht vorliegt, wird es automatisch heruntergeladen.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">docker compose up -d
</span></span></code></pre></div><p>Folgendes sollte als Ausgabe zu sehen sein:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="ln"> 1</span><span class="cl">[+] up 22/22
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"> ✔ Image docker.io/library/postgres:18 Pulled     15.3s
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> ✔ Network paperless_default           Created    0.0s
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> ✔ Volume paperless_data               Created    0.0s
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> ✔ Volume paperless_media              Created    0.0s
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> ✔ Volume paperless_redisdata          Created    0.0s
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> ✔ Volume paperless_pgdata             Created    0.0s
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> ✔ Container paperless-db-1            Created    0.2s
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> ✔ Container paperless-broker-1        Created    0.2s
</span></span><span class="line"><span class="ln">10</span><span class="cl"> ✔ Container paperless-webserver-1     Created   
</span></span></code></pre></div><p>Nun muss solange gewartet, bis die Login-Maske wieder mit dem Browser besuchbar ist.</p>
<p>Da dies einer Neuinstallation entspricht und daher Datenbanken etc. eingerichtet werden müssen, dauert dies je nach Rechenpower etwas. Daher ist etwas Geduld erforderlich.</p>
<p>Zeigt&rsquo;s im Browser <code>502 Bad Gateway</code> ist noch nicht alles fertig.  Neugierige werfen ein Blick in die Docker-Logs</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">docker compose logs -f
</span></span></code></pre></div><p>Wenn ungefähr folgende Log-Einträge zu sehen sind</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">webserver_1</span>  <span class="o">|</span> <span class="p">[</span><span class="n">tasks</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">webserver_1</span>  <span class="o">|</span>   <span class="o">.</span> <span class="n">documents</span><span class="o">.</span><span class="n">bulk_edit</span><span class="o">.</span><span class="n">delete</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">webserver_1</span>  <span class="o">|</span>   <span class="o">.</span> <span class="n">documents</span><span class="o">.</span><span class="n">signals</span><span class="o">.</span><span class="n">handlers</span><span class="o">.</span><span class="n">send_webhook</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">webserver_1</span>  <span class="o">|</span>   <span class="o">.</span> <span class="n">documents</span><span class="o">.</span><span class="n">tasks</span><span class="o">.</span><span class="n">bulk_update_documents</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">webserver_1</span>  <span class="o">|</span>   <span class="o">.</span> <span class="n">documents</span><span class="o">.</span><span class="n">tasks</span><span class="o">.</span><span class="n">check_scheduled_workflows</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">webserver_1</span>  <span class="o">|</span>   <span class="o">.</span> <span class="n">documents</span><span class="o">.</span><span class="n">tasks</span><span class="o">.</span><span class="n">consume_file</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">webserver_1</span>  <span class="o">|</span>   <span class="o">.</span> <span class="n">documents</span><span class="o">.</span><span class="n">tasks</span><span class="o">.</span><span class="n">empty_trash</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">webserver_1</span>  <span class="o">|</span>   <span class="o">.</span> <span class="n">documents</span><span class="o">.</span><span class="n">tasks</span><span class="o">.</span><span class="n">index_optimize</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">webserver_1</span>  <span class="o">|</span>   <span class="o">.</span> <span class="n">documents</span><span class="o">.</span><span class="n">tasks</span><span class="o">.</span><span class="n">sanity_check</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="n">webserver_1</span>  <span class="o">|</span>   <span class="o">.</span> <span class="n">documents</span><span class="o">.</span><span class="n">tasks</span><span class="o">.</span><span class="n">train_classifier</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="n">webserver_1</span>  <span class="o">|</span>   <span class="o">.</span> <span class="n">documents</span><span class="o">.</span><span class="n">tasks</span><span class="o">.</span><span class="n">update_document_content_maybe_archive_file</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="n">webserver_1</span>  <span class="o">|</span>   <span class="o">.</span> <span class="n">paperless_mail</span><span class="o">.</span><span class="n">mail</span><span class="o">.</span><span class="n">apply_mail_action</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="n">webserver_1</span>  <span class="o">|</span>   <span class="o">.</span> <span class="n">paperless_mail</span><span class="o">.</span><span class="n">mail</span><span class="o">.</span><span class="n">error_callback</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="n">webserver_1</span>  <span class="o">|</span>   <span class="o">.</span> <span class="n">paperless_mail</span><span class="o">.</span><span class="n">tasks</span><span class="o">.</span><span class="n">process_mail_accounts</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="n">webserver_1</span>  <span class="o">|</span> 
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="n">webserver_1</span>  <span class="o">|</span> <span class="p">[</span><span class="mi">2025</span><span class="o">-</span><span class="mi">05</span><span class="o">-</span><span class="mi">29</span> <span class="mi">12</span><span class="p">:</span><span class="mi">50</span><span class="p">:</span><span class="mi">03</span><span class="p">,</span><span class="mi">792</span><span class="p">]</span> <span class="p">[</span><span class="n">INFO</span><span class="p">]</span> <span class="p">[</span><span class="n">celery</span><span class="o">.</span><span class="n">worker</span><span class="o">.</span><span class="n">consumer</span><span class="o">.</span><span class="n">connection</span><span class="p">]</span> <span class="n">Connected</span> <span class="n">to</span> <span class="n">redis</span><span class="p">:</span><span class="o">//</span><span class="n">broker</span><span class="p">:</span><span class="mi">6379</span><span class="o">//</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="n">webserver_1</span>  <span class="o">|</span> <span class="p">[</span><span class="mi">2025</span><span class="o">-</span><span class="mi">05</span><span class="o">-</span><span class="mi">29</span> <span class="mi">12</span><span class="p">:</span><span class="mi">50</span><span class="p">:</span><span class="mi">03</span><span class="p">,</span><span class="mi">982</span><span class="p">]</span> <span class="p">[</span><span class="n">INFO</span><span class="p">]</span> <span class="p">[</span><span class="n">celery</span><span class="o">.</span><span class="n">apps</span><span class="o">.</span><span class="n">worker</span><span class="p">]</span> <span class="n">celery</span><span class="err">@</span><span class="mf">7e94</span><span class="n">fdb4f941</span> <span class="n">ready</span><span class="o">.</span>
</span></span></code></pre></div><p>sollte die Login-Maske zur Einrichtung des ersten Benutzerkontos verfügbar sein und es kann mit dem nächsten Schritt weiter gemacht werden.</p>
<p><picture><img class="img-fluid " alt="Paperless-ngx Anmelde-Maske zur Einrichtung des ersten Benutzerkontos" src="https://www.rainerrose.de/images/docs/paperless-login-initial.png?v=960c44cf92448b0054d81f613282f43c" loading="lazy" width="405" height="716" />
</picture>

</p>

<h2 id="daten-wieder-importieren" data-numberify>Daten wieder importieren<a class="anchor ms-1" href="#daten-wieder-importieren"></a></h2>
<p>Über den <code>document_importer</code> lässt sich nun wieder alles importieren: Alle Dokumente, alle Tags, alle Benutzeraccounts, gespeicherten Ansichten usw.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">docker compose <span class="nb">exec</span> -T webserver document_importer ../export/full-backup.zip
</span></span></code></pre></div><p>Ausgabe:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="ln">1</span><span class="cl">Checking the manifest
</span></span><span class="line"><span class="ln">2</span><span class="cl">Installed 4248 object(s) from 1 fixture(s)
</span></span><span class="line"><span class="ln">3</span><span class="cl">Copy files into paperless...
</span></span><span class="line"><span class="ln">4</span><span class="cl">100%|██████████| 1014/1014 [00:04&lt;00:00, 210.59it/s]
</span></span><span class="line"><span class="ln">5</span><span class="cl">Updating search index...
</span></span><span class="line"><span class="ln">6</span><span class="cl">100%|██████████| 1014/1014 [00:10&lt;00:00, 94.50it/s] 
</span></span></code></pre></div><p>Falls im Browser Weiterleitung auf den Unterpfad <code>/accounts/signup/?next=%2F</code> stattfand, diesen Pfad löschen!
Im Browser also wieder die gewohnte URL ansurfen und es sollte wieder die gewohnte Login-Maske erscheinen.</p>

<h2 id="fertig-anmelden" data-numberify>Fertig, anmelden<a class="anchor ms-1" href="#fertig-anmelden"></a></h2>
<p>Schlussendlich stoße ich noch einmal vorsichtshalber mein Backup-Script von Paperless noch an und bin fertig.</p>

<h2 id="postgresql-18" data-numberify>PostgreSQL 18<a class="anchor ms-1" href="#postgresql-18"></a></h2>
<p>Während ich diesen Artikel schrieb, stellte ich fest, dass in den <a href="https://tools.rainerrose.de/redirect.php?id=60" target="_blank" rel="noopener noreferrer">Vorlagen im Repo<i class="fas fa-external-link-square-alt ms-1"></i></a> zu <code>docker-compose.yml</code> schon <em>PostgreSQL</em> 18 unterstützt wird.
Ich bin daher genau nach meiner Anleitung vorgegangen und musste feststellen, dass der Datenbank-Container nicht nach oben kam.</p>
<p>In den Docker-Logs entdeckte ich dann folgende Meldungen:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span> <span class="n">Error</span><span class="p">:</span> <span class="ow">in</span> <span class="mi">18</span><span class="o">+</span><span class="p">,</span> <span class="n">these</span> <span class="n">Docker</span> <span class="n">images</span> <span class="n">are</span> <span class="n">configured</span> <span class="n">to</span> <span class="n">store</span> <span class="n">database</span> <span class="n">data</span> <span class="ow">in</span> <span class="n">a</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span>        <span class="n">format</span> <span class="n">which</span> <span class="n">is</span> <span class="n">compatible</span> <span class="n">with</span> <span class="s2">&#34;pg_ctlcluster&#34;</span> <span class="p">(</span><span class="n">specifically</span><span class="p">,</span> <span class="n">using</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span>        <span class="n">major</span><span class="o">-</span><span class="n">version</span><span class="o">-</span><span class="n">specific</span> <span class="n">directory</span> <span class="n">names</span><span class="p">)</span><span class="o">.</span>  <span class="n">This</span> <span class="n">better</span> <span class="n">reflects</span> <span class="n">how</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span>        <span class="n">PostgreSQL</span> <span class="n">itself</span> <span class="n">works</span><span class="p">,</span> <span class="ow">and</span> <span class="n">how</span> <span class="n">upgrades</span> <span class="n">are</span> <span class="n">to</span> <span class="n">be</span> <span class="n">performed</span><span class="o">.</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span> 
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span>        <span class="n">See</span> <span class="n">also</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">docker</span><span class="o">-</span><span class="n">library</span><span class="o">/</span><span class="n">postgres</span><span class="o">/</span><span class="n">pull</span><span class="o">/</span><span class="mi">1259</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span> 
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span>        <span class="n">Counter</span> <span class="n">to</span> <span class="n">that</span><span class="p">,</span> <span class="n">there</span> <span class="n">appears</span> <span class="n">to</span> <span class="n">be</span> <span class="n">PostgreSQL</span> <span class="n">data</span> <span class="ow">in</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span>          <span class="o">/</span><span class="k">var</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">postgresql</span><span class="o">/</span><span class="n">data</span> <span class="p">(</span><span class="n">unused</span> <span class="n">mount</span><span class="o">/</span><span class="n">volume</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span> 
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span>        <span class="n">This</span> <span class="n">is</span> <span class="n">usually</span> <span class="n">the</span> <span class="n">result</span> <span class="n">of</span> <span class="n">upgrading</span> <span class="n">the</span> <span class="n">Docker</span> <span class="n">image</span> <span class="n">without</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span>        <span class="n">upgrading</span> <span class="n">the</span> <span class="n">underlying</span> <span class="n">database</span> <span class="n">using</span> <span class="s2">&#34;pg_upgrade&#34;</span> <span class="p">(</span><span class="n">which</span> <span class="n">requires</span> <span class="n">both</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span>        <span class="n">versions</span><span class="p">)</span><span class="o">.</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span> 
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span>        <span class="n">The</span> <span class="n">suggested</span> <span class="n">container</span> <span class="n">configuration</span> <span class="k">for</span> <span class="mi">18</span><span class="o">+</span> <span class="n">is</span> <span class="n">to</span> <span class="n">place</span> <span class="n">a</span> <span class="n">single</span> <span class="n">mount</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span>        <span class="n">at</span> <span class="o">/</span><span class="k">var</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">postgresql</span> <span class="n">which</span> <span class="n">will</span> <span class="n">then</span> <span class="n">place</span> <span class="n">PostgreSQL</span> <span class="n">data</span> <span class="ow">in</span> <span class="n">a</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span>        <span class="n">subdirectory</span><span class="p">,</span> <span class="n">allowing</span> <span class="n">usage</span> <span class="n">of</span> <span class="s2">&#34;pg_upgrade --link&#34;</span> <span class="n">without</span> <span class="n">mount</span> <span class="n">point</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span>        <span class="n">boundary</span> <span class="n">issues</span><span class="o">.</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span> 
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span>        <span class="n">See</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">docker</span><span class="o">-</span><span class="n">library</span><span class="o">/</span><span class="n">postgres</span><span class="o">/</span><span class="n">issues</span><span class="o">/</span><span class="mi">37</span> <span class="k">for</span> <span class="n">a</span> <span class="p">(</span><span class="n">long</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span>         <span class="o">|</span>        <span class="n">discussion</span> <span class="n">around</span> <span class="n">this</span> <span class="n">process</span><span class="p">,</span> <span class="ow">and</span> <span class="n">suggestions</span> <span class="k">for</span> <span class="n">how</span> <span class="n">to</span> <span class="k">do</span> <span class="n">so</span><span class="o">.</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="n">db</span><span class="o">-</span><span class="mi">1</span> <span class="n">exited</span> <span class="n">with</span> <span class="n">code</span> <span class="mi">1</span> <span class="p">(</span><span class="n">restarting</span><span class="p">)</span>
</span></span></code></pre></div><p>Der Downgrade auf 17 war nach der obigen Anleitung recht schnell erledigt und <em>paperless</em> lief wieder zufriedenstellend mit der Version 17 von <em>PostgreSQL</em>.</p>

<h2 id="umstellung-auf-mariadb-12" data-numberify>Umstellung auf MariaDB 12<a class="anchor ms-1" href="#umstellung-auf-mariadb-12"></a></h2>
<p>Ich nahm dies als Anlass, die Datenbank-Engine auf MariaDB umzustellen.
Im Repo des Projektes gibt es dazu ebenfalls <a href="https://tools.rainerrose.de/redirect.php?id=61" target="_blank" rel="noopener noreferrer">eine Vorlage<i class="fas fa-external-link-square-alt ms-1"></i></a>.
Es gab drei Stellen, die ich dafür anpassen musste.</p>
<p>Es mussten im Abschnitt von <code>db</code> ein paar Umgebungsvariablen und natürlich das <code>image</code> umgestellt werden; hier ist 12 gerade aktuell.</p>
<p>Weiter unten im Abschnitt <code>webserver</code> mussten ebenfalls noch ein paar Umgebungsvariablen hinzugefügt werden.</p>
<p>Als letztes musste ich den Volume-Namen im Abschnitt <code>volumes</code> umbenennen.
Danach konnte ich die Container wieder starten und die Daten importieren, wie weiter oben beschrieben.</p>
<p>Nachfolgend ein <code>diff</code> aus meinem <em>ansible</em>-Playbook. (Ja, ich habe <code>restart</code> auch noch gleich umstellt bzw. an die neue Vorlage aus dem Projekt angeglichen. )</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gd">--- a/roles/paperless/files/docker-compose.yml
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gd"></span><span class="gi">+++ b/roles/paperless/files/docker-compose.yml
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gi"></span><span class="gu">@@ -28,26 +28,24 @@
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span> 
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> services:
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">   broker:
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gd">-    image: docker.io/library/redis:8
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gd">-    restart: unless-stopped
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gd"></span><span class="gi">+    image: docker.io/library/redis:7
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gi">+    restart: always
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gi"></span>     volumes:
</span></span><span class="line"><span class="ln">12</span><span class="cl">       - redisdata:/data
</span></span><span class="line"><span class="ln">13</span><span class="cl"> 
</span></span><span class="line"><span class="ln">14</span><span class="cl">   db:
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gd">-    image: docker.io/library/mariadb:12
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gd">-    restart: unless-stopped
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gd"></span><span class="gi">+    image: docker.io/library/postgres:17
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="gi">+    restart: always
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gi"></span>     volumes:
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gd">-      - dbdata:/var/lib/mysql
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="gd"></span><span class="gi">+      - pgdata:/var/lib/postgresql/data
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gi"></span>     environment:
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gd">-      MARIADB_HOST: paperless
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="gd">-      MARIADB_DATABASE: paperless
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gd">-      MARIADB_USER: paperless
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="gd">-      MARIADB_PASSWORD: paperless
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gd">-      MARIADB_ROOT_PASSWORD: paperless
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gd"></span><span class="gi">+      POSTGRES_DB: paperless
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="gi">+      POSTGRES_USER: paperless
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="gi">+      POSTGRES_PASSWORD: paperless
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="gi"></span> 
</span></span><span class="line"><span class="ln">32</span><span class="cl">   webserver:
</span></span><span class="line"><span class="ln">33</span><span class="cl">     image: ghcr.io/paperless-ngx/paperless-ngx:latest
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="gd">-    restart: unless-stopped
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="gd"></span><span class="gi">+    restart: always
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="gi"></span>     depends_on:
</span></span><span class="line"><span class="ln">37</span><span class="cl">       - db
</span></span><span class="line"><span class="ln">38</span><span class="cl">       - broker
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="gu">@@ -68,13 +66,9 @@ services:
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="gu"></span>       - extra.env
</span></span><span class="line"><span class="ln">41</span><span class="cl">     environment:
</span></span><span class="line"><span class="ln">42</span><span class="cl">       PAPERLESS_REDIS: redis://broker:6379
</span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="gd">-      PAPERLESS_DBENGINE: mariadb
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="gd"></span>       PAPERLESS_DBHOST: db
</span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="gd">-      PAPERLESS_DBUSER: paperless # only needed if non-default username
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="gd">-      PAPERLESS_DBPASS: paperless # only needed if non-default password
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="gd">-      PAPERLESS_DBPORT: 3306
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="gd"></span> volumes:
</span></span><span class="line"><span class="ln">49</span><span class="cl">   data:
</span></span><span class="line"><span class="ln">50</span><span class="cl">   media:
</span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="gd">-  dbdata:
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="gd"></span><span class="gi">+  pgdata:
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="gi"></span>   redisdata:
</span></span></code></pre></div>]]></content:encoded>
    <enclosure url="https://www.rainerrose.de/images/docs/paperless.png" length="8086" type="image/.png" />
    </item>
    
    <item>
      <title>Mein Setup: Kontakte, Kalender, EMail, Passwörter</title>
      <link>https://www.rainerrose.de/posts/2025/mein-setup-kontakte-kalender-email-passwoerter/</link>
      <pubDate>Tue, 30 Sep 2025 22:07:22 +0200</pubDate>
      <guid>https://www.rainerrose.de/posts/2025/mein-setup-kontakte-kalender-email-passwoerter/</guid>
      <description><![CDATA[<!-- Anreißer -->
<p>Ich wurde schon mal des Öfteren gefragt wie ich denn meine Kontakte, Kalender, Passwörter, EMails usw. verwalte.<br>
Persönlich möchte ich diese Datenschätze nicht oder nur möglichst wenig der &ldquo;Cloud&rdquo; überantworten und mache daher vieles selbst.
Das Setup bzw. das Konzept hat eine jahrelange Entwicklung und eine Menge Veränderungen hinter sich. Zu einigen Teilbereichen sind auch schon vereinzelte Blogartikel entstanden.</p>
<p>Ein Gespräch letztens auf Mastodon hat mich dazu veranlasst, dies mal aufzuschreiben und hier zu veröffentlichen. Wer mag, kann sich aus diesem Text Ideen holen und für seine Situation anpassen.</p>

<h2 id="tldr" data-numberify>TL&DR;<a class="anchor ms-1" href="#tldr"></a></h2>
<ul>
<li><em>Nextcloud</em> selfhostet auf eigener Hardware im Homelab</li>
<li>Passwort-Safes <code>KeePass</code>, <code>KeePassXC</code>, <em>Bitwarden</em>, <em>Vaultwarden</em></li>
<li>Backup mit <code>restic</code> und <code>resticprofile</code> verschlüsselt mit Deduplizierung auf S3-Storage und externen Festplatten</li>
</ul>
<p>Jetzt die Langfassung: Wie sieht mein Setup aus?</p>]]></description>
      <content:encoded><![CDATA[<!-- Anreißer -->
<p>Ich wurde schon mal des Öfteren gefragt wie ich denn meine Kontakte, Kalender, Passwörter, EMails usw. verwalte.<br>
Persönlich möchte ich diese Datenschätze nicht oder nur möglichst wenig der &ldquo;Cloud&rdquo; überantworten und mache daher vieles selbst.
Das Setup bzw. das Konzept hat eine jahrelange Entwicklung und eine Menge Veränderungen hinter sich. Zu einigen Teilbereichen sind auch schon vereinzelte Blogartikel entstanden.</p>
<p>Ein Gespräch letztens auf Mastodon hat mich dazu veranlasst, dies mal aufzuschreiben und hier zu veröffentlichen. Wer mag, kann sich aus diesem Text Ideen holen und für seine Situation anpassen.</p>

<h2 id="tldr" data-numberify>TL&DR;<a class="anchor ms-1" href="#tldr"></a></h2>
<ul>
<li><em>Nextcloud</em> selfhostet auf eigener Hardware im Homelab</li>
<li>Passwort-Safes <code>KeePass</code>, <code>KeePassXC</code>, <em>Bitwarden</em>, <em>Vaultwarden</em></li>
<li>Backup mit <code>restic</code> und <code>resticprofile</code> verschlüsselt mit Deduplizierung auf S3-Storage und externen Festplatten</li>
</ul>
<p>Jetzt die Langfassung: Wie sieht mein Setup aus?</p>

<h2 id="die-details" data-numberify>Die Details<a class="anchor ms-1" href="#die-details"></a></h2>
<p>Angefangen habe ich mit <em>Owncloud</em> auf einem <em>BananaPi</em>. Vor einigen Jahren bin dann zu <em>Nextcloud</em> gewechselt. Aktuell läuft die Installation auf meinem Heim-NAS von <em>QNAP</em> in einer virtuellen Maschine wo ein <em>Debian</em> drauf läuft.<br>
Aus-Gründen<sup>(tm)</sup> waren und sind die Installationen nie von außen erreichbar.</p>

<h2 id="nextcloud" data-numberify>Nextcloud<a class="anchor ms-1" href="#nextcloud"></a></h2>
<p>In der <em>Nextcloud</em> nutze ich überwiegend folgende Funkionen:</p>
<ul>
<li>Verwaltung von
<ul>
<li>Kalendern</li>
<li>Kontakt-Daten (VCards)</li>
</ul>
</li>
<li>Dateiaustausch</li>
</ul>
<p>Die gesamte Familie bindet alles in ihre jeweiligen Mail- und Kalender-Clients auf den unterschiedlichsten Rechnern, mobilen Geräten mit verschiedensten Betriebssystemen an. Die Synchronisation funktioniert somit natürlich nur im heimischen WLAN oder über VPN.</p>

<h3 id="backups" data-numberify>Backups<a class="anchor ms-1" href="#backups"></a></h3>
<p>Da mir meine Daten (Kalender und Kontakte) sehr wichtig sind, sichere ich die Daten regelmäßig und fertige ein Backup an.<br>
Aktuell läuft auf dem Server, wo die Nextcloud installiert ist, ein nächtlicher Cronjob:</p>
<ul>
<li>der die <em>Nextcloud</em> in den Wartungsmodus versetzt,
<ul>
<li>um einen konsistenten Zustand zu erreichen,</li>
</ul>
</li>
<li>einen Dump der mysql-Tabellen auf Festplatte zieht,</li>
<li>eine dateibasierte Sicherung anfertigt</li>
<li>und schlussendlich den Wartungsmodus wieder beendet.</li>
</ul>
<p>Der Cronjob läuft mitten in der Nacht, wenn sowieso alle schlafen. Selbst wenn das mal nicht der Fall sein sollte, verhindert der Wartungsmodus schlimmeres.<br>
Dieses Verfahren existiert schon so seit Jahren und hat sich aus meiner Sicht bewährt. Selbst der Umzug von <em>Owncloud</em> nach <em>Nextcloud</em> sowie vom <em>BananaPi</em> in die virtuelle Maschine hat damit wunderbar funktioniert.</p>
<p>Die dateibasierte Sicherung und die Dumps ins Dateisystem sind nur ein erster Schritt. Wenn ich einzelne Kontakte oder ähnliches wiederherstellen möchte, ist das rauspfriemeln einzelner Daten nicht wirklich schön, beispielsweise eine einzelne aus Versehen gelöschte Kontakt-Karte.<br>
Allerdings gibt es die Möglichkeit alle Kalendereinträge in <strong>eine</strong> <code>ics</code> Datei herunterzuladen und alle Kontakte in <strong>eine</strong> <code>.vcf</code>-Datei herunterzuladen. Daraus ist die Wiederherstellung aus meiner Sicht wesentlich einfacher. Wenn alle Dateien gelöscht sind, reicht ggf. sogar ein Doppelklick im Dateiexplorer auf die jeweilige Datei und es wird im Standard-Mail-Programm geöffnet. Von diesem ist dann ein Import wieder möglich. Dadurch, dass das Mail-Programm wieder in die Nextcloud synchronisiert, ist der Kontakt wieder &ldquo;drin&rdquo;. Natürlich war ich faul und habe dies automatisiert; wie ich dies realisiert habe, lässt sich <a href="/posts/export-nextcloud-rolle/">hier nachlesen</a>.</p>
<p>Die ganzen Datenkopien (wie Dumps und Exporte) sichere ich nun mit einem Backup-Programm. Da mir die Daten allgemein wichtig sind und ich auch vor Diebstahl und Feuer gewappnet sein möchte, setze ich hier auf das 3-2-1 Prinzip.</p>

<h4 id="3-2-1-prinzip" data-numberify>3-2-1-Prinzip<a class="anchor ms-1" href="#3-2-1-prinzip"></a></h4>

<blockquote class="alert alert-info" role="alert">
    <p class="alert-heading fw-bold">
      <i class="fas fa-info-circle me-2"></i>Hinweis
    </p>
    <p>Hinweis: Das 3-2-1-Prinzip bedeutet in kurz:<br>
Du hast <strong>drei</strong> Kopien auf <strong>zwei</strong> unterschiedlichen Medien, wobei <strong>eine</strong> Kopie außerhalb liegt.</p>
</blockquote>
<p>Die bedeutet: Ich habe eine externe Festplatte auf die ich die Backups regelmäßig kopiere, wobei eine Kopie bei meinen Eltern liegt. Das tägliche Backup schiebe ich in die <em>Cloud</em>. <a href="/posts/backupkonzept/#offsite-backup">Details siehe in diesem Blog-Artikel im Abschnitt Offsite-Backup</a>.</p>

<h4 id="cloudspeicher" data-numberify>Cloudspeicher<a class="anchor ms-1" href="#cloudspeicher"></a></h4>
<p>&ldquo;<em>Cloud</em>&rdquo; bedeutet hierbei irgend ein Speicherort, der Dateien aufnehmen kann. Das kann ein <em>OneDrive</em>, eine <em>Dropbox</em>, <em>PDrive</em> oder was auch immer sein.
Bei mir ist dies aktuell ein angemieteter S3-kompatibler-Speicher.<br>
Die Backups verschlüssele ich natürlich, daher ist es egal, wo diese im Endeffekt liegen:</p>
<ul>
<li>ob es nun eine externe Festplatte ist, die geklaut wird</li>
<li>oder irgendein Cloud-Speicher, wo Daten abfließen können.</li>
</ul>
<p>Das gewählte Verschlüsselungspasswort sollte daher möglichst lang und komplex sein.</p>

<h3 id="backup-software" data-numberify>Backup-Software<a class="anchor ms-1" href="#backup-software"></a></h3>
<p>Als Software verwendet ich seit einiger <code>restic</code> in Zusammenarbeit mit <code>resticprofile</code>, da ich mit diesen Tools und die Backup-Jobs gut weg-automatisieren kann. Wenn ich daran denken muss, Backups zu machen, vergesse ich das nur.<br>
Wie die Software funktioniert und bedient wird, haben Andreas und ich mal
<a href="/docs/writings/2025/#backups-mit-restic-und-resticprofile">auf den Chemmnitzer LinuxTagen erzählt</a>.</p>
<p>Aus meiner Sicht ist der Vorteil, dass <code>restic</code></p>
<ul>
<li>die Daten verschlüsselt,</li>
<li>den Platzbedarf mittels Deduplizierung verringert</li>
<li>und ausmisten nach Regeln beherrscht, da kein Speicher unendlich ist.</li>
</ul>
<p><code>resticprofile</code> unterstützt hierbei sogar bei der Erzeugung eines möglichst langen und zufälligem Passwortes.
Dadurch, dass ich Dinge automatisch in die <em>Cloud</em> sichere, aber lokal noch eine Festplatte habe, die ich &ldquo;natürlich&rdquo; (leider) nur unregelmäßig befülle, hat mir dieses Konzept schon mehrfach
<a href="/posts/backupkonzept/#beispiel">wortwörtlich den Hintern gerettet</a>.
Aktuell kostet mich der S3-Storage irgendwas unter 4 Euro pro Monat.
<a href="/posts/backupkonzept/">Bei Interesse nach mehr Details hier klicken</a>.
Die dortige Angabe <em>Backblaze</em> stimmt inzwischen nicht mehr. Wegen <code>#unplugTrump</code> bin ich zu <em>Hetzner</em> <em>StorageShare</em> gewechselt.</p>

<h2 id="passwort-safes" data-numberify>Passwort-Safes<a class="anchor ms-1" href="#passwort-safes"></a></h2>
<p>Ebenfalls seit Jahren nutze ich einen Passwort-Speicher. Bisher reichte mir ein <code>KeePass</code>. Später wechselte ich zu <code>KeePassXC</code>. Die Tresor-Datei lege ich bei mir in meinen <em>Nextcloud</em>-Ordner. Dort synchronisiert sich diese Datei wunderbar zwischen meinen Geräten und ist überall verfügbar, wo ich sie benötige. <br>
Das <code>KeePass</code>-Tersor-Format ist recht verbreitet und es gibt für die unterschiedlichsten Betriebssysteme Software dafür. Die Client-Software für <em>Nextcloud</em> ist ebenso für viele Betriebssysteme verfügbar.</p>
<p>Dank <em>Auto-Fill</em>-Funktion habe ich schon länger kein Passwort mehr getippt und durch die <em>Passwort-Würfeln</em>-Funktion kenne ich auch gar kein Passwort mehr auswendig. Jedem Dienst ein Passwort zu verpassen, ist dadurch für mich recht einfach geworden.
In letzter Zeit musste ich jedoch öfters Passwörter teilen und da hat mir <code>KeePassXC</code> nicht gereicht. <code>KeeShare</code> habe ich erst vor kurzem  <a href="https://tools.rainerrose.de/redirect.php?id=53" target="_blank" rel="noopener noreferrer">in einen Artikel auf gnulinux.ch<i class="fas fa-external-link-square-alt ms-1"></i></a> entdeckt.<br>
Aus diesem Grund habe ich mir einen <em>Vaultwarden</em> installiert. <em>Vaultwarden</em> ist auch perfekt, wenn ich Dateien oder Textschnipsel über unsichere Kanäle verschicken möchte. Die Funktion nennt sich <em>vaultwarden-send</em>. Über diese Funktion kann ich Dateien oder kurze Texte in den Vault hochladen und danach einen Link zum Herunterladen generieren.
Zusätzlich kann ich diesen Link noch mit Passwort-Schutz und sonstigen Parameter versehen. Diesen Link kann ich ruhigen Gewissens unverschlüsselt per E-Mail verschicken. Die Daten liegen verschlüsselt auf der Server-Festplatte und löschen sich  standardmäßig nach sieben Tagen selbst.</p>

<h3 id="tipps-zu-vaultwarden" data-numberify>Tipps zu Vaultwarden<a class="anchor ms-1" href="#tipps-zu-vaultwarden"></a></h3>

<h4 id="standard-einstellungen" data-numberify>Standard-Einstellungen<a class="anchor ms-1" href="#standard-einstellungen"></a></h4>
<p>Ich empfehle zwei Standard-Einstellungen zu ändern:</p>
<ul>
<li><code>Standard-URI Übereinstimmungserkennung</code> auf <code>Beginnt mit</code>
<ul>
<li>das funktioniert bei mir am zuverlässigsten.</li>
<li>gerade auch in Kombination, dass im URL-Feld des jeweiligen Eintrags dann nur die Domain hinterlegt wird.</li>
<li>Das bedeutet ohne Pfade und sonstige Parameter!</li>
<li>Auffindbar derzeit unter: <code>Einstelllungen-&gt;Automatisches Ausfüllen</code></li>
</ul>
</li>
<li><code>Zwischenablage leeren</code> würde ich auf einen kurzen Zeitraum setzen,  z.B. auf 30 Sekunden.
<ul>
<li>soweit ich mich erinnere ist der Standard <code>niemals</code>.</li>
<li>Auffindbar derzeit unter: <code>Einstelllungen-&gt;Sonstiges</code></li>
</ul>
</li>
</ul>
<p>Leider muss die Einstellung in <strong>jedem Client einzeln</strong> gesetzt werden (Browser-Plugin, Smartphone-App usw.)</p>

<h4 id="admin-seite" data-numberify>Admin-Seite<a class="anchor ms-1" href="#admin-seite"></a></h4>
<p>Die Admin-Seite würde ich aus Sicherheitsgründen ausschalten bzw. ausgeschaltet lassen.
Wenn ich die Funktionalität doch einmal benötige, schalte ich sie kurzzeitig ein, auch wenn es zwei kurze Downtimes bedeutet.</p>

<h4 id="verständnis-entwickeln" data-numberify>Verständnis entwickeln<a class="anchor ms-1" href="#verständnis-entwickeln"></a></h4>
<p>Als ich <em>vaultwarden</em> aufgesetzt habe, hatte ich zuerst zwei Test-Accounts angelegt.
Danach habe ich eine &ldquo;Organistation&rdquo; und mehrere &ldquo;Sammlungen&rdquo; angelegt.
Mit diesen zwei Accounts konnte ich dann ein besseres Verständnis der beiden Begriffe und des Berechtigungssystems erlangen.</p>

<h2 id="email" data-numberify>EMail<a class="anchor ms-1" href="#email"></a></h2>
<p>Das ist für mich persönlich so ein großes Unlust-Thema und PITA.<br>
Seit ich online bin, lagere ich diesen Service als Dienstleistung aus. Ich klicke mir irgendwo eine Domain und einen E-Mail-Speicherplatz und lasse das Menschen machen, die mehr Ahnung von dem Thema haben als ich.
Aktuell bin ich seit Jahren sehr glücklich bei <a href="https://tools.rainerrose.de/redirect.php?id=11" target="_blank" rel="noopener noreferrer">uberspace<i class="fas fa-external-link-square-alt ms-1"></i></a>.<br>
Ich habe bei denen mehrere Accounts (<code>asteroid</code> genannt). Dort lassen sich ohne Zusatzkosten Domains &ldquo;aufschalten&rdquo;.
In deren verfügbarer Dokumentation (<code>manual</code>) sind die benötigten Befehle recht gut beschrieben, sofern man sich an die Kommandozeile herantraut.
In deren Dokumentationsbereich namens <code>Uberlab</code> sind weitere Möglichkeiten beschrieben, zusätzliche Software zu installieren; inkl. Update-Schritten. Vielfach ist es nur ein copy&amp;paste von vorgefertigten Befehlen.</p>

<h2 id="idee-und-ausblick" data-numberify>Idee und Ausblick<a class="anchor ms-1" href="#idee-und-ausblick"></a></h2>
<p>Soweit ein Einblick in mein Setup und so wie es für mich funktioniert und bewährt hat. Ich habe versucht es kurz zu halten und bei ausufernden Themen mit Verweisen zu arbeiten.</p>
<p>Wer mag, kann die für sich passenden Informationen herausziehen und die eigenen Verhältnisse und Anforderungen anpassen oder ein Konzept zu entwickeln.</p>
<p>Auf der Wunschliste steht noch die Generierung der TLS-Zertifikate per <em>Let&rsquo;s-Encypt</em>, da muss ich mich nochmal einlesen, wie ich dies umsetze, wenn die Systeme nicht von außen erreichbar sind.</p>

<h2 id="changelog" data-numberify>Changelog<a class="anchor ms-1" href="#changelog"></a></h2>
<table>
  <thead>
      <tr>
          <th>Datum</th>
          <th>Änderung</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>16.01.2026</td>
          <td>Vaultwarden: Hinweis auf Admin-Seite und ein paar Unterüberschriften in diesem Absatz hinzugefügt.</td>
      </tr>
  </tbody>
</table>]]></content:encoded>
    <enclosure url="https://www.rainerrose.de/wallpaper/posts/hardware.jpg" length="1385855" type="image/.jpg" />
    </item>
    
    <item>
      <title>Ansible Rolle export_nextcloud</title>
      <link>https://www.rainerrose.de/posts/export-nextcloud-rolle/</link>
      <pubDate>Sat, 22 Feb 2025 15:37:52 +0100</pubDate>
      <guid>https://www.rainerrose.de/posts/export-nextcloud-rolle/</guid>
      <description><![CDATA[<!-- Anreißer -->
<p>Derzeit sichere ich meine Nextcloud-Instanz mit einem dateibasierten Backup-Tool, sowie einem Dump der Datenbank.
Letztens hatte ich in meinem Umfeld jemanden, der ein paar Kontakte aus Versehen gelöscht hat. Nun können die vermissten Kontakte zwar aus dem Dump der Datenbank herausgepfriemelt werden, schön ist das aber nicht.</p>
<p>Nach etwas Recherche bin ich auf einen recht simplen <code>curl</code>-Befehl gestoßen, der eine <code>.ics</code> bzw <code>.vcf</code>-Datei heraus exportiert.</p>
<p>Dies ist dann doch aus meiner Sicht etwas einfacher. Sofern z.B. alle Kontakte gelöscht worden sind, reicht dann sogar ein Doppelklick auf die Datei und alle Kontakte werden wieder importiert. Dies sollte wesentlich stressfreier sein.</p>
<p>Als Familien-Admin möchte ich den Export für alle Nutzenden der Instanz regelmäßig machen; daher habe ich mir eine Ansible-Rolle dafür gestrickt.</p>
<p>Das Grundprinzip und die Konfiguration der Rolle möchte ich in diesem Artikel beschreiben.</p>]]></description>
      <content:encoded><![CDATA[<!-- Anreißer -->
<p>Derzeit sichere ich meine Nextcloud-Instanz mit einem dateibasierten Backup-Tool, sowie einem Dump der Datenbank.
Letztens hatte ich in meinem Umfeld jemanden, der ein paar Kontakte aus Versehen gelöscht hat. Nun können die vermissten Kontakte zwar aus dem Dump der Datenbank herausgepfriemelt werden, schön ist das aber nicht.</p>
<p>Nach etwas Recherche bin ich auf einen recht simplen <code>curl</code>-Befehl gestoßen, der eine <code>.ics</code> bzw <code>.vcf</code>-Datei heraus exportiert.</p>
<p>Dies ist dann doch aus meiner Sicht etwas einfacher. Sofern z.B. alle Kontakte gelöscht worden sind, reicht dann sogar ein Doppelklick auf die Datei und alle Kontakte werden wieder importiert. Dies sollte wesentlich stressfreier sein.</p>
<p>Als Familien-Admin möchte ich den Export für alle Nutzenden der Instanz regelmäßig machen; daher habe ich mir eine Ansible-Rolle dafür gestrickt.</p>
<p>Das Grundprinzip und die Konfiguration der Rolle möchte ich in diesem Artikel beschreiben.</p>
<p>Auf die Bedienung und Verwendung von Ansible möchte ich hier nicht eingehen, da setze ich entsprechendes Wissen voraus.</p>
<p>Zum Zeitpunkt des Artikel ist die Nextcloud-Version 31 aktuell. Die Pfade, die in der Ansible-Rolle verwendet werden, orientieren sich an diesem Stand. Sollten sich die Subpfade der URLs ändern, müsste die Rolle angepasst werden.</p>

<h2 id="überblick" data-numberify>Überblick<a class="anchor ms-1" href="#überblick"></a></h2>
<p>Um die Rolle zu verstehen, ist es womöglich am einfachsten, sich das Vorgehen zu verdeutlichen, wenn ein manuelles Backup angefertigt werden soll. Dies soll hier in den nächsten Abschnitten beschrieben werden, um dann im nächsten Schritt zur Automatisierung zu kommen.</p>

<h2 id="händisches-backup" data-numberify>Händisches Backup<a class="anchor ms-1" href="#händisches-backup"></a></h2>
<p>Melde dich als ersten Schritt in Deiner Nextcloud an.</p>

<h3 id="kontakte" data-numberify>Kontakte<a class="anchor ms-1" href="#kontakte"></a></h3>
<ul>
<li>Wähle den Reiter <code>Kontakte</code>.</li>
<li>Gehe nach unten auf der linken Seite und klicke da auf <code>Kontakte-Einstellungen</code>.</li>
</ul>
<p><picture><img class="img-fluid " alt="Weg zu den Kontakte-Einstellungen" src="https://www.rainerrose.de/images/posts/export-nextcloud-rolle/nextcloud-kontakte-weg.jpg?v=59fab2dd88dc536d0263c9c2916f2368" loading="lazy" width="595" height="464" />
</picture>

</p>
<ul>
<li>Navigiere jetzt zu den <code>Allgemeinen Einstellungen</code>.</li>
<li>Beim gewünschten Adressbuch auf die drei Punkte klicken (1)</li>
<li>und auf <code>Herunterladen</code> klicken (2).</li>
</ul>
<p><picture><img class="img-fluid " alt="Weg zu den Export der Kontakte" src="https://www.rainerrose.de/images/posts/export-nextcloud-rolle/nextcloud-kontakte-export.jpg?v=59f012e6d6fa7c320100b4896e27c389" loading="lazy" width="925" height="693" />
</picture>

</p>
<ul>
<li>Voilà! Es wird eine <code>.vcf</code>-Datei mit einem aktuellen Datumsstempel zum Download angeboten.</li>
</ul>

<h3 id="kalender" data-numberify>Kalender<a class="anchor ms-1" href="#kalender"></a></h3>
<ul>
<li>Wähle den Reiter <code>Kontakte</code>.</li>
<li>Wähle Deinen Kalender aus, den du exportieren möchtest.</li>
<li>Fahre Deine Maus etwas nach rechts bis das Stift-Icon erscheint (1) und klicke auf diesen.</li>
</ul>
<p><picture><img class="img-fluid " alt="Weg zu den Kalender-Einstellungen" src="https://www.rainerrose.de/images/posts/export-nextcloud-rolle/kalender-einstellungen-weg.jpg?v=4a778c757475d6d8fdc941d53a6da564" loading="lazy" width="623" height="264" />
</picture>

</p>
<ul>
<li>Im nächsten Dialog <code>Kalender bearbeiten</code> gibt es dann eine Schaltfläche mit der Beschriftung <code>Exportieren</code>.</li>
</ul>
<p><picture><img class="img-fluid " alt="Weg zu den Kalender-Einstellungen" src="https://www.rainerrose.de/images/posts/export-nextcloud-rolle/kalender-export.jpg?v=801d80930c1b8ce33e664c38ade642e3" loading="lazy" width="330" height="454" />
</picture>

</p>
<ul>
<li>Voilà! Es wird eine <code>.ics</code>-Datei mit einem aktuellen Datumsstempel zum Download angeboten.</li>
</ul>

<h2 id="automatisierung" data-numberify>Automatisierung<a class="anchor ms-1" href="#automatisierung"></a></h2>
<p>Jetzt wollen wir das ganze automatisieren; dafür gibt es meine Ansible-Rolle <code>export_nextcloud</code>. Die Rolle findet Du auf meinem <a href="https://tools.rainerrose.de/redirect.php?id=46" target="_blank" rel="noopener noreferrer">Gitlab-Account<i class="fas fa-external-link-square-alt ms-1"></i></a>.</p>
<p>Die Rolle solltest Du auf einem Linux-Host deployen, dem Du vertraust und der Zugriff auf die Nextcloud hat.</p>

<h3 id="minimale-angaben" data-numberify>Minimale Angaben<a class="anchor ms-1" href="#minimale-angaben"></a></h3>

<h4 id="variable-export_nextcloud_url" data-numberify>Variable export_nextcloud_url<a class="anchor ms-1" href="#variable-export_nextcloud_url"></a></h4>
<p>Zunächst benötigt die Rolle die URL, worunter die Nextcloud-Instanz erreichbar ist.
Dazu hinterlegst Du diese in der Variable <code>export_nextcloud_url</code> als Hostvariable, wo das Export-Script später laufen soll.</p>
<p>Beispiel:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nt">export_nextcloud_url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;https://meine.nextcloud.instanz&#34;</span><span class="w">
</span></span></span></code></pre></div>
<h4 id="variable-export_nextcloud_user" data-numberify>Variable export_nextcloud_user<a class="anchor ms-1" href="#variable-export_nextcloud_user"></a></h4>
<p>Jetzt brauchst Du eine Liste mit einem oder mehreren Usernamen, die du sichern möchtest. Zusätzlich ein Passwort und einen Zeitpunkt, wann das Export-Script laufen soll.</p>

<blockquote class="alert alert-warning" role="alert">
    <p class="alert-heading fw-bold">
      <i class="fas fa-exclamation-circle me-2"></i>Empfehlung
    </p>
    <p>Statt des regulären Passwortes für Deinen User, empfehle ich die Generierung eines Personal Access Token kurz &ldquo;<code>PAT</code>&rdquo;.</p>
</blockquote>
<p>Die Einrichtung eines <code>PAT</code> beschreibe ich <a href="/posts/nextcloud-pat-erstellen/">in einem anderem Blog-Artikel</a>.</p>
<p>Als dritten Parameter wird noch der Zeitstempel benötigt, wo der Export laufen soll.
Mit dieser Angabe wird später eine SystemD-Timer-Unit erstellt.</p>
<p>Die Syntax dieses Attributes folgt dem Parameter <code>on_calendar</code> von <a href="https://www.freedesktop.org/software/systemd/man/systemd.timer#OnCalendar=" target="_blank" rel="noopener noreferrer">systemd.unit(5)<i class="fas fa-external-link-square-alt ms-1"></i></a>.
Aus jeden <code>username</code> wird eine extra SystemD-Timer-Units erstellt, nach folgendem Muster:</p>
<ul>
<li><code>export_nextcloud-user1.timer</code></li>
<li><code>export_nextcloud-user2.timer</code></li>
</ul>
<p>Die Konfiguration sieht am Ende zum Beispiel so aus:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nt">export_nextcloud_user</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">  </span>- <span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;user1&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="nt">password</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;password1&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="nt">on_calendar</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;*-*-* 03:00&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">  </span>- <span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;user2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">    </span><span class="nt">password</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;password2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w">    </span><span class="nt">on_calendar</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;*-*-* 03:01&#39;</span><span class="w">
</span></span></span></code></pre></div>
<h3 id="optionale-angaben" data-numberify>Optionale Angaben<a class="anchor ms-1" href="#optionale-angaben"></a></h3>

<h4 id="variable-export_nextcloud_script_path" data-numberify>Variable export_nextcloud_script_path<a class="anchor ms-1" href="#variable-export_nextcloud_script_path"></a></h4>
<p>Die nächste Variable bestimmt, wohin das Script kopiert werden soll. Der Standardpfad lautet <code>/opt/export_nextcloud</code>.</p>

<h4 id="variable-export_nextcloud_backup_path" data-numberify>Variable export_nextcloud_backup_path<a class="anchor ms-1" href="#variable-export_nextcloud_backup_path"></a></h4>
<p>Über diese Variable kann bestimmt werden, wohin die exportierten Kalender- und Kontakte-Dateien hingeschrieben werden sollen.
<code>/backup/nextcloud_export</code> lautet hier der Standardpfad.</p>
<p>Hier entsteht pro Username eine Kalender- und eine Kontakte-Datei mit dem Username
nach folgendem Muster:</p>
<ul>
<li><code>kalender-${username}.ics</code></li>
<li><code>kontakte-${username}.vcf</code></li>
</ul>
<p>Und ja, ich war zu faul, noch ein extra Aufräum-Mechanismus zu schreiben.
Für die Versionierung ist bei mir mein Backup-Programm zuständig ;-) .
Das kann mein Sicherungsprogramm viel effizienter als ich.
Wenn ich an ältere Versionen herankommen möchte, muss ich die Sicherung bemühen; ich spare mir dadurch zusätzliche Fehlerquellen und Abhängigkeiten.</p>

<h4 id="variable-export_nextcloud_prometheus_path" data-numberify>Variable export_nextcloud_prometheus_path<a class="anchor ms-1" href="#variable-export_nextcloud_prometheus_path"></a></h4>
<p>Diese Variable legt den Pfad fest, wo die Prometheus-Statistiken hingeschrieben werden.</p>

<h4 id="variable-export_nextcloud_extra_curl_options" data-numberify>Variable export_nextcloud_extra_curl_options<a class="anchor ms-1" href="#variable-export_nextcloud_extra_curl_options"></a></h4>
<p>Diese Variable habe ich am 03.10.2025 eingeführt, weil der zugrunde liegende <code>curl</code> sich am selbst-signierten Zertifikat meiner Nextcloud-Instanz störte.
Mit dem Parameter <code>-k</code> oder <code>--insecure</code> lässt sich das aber übersteuern. Dies erforderte allerdings eine Anpassung des Scriptes.</p>
<p>Nun können weitere Parameter über diese Variable optional mitgegeben werden.</p>
<p>Im unteren Beispiel Ansible Playbook fügt ihr an den einen Task folgende zwei Zeilen an.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="w">  </span><span class="nt">vars</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="nt">export_nextcloud_extra_curl_options</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;--insecure&#34;</span><span class="w">
</span></span></span></code></pre></div>
<h3 id="ansible-playbook" data-numberify>Ansible Playbook<a class="anchor ms-1" href="#ansible-playbook"></a></h3>
<p>Jetzt kannst Du die Rolle in ein Ansible-Playbook einbinden.</p>
<p>Ein minimales Beispiel wäre:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Setup export_nextcloud</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">  </span><span class="nt">hosts</span><span class="p">:</span><span class="w"> </span><span class="l">export_nextcloud</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">  </span><span class="nt">roles</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">    </span>- <span class="l">export_nextcloud</span><span class="w">
</span></span></span></code></pre></div>
<h2 id="backup" data-numberify>Backup<a class="anchor ms-1" href="#backup"></a></h2>
<p>Im Nachgang bzw. nach dem Ausführen der jeweiligen SystemD-Timer-Unit, empfehle ich noch ein Backup des Verzeichnisses wo die exportierten Dateien liegen.
Das Verzeichnis kannst zum Beispiel dateibasiert mittels deiner präferierten Wunsch-Backup-Software gesichert werden.
Ich selbst habe dazu die Pfade in meinem Sicherungsscript aufgenommen; hier verwende ich <code>restic</code> seit längerem erfolgreich.</p>

<h2 id="fertig" data-numberify>Fertig.<a class="anchor ms-1" href="#fertig"></a></h2>
<p>Im Grunde war es das auch schon. Viel Spaß beim Benutzen.
Der Source-Code der Rolle liegt in meinem <a href="https://tools.rainerrose.de/redirect.php?id=46" target="_blank" rel="noopener noreferrer">Gitlab-Account<i class="fas fa-external-link-square-alt ms-1"></i></a>.</p>

<h2 id="changelog" data-numberify>Changelog<a class="anchor ms-1" href="#changelog"></a></h2>
<table>
  <thead>
      <tr>
          <th>Datum</th>
          <th>Änderung</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>03.10.2025</td>
          <td>Neue optionale Variable <code>export_nextcloud_extra_curl_options</code></td>
      </tr>
  </tbody>
</table>]]></content:encoded>
    <enclosure url="https://www.rainerrose.de/wallpaper/posts/nextcloud.png" length="9229" type="image/.png" />
    </item>
    
    <item>
      <title>Nextcloud - Einrichtung eines PAT</title>
      <link>https://www.rainerrose.de/posts/nextcloud-pat-erstellen/</link>
      <pubDate>Sat, 22 Feb 2025 15:37:43 +0100</pubDate>
      <guid>https://www.rainerrose.de/posts/nextcloud-pat-erstellen/</guid>
      <description><![CDATA[<!-- Anreißer -->
<p>Soll auf Daten in einer Nextcloud zugegriffen werden, erfolgt dies häufig mittels der Kombination aus <code>Username</code> und <code>Passwort</code>.
Erfolg dieser Zugriff jedoch mittels mobilen Geräten oder automatisiert aus anderen Diensten, ist es meist eine schlechte Idee, das Passwort großflächig zu verteilen. Mobile Geräte haben manchmal die Eigenschaft verloren zu gehen oder gestohlen zu werden.
Weiterhin werden Dienste oder Geräte kompromitert oder unterstützen vielfach keine Zweifaktor-Authentisierung (2FA).
Hierbei hilft die
Generierung eines Personal Access Token kurz &ldquo;<code>PAT</code>&rdquo;.</p>]]></description>
      <content:encoded><![CDATA[<!-- Anreißer -->
<p>Soll auf Daten in einer Nextcloud zugegriffen werden, erfolgt dies häufig mittels der Kombination aus <code>Username</code> und <code>Passwort</code>.
Erfolg dieser Zugriff jedoch mittels mobilen Geräten oder automatisiert aus anderen Diensten, ist es meist eine schlechte Idee, das Passwort großflächig zu verteilen. Mobile Geräte haben manchmal die Eigenschaft verloren zu gehen oder gestohlen zu werden.
Weiterhin werden Dienste oder Geräte kompromitert oder unterstützen vielfach keine Zweifaktor-Authentisierung (2FA).
Hierbei hilft die
Generierung eines Personal Access Token kurz &ldquo;<code>PAT</code>&rdquo;.</p>
<p>Ein <code>PAT</code> hingegen sollte nur genau einem Dienst oder einem Gerät zugeordnet werden.
Das jeweilige <code>PAT</code> und kann dann bei Verlust oder Kompromittierung über den Hauptzugang gesperrt werden, so dass der Zugriff feingranular entzogen werden kann.
Vorteil: Das &ldquo;Hauptpasswort&rdquo; und die anderen <code>PAT</code> müssen nicht geändert werden und können bestehen bleiben.</p>

<h2 id="erstellung" data-numberify>Erstellung<a class="anchor ms-1" href="#erstellung"></a></h2>

<blockquote class="alert alert-info" role="alert">
    <p class="alert-heading fw-bold">
      <i class="fas fa-info-circle me-2"></i>Hinweis
    </p>
    <p>Zum Zeitpunkt des Artikel ist die Nextcloud-Version 31 aktuell.</p>
</blockquote>
<p>Zur Erstellung eines <code>PAT</code> melde dich als ersten Schritt in Deiner Nextcloud an.</p>
<p>Rechts oben sollten Deine Initialen oder Dein Profilbild stehen, dort bitte drauf klicken und es erscheint ein weiteres Pull-Down-Menü, wo du auf <code>Einstellungen</code> klickst.</p>
<p><picture><img class="img-fluid " alt="Einstellungen" src="https://www.rainerrose.de/images/posts/nextcloud-pat-erstellen/01-einstellungen.png?v=dd3ee05e268688d07868de57e9488d74" loading="lazy" width="400" height="384" />
</picture>

</p>
<p>Danach gibt es eine Übersicht von Deinem Profil und links eine Menüstruktur.</p>
<p>Mit Klick auf den Menüpunkt <code>Sicherheit</code> geht es jetzt weiter.
Unterhalb des Abschnittes, findest Du schon eine Auflistung der schon aktiven Zugriffe unterhalb von <code>Geräte &amp; Sitzungen</code>.</p>
<p><picture><img class="img-fluid " alt="Sicherheit" src="https://www.rainerrose.de/images/posts/nextcloud-pat-erstellen/02-sicherheit.png?v=f48c07b99bcfad2d35881e666c8b4ab5" loading="lazy" width="836" height="846" />
</picture>

</p>
<p>Am Ende der Tabelle (sofern sie existiert) gibt es eine Eingabemaske mit <code>App-Name</code> und dem Button &ldquo;Neues App-Passwort erstellen&rdquo;.</p>
<p>Trage hier bitte einen sinnvollen Namen ein, so dass Du das erstellte <code>PAT</code> später wieder zuordnen kannst.
Ein sprechender Name erleichtert Dir später die Suche, falls Du einem Gerät oder Dienst den Zugriff entziehen möchtest.</p>
<p><picture><img class="img-fluid " alt="PAT" src="https://www.rainerrose.de/images/posts/nextcloud-pat-erstellen/03-pat.png?v=00e25fcae9fcb172f39da9a4bf45d5c0" loading="lazy" width="431" height="312" />
</picture>

</p>

<blockquote class="alert alert-primary" role="alert">
    <p class="alert-heading fw-bold">
      <i class="fas fa-exclamation-circle me-2"></i>Wichtig
    </p>
    <p>Wenn Du auf das kleine Fenster wieder schließt, wird der Dialog <strong>geschlossen</strong>. Es gibt <strong>keine</strong> Möglichkeit, das Passwort nochmal anzuzeigen. Daher ist es wichtig, sich das Passwort an einem sicheren Ort zu notieren.
Hast Du dies vergessen, musst Du ein neues <code>PAT</code> anlegen.</p>
</blockquote>
<p>Das generierte <code>PAT</code> ist im Grunde nichts anderes ist, als ein extra Passwort.
Notiere Dir jetzt das generierte Passwort an einer sicheren Stelle, übertrage es z.B. in Deinen Passwort-Safe.
Das Passwort ist <strong>sofort aktiv</strong> und kann verwendet werden. Der gewohnte Benutzername bliebt hierbei gleich.</p>
<p>Wenn Du z.B. den Nextcloud-APP auf deinem Smartphone einrichten möchtest, hast Du jetzt die Chance mit Klick auf <code>QR-Code für mobile Apps anzeigen</code>
Dir einen QR-Code anzeigen zu lassen, den Du mit der Nextcloup-App abscannen kannst und die Tipparbeit und Tippfehler ersparst.</p>
<p><picture><img class="img-fluid " alt="PAT QR-Code" src="https://www.rainerrose.de/images/posts/nextcloud-pat-erstellen/03-pat-qr.png?v=94fb3686146adc3dd4cb7a809719890f" loading="lazy" width="404" height="466" />
</picture>

</p>
<p>Ist das Passwort in der Anwendung hinterlegt, kann der Dialog mit Klick auf das kleine <code>X</code>
geschlossen werden.</p>

<h2 id="löschen" data-numberify>Löschen<a class="anchor ms-1" href="#löschen"></a></h2>
<p>Das Löschen eines <code>PAT</code> wird auf der selben Seite in den Profil-Einstellungen vorgenommen werden.
Über die drei Punkte bekommst Du ein Pulldown-Menü.</p>
<p><picture><img class="img-fluid " alt="PAT löschen" src="https://www.rainerrose.de/images/posts/nextcloud-pat-erstellen/03-pat-loeschen.png?v=870b786167d2329178ac82a03929752c" loading="lazy" width="762" height="219" />
</picture>

</p>
<p>Hier kannst Du das <code>PAT</code> Widerrufen. Sofern der Client dies Unterstützt (z.B. die Smartphone-App) können via Klick auf
<code>Gerät löschen</code>
sogar die lokal vorgehaltenen Daten gelöscht werden.</p>]]></content:encoded>
    <enclosure url="https://www.rainerrose.de/images/posts/nextcloud-pat-erstellen/03-pat.png" length="34145" type="image/.png" />
    </item>
    
  </channel>
</rss>

