Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Docker 20.10.2026 · 11 min Lesezeit

wallabag mit Docker Compose selbst hosten: Read-it-later mit Backup und Restore

Diese Anleitung zeigt wallabag mit Docker Compose, MariaDB und Redis: Installation, Passwortwechsel, lokale Bilder, OAuth-Client und API, Backup mit echtem Restore nach Totalverlust sowie das Update von 2.6.13 auf 2.6.14 inklusive der Fehler, die dabei wirklich auftreten.

Mit KI erstellt – redaktionelle Prüfung ausstehend

WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

KI-generiertes Titelbild: wallabag mit Docker selbst hosten, Karten für Artikel speichern, Backup und Restore sowie Offline lesen

Ein Fachartikel, den Sie in Ruhe lesen wollen, ein Changelog für das nächste Wartungsfenster, eine Herstellerdoku, die morgen schon anders aussehen kann: Read-it-later-Dienste speichern solche Seiten als bereinigten Text. wallabag ist die quelloffene Variante dafür, MIT-lizenziert, mit Weboberfläche, Browser-Erweiterungen, Apps und einer REST-API. Die Inhalte liegen auf Ihrem eigenen Server statt bei einem Cloud-Anbieter. Diese Anleitung zeigt den Betrieb mit Docker Compose, MariaDB und Redis, das Speichern von Artikeln per API, ein Backup mit echtem Restore nach Totalverlust und das Update von 2.6.13 auf 2.6.14. Alle Ausgaben stammen aus einem eigenen Testlauf.

Voraussetzungen

wallabag ist eine Symfony-Anwendung mit PHP-FPM und Nginx im Container. Dazu kommen MariaDB und Redis. Für eine Instanz mit einigen Nutzern genügen:

  • Linux-Server oder VM mit 1 bis 2 CPU-Kernen und 2 GB RAM, x86_64 oder ARM (das Image gibt es laut Docker Hub für amd64, arm64 und armv7)
  • 5 GB freier Speicher für Images, Datenbank und gespeicherte Bilder, je nach Lesegewohnheit mehr
  • Docker Engine mit Compose-Plugin v2
  • eine eigene Subdomain mit Reverse Proxy und TLS, wenn Browser-Erweiterung und Apps von unterwegs zugreifen sollen
  • ein Speicherziel außerhalb des Servers für die Backups
EckdatenWert
Imagewallabag/wallabag:2.6.14 (aktuelles Release vom 07.10.2025, identisch mit latest)
Port80/TCP im Container, im Beispiel auf 8080 veröffentlicht
Volumes/var/www/wallabag/web/assets/images (Bilder), MariaDB unter /var/lib/mysql
Wichtige EnvSYMFONY__ENV__DOMAIN_NAME, SYMFONY__ENV__SECRET, SYMFONY__ENV__DATABASE_*, MYSQL_ROOT_PASSWORD
Standard-Loginwallabag / wallabag, sofort ändern

Schritt 1: Projektordner, .env und compose.yaml anlegen

Die Vorlage folgt dem Compose-Beispiel aus dem offiziellen Repository wallabag/docker, ist aber an drei Stellen geschärft: feste Versions-Tags statt latest, Geheimnisse in der .env und ein Healthcheck auf /api/info. Ohne eigenes SYMFONY__ENV__SECRET nutzt das Image einen fest eingebauten Standardwert aus seiner Vorlage, den jeder kennt.

mkdir -p /opt/wallabag && cd /opt/wallabag
cat > .env <<EOF
WALLABAG_TAG=2.6.14
DOMAIN_NAME=https://wallabag.example.de
DB_ROOT_PASSWORD=$(openssl rand -hex 16)
DB_PASSWORD=$(openssl rand -hex 16)
APP_SECRET=$(openssl rand -hex 24)
EOF
chmod 600 .env
services:
  wallabag:
    image: wallabag/wallabag:${WALLABAG_TAG}
    restart: unless-stopped
    environment:
      - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
      - SYMFONY__ENV__DATABASE_DRIVER=pdo_mysql
      - SYMFONY__ENV__DATABASE_HOST=db
      - SYMFONY__ENV__DATABASE_PORT=3306
      - SYMFONY__ENV__DATABASE_NAME=wallabag
      - SYMFONY__ENV__DATABASE_USER=wallabag
      - SYMFONY__ENV__DATABASE_PASSWORD=${DB_PASSWORD}
      - SYMFONY__ENV__DATABASE_CHARSET=utf8mb4
      - SYMFONY__ENV__DATABASE_TABLE_PREFIX=wallabag_
      - SYMFONY__ENV__SECRET=${APP_SECRET}
      - SYMFONY__ENV__LOCALE=de
      - SYMFONY__ENV__MAILER_DSN=smtp://127.0.0.1
      - SYMFONY__ENV__FROM_EMAIL=wallabag@example.com
      - SYMFONY__ENV__DOMAIN_NAME=${DOMAIN_NAME}
      - SYMFONY__ENV__SERVER_NAME=wallabag
      - SYMFONY__ENV__FOSUSER_REGISTRATION=false
    ports:
      - "8080:80"
    volumes:
      - ./images:/var/www/wallabag/web/assets/images
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost/api/info"]
      interval: 30s
      timeout: 5s
      start_period: 120s
  db:
    image: mariadb:11.8
    restart: unless-stopped
    environment:
      - MARIADB_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
    volumes:
      - ./db:/var/lib/mysql
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 20s
      timeout: 5s
  redis:
    image: redis:8-alpine
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 20s
      timeout: 3s

SYMFONY__ENV__DOMAIN_NAME muss die Adresse sein, unter der Nutzer wallabag aufrufen. Daraus baut die Anwendung unter anderem die URLs lokal gespeicherter Vorschaubilder. Läuft ein Reverse Proxy auf demselben Host, binden Sie den Port besser an 127.0.0.1:8080:80.

Verifizieren: docker compose config --quiet gibt nichts aus und endet mit Exit-Code 0.

Schritt 2: Starten, Bildordner berechtigen und Standardpasswort ändern

Legen Sie den Bildordner vor dem ersten Start an und übergeben Sie ihn dem Benutzer nobody, unter dem PHP-FPM im Container läuft. Legt Docker den Ordner selbst an, gehört er root. Im Test sah dann alles gesund aus, wallabag trug sogar lokale Bild-URLs in die Artikel ein, die Dateien fehlten aber. Ein Schreibversuch im Container zeigte den Grund: touch: /var/www/wallabag/web/assets/images/x: Permission denied.

mkdir -p images db
sudo chown 65534:65534 images
docker compose up -d
docker compose ps
curl -s localhost:8080/api/info

Beim ersten Start legt der Entrypoint mit dem Root-Passwort die Datenbank und den Benutzer wallabag an und installiert das Schema. Im Test war die Instanz rund 30 Sekunden nach dem Start healthy. Melden Sie sich mit wallabag / wallabag an und ändern Sie das Passwort sofort, entweder unter Konfiguration, Benutzer-Einstellungen oder in der Konsole:

docker compose exec wallabag su -s /bin/sh nobody -c \
  "php bin/console fos:user:change-password wallabag 'NEUES-PASSWORT' --env=prod"
# Changed password for user wallabag

Die öffentliche Registrierung ist mit SYMFONY__ENV__FOSUSER_REGISTRATION=false abgeschaltet; /api/info meldet entsprechend "allowed_registration":false. Weitere Konten legen Sie als Admin unter Benutzerverwaltung oder mit fos:user:create an.

Verifizieren: docker compose ps zeigt alle drei Dienste als healthy, /api/info liefert {"appname":"wallabag","version":"2.6.14","allowed_registration":false}. Der Login mit dem alten Passwort wallabag führt zurück auf /login, der mit dem neuen auf die Startseite.

Schritt 3: Bilder lokal speichern

Standardmäßig verlinkt wallabag die Bilder eines Artikels nur auf der Originalseite. Verschwindet die Quelle, fehlen sie. Als Admin schalten Sie unter Interne Einstellungen im Reiter Verschiedenes den Wert Bilder lokal herunterladen auf 1. In der Datenbank heißt die Einstellung download_images_enabled, frisch installiert steht sie auf 0.

Interne Einstellungen von wallabag im Reiter Verschiedenes mit dem Wert 1 bei Bilder lokal herunterladen
Interne Einstellungen, Reiter Verschiedenes: Der Wert 1 bei Bilder lokal herunterladen speichert Bilder auf dem eigenen Server.

Gespeicherte Bilder landen unter images/ in Unterordnern nach Hash. Bestehende Artikel holen ihre Bilder nach, wenn Sie sie neu laden (Symbol Neu laden in der Artikelansicht oder API-Aufruf PATCH /api/entries/ID/reload.json).

Verifizieren: Speichern Sie einen Artikel mit Abbildungen; danach liefert find images -type f | wc -l einen Wert größer null. Im Test waren es nach drei Artikeln 19 Dateien.

Schritt 4: API-Client anlegen und Artikel per API speichern

Browser-Erweiterungen, Apps und eigene Skripte sprechen die API über OAuth2 an. Der naheliegende Konsolenbefehl fos:oauth-server:create-client bricht in 2.6.13 mit Too few arguments to function Wallabag\ApiBundle\Entity\Client::__construct(), 0 passed ab. Legen Sie den Client daher in der Weboberfläche unter API-Client-Verwaltung, Neuen Client erstellen an. Name genügt, die Weiterleitungs-URI bleibt für Skripte leer. Die Seite zeigt danach Client-ID und Client-Secret.

API-Client-Verwaltung von wallabag mit einem angelegten Client, Client-ID und Client-Secret sind unkenntlich gemacht
API-Client-Verwaltung mit angelegtem Client; ID und Secret sind hier unkenntlich gemacht.
B=https://wallabag.example.de
curl -s -X POST $B/oauth/v2/token -d grant_type=password \
  -d client_id=CLIENT_ID -d client_secret=CLIENT_SECRET \
  -d username=wallabag --data-urlencode password='NEUES-PASSWORT'
# {"access_token":"...","expires_in":3600,"token_type":"bearer",...}

curl -s -H "Authorization: Bearer ACCESS_TOKEN" -X POST $B/api/entries.json \
  --data-urlencode "url=https://www.debian.org/News/2025/20250809" -d tags=changelog

Ein Access-Token gilt eine Stunde, danach holen Sie mit dem Refresh-Token ein neues. Ein ungültiges Token beantwortet die API mit HTTP 401 und "error":"invalid_grant". Die Antwort auf den POST enthält Titel, Domain, Lesezeit und Tags des gespeicherten Artikels; in der Oberfläche erscheint er sofort unter Ungelesen.

Liste der ungelesenen Einträge in wallabag mit drei per API gespeicherten Artikeln und Tags
Die per API gespeicherten Artikel erscheinen mit Tags und Lesezeit unter Ungelesen.

Verifizieren: curl -s -H "Authorization: Bearer ACCESS_TOKEN" $B/api/entries.json nennt im Feld total die Zahl Ihrer Artikel, der neue Eintrag steht in _embedded.items.

Schritt 5: Backup von Datenbank, Bildern und Konfiguration

Die offizielle Doku nennt drei Bestandteile: Datenbank, Bilder unter web/assets/images und die Grundeinstellungen. Im Container entsteht parameters.yml bei jedem Start aus den Umgebungsvariablen, daher sichern Sie stattdessen .env und compose.yaml. Der Dump läuft im laufenden Betrieb konsistent mit --single-transaction:

cd /opt/wallabag && set -a && . ./.env && set +a
mkdir -p backup
docker compose exec -T db mariadb-dump -uroot -p"$DB_ROOT_PASSWORD" \
  --single-transaction --routines --databases wallabag > backup/wallabag-db.sql
tar czf backup/wallabag-images.tgz images
cp .env compose.yaml backup/

Kopieren Sie den Ordner backup anschließend auf ein externes Ziel, etwa per restic oder rsync, und planen Sie den Ablauf per Cron. Der Dump enthält bewusst nur die Datenbank wallabag, nicht die MariaDB-Benutzerverwaltung. Das hat Folgen für den Restore, siehe Schritt 6.

Verifizieren: grep -c "INSERT INTO" backup/wallabag-db.sql liefert einen Wert größer null, tar tzf backup/wallabag-images.tgz | head listet Bilddateien. Im Test waren Dump und Bildarchiv bei drei Artikeln 68 KB und 40 KB groß.

Schritt 6: Restore nach Totalverlust

Im Test lief der Ernstfall vollständig: Marker-Tabelle angelegt, gesichert, Stack mit docker compose down -v entfernt, Datenbank- und Bildordner gelöscht. Nach dem Neustart der leeren Datenbank meldete MariaDB Unknown database 'wallabag'. Wer jetzt nur den Dump einspielt, bekommt eine Neustartschleife: wallabag erkennt die vorhandene Datenbank, überspringt mit WARN: MySQL database is already configured die Einrichtung samt Benutzer und scheitert dann an SQLSTATE[HY000] [1045] Access denied for user 'wallabag'. Der Benutzer gehört eben nicht zum Dump. Legen Sie ihn deshalb vor dem Import aus den Werten der .env an:

cd /opt/wallabag && set -a && . ./.env && set +a
docker compose down
sudo rm -rf db images && mkdir db
docker compose up -d db     # warten, bis "healthy"
docker compose exec -T db mariadb -uroot -p"$DB_ROOT_PASSWORD" -e \
  "CREATE USER IF NOT EXISTS 'wallabag'@'%' IDENTIFIED BY '$DB_PASSWORD';
   GRANT ALL PRIVILEGES ON wallabag.* TO 'wallabag'@'%';"
docker compose exec -T db mariadb -uroot -p"$DB_ROOT_PASSWORD" < backup/wallabag-db.sql
sudo tar xzf backup/wallabag-images.tgz
docker compose up -d

Das Bildarchiv behält beim Entpacken als root die Eigentümer nobody:nogroup. Einen kompletten Dump der Benutzerverwaltung mit --system=users sollten Sie nicht einspielen: Im Test überschrieb er das Passwort des internen Benutzers healthcheck der neuen MariaDB-Instanz, der Container blieb danach mit Access denied for user 'healthcheck'@'localhost' dauerhaft unhealthy.

Artikelansicht in wallabag nach dem Restore mit Titel, Lesezeit, Quelle und Tag
Nach dem Restore: gespeicherter Artikel mit Tag und Lesezeit, unverändert wie vor dem Totalverlust.

Verifizieren: Zehn Sekunden nach dem Start antwortete /api/info im Test wieder mit 200. Marker-Tabelle, alle drei Artikel samt Tags und alle 19 Bilder waren zurück, ein vorher ausgestelltes API-Token funktionierte weiter (HTTP 200), und ein lokal gespeichertes Bild lieferte über /assets/images/... HTTP 200.

Schritt 7: Update auf eine neue Version

Der Test startete bewusst mit 2.6.13 und zog die Instanz nach Backup und Restore auf 2.6.14. Sichern Sie vor jedem Update wie in Schritt 5, setzen Sie dann den Tag in der .env und starten Sie neu. Das Image führt Datenbankmigrationen nicht automatisch aus, rufen Sie sie daher nach dem Start auf:

sed -i 's/^WALLABAG_TAG=.*/WALLABAG_TAG=2.6.14/' .env
docker compose pull wallabag && docker compose up -d
docker compose exec wallabag su -s /bin/sh nobody -c \
  "php bin/console doctrine:migrations:migrate --env=prod --no-interaction"

Der Container wurde neu erstellt, der Migrationsbefehl lief ohne Fehler durch. Artikel, Marker und API-Token blieben erhalten. Angemeldete Browser-Sitzungen waren danach beendet und leiteten auf /login um; Nutzer melden sich einfach neu an. Lesen Sie vor größeren Sprüngen die Upgrade-Hinweise in der offiziellen Doku.

Verifizieren: curl -s localhost:8080/api/info meldet "version":"2.6.14", docker compose ps zeigt das Image wallabag/wallabag:2.6.14 als healthy.

Troubleshooting

SymptomUrsache und Lösung
Artikel zeigen lokale Bild-URLs, die Bilder fehlenBildordner gehört root. chown 65534:65534 images, Artikel neu laden.
Neustartschleife, Log zeigt [1045] Access denied for user 'wallabag'Datenbank vorhanden, Benutzer fehlt oder Passwort weicht von der .env ab. Benutzer wie in Schritt 6 anlegen.
fos:oauth-server:create-client endet mit Too few argumentsKonsolenbefehl scheiterte im Test mit 2.6.13. Client in der Weboberfläche anlegen.
API antwortet 401 invalid_grantToken abgelaufen (3600 Sekunden) oder falsch. Mit Refresh-Token oder Passwort neues Token holen.
MariaDB nach Restore unhealthy, Access denied for user 'healthcheck'Systembenutzer aus einem anderen Server eingespielt. Nur die Datenbank wallabag wiederherstellen.
Log meldet bei jedem Start WARN: MySQL database is already configuredHinweis, kein Fehler. Der Entrypoint empfiehlt, MYSQL_ROOT_PASSWORD nach der Ersteinrichtung aus dem wallabag-Dienst zu entfernen.

Häufige Fragen

Reicht SQLite statt MariaDB?

Für eine Person ja. Ohne Datenbankvariablen nutzt das Image SQLite unter /var/www/wallabag/data; dann sichern Sie diesen Ordner im gestoppten Zustand. Für mehrere Nutzer und Importe größerer Bestände ist MariaDB robuster.

Wozu dient der Redis-Container?

Redis nimmt asynchrone Importaufgaben auf, etwa aus Pocket, Instapaper oder einem wallabag-Export. Den Worker starten Sie laut Image-Doku mit dem Befehl import <typ> in einem zusätzlichen Container mit denselben Variablen.

Kann ich wallabag direkt ins Internet stellen?

Nur hinter einem Reverse Proxy mit TLS, mit geändertem Standardpasswort und abgeschalteter Registrierung. Der Proxy muss X-Forwarded-Proto https setzen, und SYMFONY__ENV__DOMAIN_NAME muss die öffentliche HTTPS-Adresse enthalten.

Wie komme ich an meine Artikel, wenn ich wallabag aufgebe?

Über den Export in der Oberfläche (EPUB, PDF, JSON, CSV, TXT, XML) oder mit dem Konsolenbefehl wallabag:export. Der JSON-Export lässt sich in eine andere wallabag-Instanz importieren.

Fazit

wallabag ist schnell aufgesetzt und im Alltag unauffällig. Die Stolpersteine liegen nicht im Start, sondern im Betrieb: Bildordner mit falschem Eigentümer, ein defekter Konsolenbefehl für API-Clients und ein Restore, der ohne neu angelegten Datenbankbenutzer in einer Neustartschleife endet. Mit den drei Korrekturen aus dieser Anleitung überstand die Testinstanz Totalverlust und Update ohne Datenverlust. Für Teams, die Fachartikel, Doku-Stände und Changelogs dauerhaft und durchsuchbar ablegen wollen, ist das ein solider, schlanker Baustein.

Weiterführende Anleitungen und Quellen

wallabagDocker ComposeRead-it-laterSelfhostingMariaDBBackupREST-API