linkding mit Docker Compose: Lesezeichen-Manager fürs Team selbst hosten
linkding ist ein schlanker, selbst gehosteter Lesezeichen-Manager mit SQLite, Tags, REST-API und Browser-Erweiterung. Die Anleitung zeigt Installation per Docker Compose, Teamfreigabe, API-Token, Archivierung ohne schweres Chromium-Image, getestetes Backup und Restore mit full_backup sowie ein Update von 1.46.2 auf 1.47.0 und typische Fehlerbilder.

Link-Sammlungen in Browser-Profilen, Wiki-Seiten und Chatverläufen sind in vielen Teams der Normalzustand. Wer Herstellerportale, Doku-Seiten und interne Werkzeuge zentral und durchsuchbar ablegen will, braucht keinen schweren Wissensspeicher. linkding ist ein bewusst schlanker, selbst gehosteter Lesezeichen-Manager: ein Container, eine SQLite-Datei, Tags, Volltextsuche, REST-API und offizielle Browser-Erweiterungen für Firefox und Chrome.
Diese Anleitung führt von der compose.yaml über Erstkonfiguration, API-Token und geteilte Lesezeichen bis zu Backup, Restore, Update und Deinstallation. Grundlage ist ein eigener Testlauf am 28.09.2026 mit linkding 1.47.0. Zwei Praxisfunde vorweg: Der offizielle Backup-Befehl sichert die Datei secretkey.txt nicht mit, nach einem Restore sind deshalb alle Browser-Sitzungen ungültig, API-Token funktionieren aber weiter. Und Adressen im lokalen Netz lädt linkding ab Werk nicht, weil ein SSRF-Schutz greift.
Was linkding ist und wo die Grenzen liegen
linkding ist ein Django-Projekt unter MIT-Lizenz. Laut GitHub-API (abgerufen am 28.09.2026) hat das Repository sissbruecker/linkding 11.239 Sterne, der letzte Push war am 18.09.2026 und das aktuelle Release ist v1.47.0 vom 13.09.2026. Das Projekt wird also aktiv gepflegt.
Der Funktionsumfang ist klar umrissen:
- Lesezeichen mit Titel, Beschreibung, Notizen und Tags, Suche mit eigener Syntax (zum Beispiel
#dokufür einen Tag) - Status für ungelesen, archiviert und geteilt, dazu Bundles als gespeicherte Filter
- Mehrere Benutzer, Verwaltung über die eingebaute Django-Administration unter
/admin - REST-API mit benannten Token für Skripte, Erweiterungen und Apps
- Automatischer Link auf einen Snapshot der Internet Archive Wayback Machine
- Optional HTML-Snapshots der Seiten auf dem eigenen Server (nur im Image
latest-plus) - Anmeldung per Formular, OIDC oder Authentifizierungs-Proxy wie Authelia
Die Grenzen sind ebenso klar. linkding kennt keine Gruppen- oder Ordnerrechte: Lesezeichen gehören genau einem Benutzer, und die Freigabe ist ein einfacher Schalter pro Lesezeichen, den die ganze Instanz sieht. Es gibt keine Volltextindizierung der archivierten Seiteninhalte und keine KI-Verschlagwortung. Wer das braucht, ist mit den Alternativen besser bedient: Karakeep bringt KI-Tagging und Volltextsuche mit, Linkwarden setzt auf Collections mit Teamrechten und automatische Archivierung als PDF und Screenshot. Beide benötigen dafür PostgreSQL oder Meilisearch und deutlich mehr Arbeitsspeicher. linkding ist die Wahl, wenn ein Team schnell eine gemeinsame, gut durchsuchbare Linkliste mit wenig Wartung will.
Voraussetzungen und Ressourcen
- Linux-Host mit Docker Engine und Compose-Plugin (Test: Docker 29 mit Compose v2)
- Rund 200 MB freier Arbeitsspeicher für das Standard-Image; im Test belegte der Container nach dem Start 160,5 MiB bei einem Limit von 512 MiB
- Speicherplatz: das Standard-Image ist komprimiert rund 142 MB groß, entpackt meldete Docker 586 MB; die Datenbank startet bei etwa 260 KB
- Für den Betrieb im Team ein DNS-Name und ein Reverse Proxy mit TLS
Image-Varianten und Architekturen
| Tag | Inhalt | Größe amd64 (komprimiert, Docker Hub) |
|---|---|---|
latest bzw. 1.47.0 | Grundfunktionen auf Debian-Basis | ca. 142 MB |
latest-alpine | wie latest, Alpine-Basis, laut Doku experimentell | ca. 51 MB |
latest-plus | zusätzlich Chromium und singlefile-cli für HTML-Snapshots | ca. 602 MB |
latest-plus-alpine | Plus-Variante auf Alpine, experimentell | ca. 401 MB |
Docker Hub listet für alle Varianten Manifeste für amd64, arm64 und arm/v7, ein Raspberry Pi ist also möglich. Die Archivierungs-Doku nennt die Plus-Variante allerdings als nicht verfügbar für ARMv7; dieser Widerspruch ist unbestätigt, wer auf ARMv7 Snapshots braucht, sollte das vorher prüfen.
Für die Plus-Variante nennt die Doku mindestens 1 GB RAM zusätzlich, weil bei jedem neuen Lesezeichen ein Headless-Chromium die Seite rendert. Dazu kommen der Speicherplatz für die Snapshots und die Unzuverlässigkeit bei Seiten mit Bot-Erkennung oder Login. Auf kleinen VMs ist deshalb das Standard-Image plus Browser-Archivierung (siehe unten) die bessere Wahl. Diese Anleitung verwendet das schlanke Image.
Vollständige compose.yaml und .env
Das Projekt liefert im Repository eine docker-compose.yml und eine .env.sample. Die folgende Datei baut darauf auf, pinnt aber die Version, bindet den Port nur an localhost und begrenzt den Speicher:
services:
linkding:
container_name: linkding
image: sissbruecker/linkding:1.47.0
ports:
# nur lokal, der Reverse Proxy spricht 127.0.0.1:9090 an
- "127.0.0.1:9090:9090"
volumes:
- ./data:/etc/linkding/data
env_file:
- .env
mem_limit: 512m
restart: unless-stopped
Einen eigenen healthcheck braucht es nicht: Das Image bringt bereits curl -f http://localhost:9090/health im Abstand von 30 Sekunden mit. Die .env daneben, ohne echte Geheimnisse:
# Erster Administrator, wird nur angelegt, wenn der Name noch nicht existiert
LD_SUPERUSER_NAME=admin
LD_SUPERUSER_PASSWORD=BitteLangesPasswortSetzen
# Öffentliche Adresse hinter dem Reverse Proxy, mit Protokoll, ohne Pfad
LD_CSRF_TRUSTED_ORIGINS=https://links.example.com
# Hintergrundaufgaben (Favicons, Vorschaubilder, Snapshots) aktiv lassen
LD_DISABLE_BACKGROUND_TASKS=False
# Optional: interne Hosts, deren Metadaten geladen werden dürfen
# LD_ALLOWED_INTERNAL_HOSTS=wiki.firma.local,192.168.10.0/24
Dateipfade und Parameter
./datawird nach/etc/linkding/datagemountet und enthältdb.sqlite3,tasks.sqlite3,secretkey.txtsowie die Ordnerassets,faviconsundpreviews.LD_SUPERUSER_PASSWORDleer lassen legt den Benutzer ohne nutzbares Passwort an, sinnvoll nur mit Authentifizierungs-Proxy.LD_CONTEXT_PATH(zum Beispiellinkding/, mit Schrägstrich am Ende) betreibt linkding unter einem Unterpfad.LD_DB_ENGINE=postgresund dieLD_DB_*-Variablen schalten auf PostgreSQL um; für Teams bis zu einigen Dutzend Personen reicht SQLite.LD_DISABLE_URL_VALIDATION=Trueerlaubt URLs, die Djangos Validator ablehnt, etwa Hostnamen mit Unterstrich.
Installation und Start
# Verzeichnis anlegen und Dateien ablegen
sudo mkdir -p /opt/linkding
cd /opt/linkding
# compose.yaml und .env wie oben anlegen, dann die .env schützen
sudo chmod 600 .env
# Image laden und starten
sudo docker compose up -d
# Start verfolgen
sudo docker compose logs -f
Beim ersten Start erzeugt linkding einen geheimen Schlüssel (INFO Generated secret key file), führt die Datenbank-Migrationen aus und legt den Administrator an (INFO Created initial superuser). Bei jedem weiteren Start erscheint stattdessen Skip creating initial superuser, user already exists; ein geändertes Passwort in der .env wird also nicht erneut gesetzt. Der Anwendungsserver uWSGI läuft als Benutzer www-data (UID 33), die Dateien im data-Ordner gehören deshalb UID 33.
Wer keine Zugangsdaten in der .env ablegen will, lässt die beiden LD_SUPERUSER_*-Zeilen weg und legt den Administrator interaktiv an:
sudo docker compose exec linkding python manage.py createsuperuser --username=admin --email=admin@example.com
Funktionsprüfung und Healthcheck
Der Endpunkt /health braucht keine Anmeldung und liefert die laufende Version. Im Test antwortete er etwa 18 Sekunden nach dem Start mit HTTP 200:
curl -s http://127.0.0.1:9090/health
{"version": "1.47.0", "status": "healthy"}
# Status aus Docker-Sicht
sudo docker inspect linkding --format '{{.State.Health.Status}}'
healthy
Die Anmeldung ist danach unter /login/ möglich. Ein falsches Passwort beantwortet linkding mit HTTP 401, eine erfolgreiche Anmeldung leitet mit 302 auf /bookmarks weiter. Ohne Sitzung führt jeder Aufruf von /bookmarks auf /login?next=/bookmarks.
Erstkonfiguration: Benutzer und Teamfreigabe
Weitere Konten legen Administratoren unter /admin im Bereich Users an. Normale Benutzer ohne Staff-Status kommen nicht in die Administration, im Test landete ein solches Konto beim Aufruf von /admin/ auf der Admin-Anmeldeseite. Für Skripte oder die Einrichtung per Automatisierung funktioniert auch der nicht interaktive Weg:
# zweiten Administrator ohne Rückfrage anlegen
sudo docker compose exec -e DJANGO_SUPERUSER_PASSWORD='SicheresPasswort' linkding \
python manage.py createsuperuser --noinput --username=admin2 --email=admin2@example.com
Superuser created successfully.
Damit Kolleginnen und Kollegen Lesezeichen sehen, muss jeder Benutzer die Freigabe in den allgemeinen Einstellungen unter Settings, General aktivieren (Profilfeld enable_sharing) und das einzelne Lesezeichen als geteilt markieren. Geteilte Einträge erscheinen für alle angemeldeten Benutzer unter /bookmarks/shared. Im Test war ein als shared angelegtes Lesezeichen dort für ein zweites Konto sichtbar, ein nicht geteiltes nicht. Eine zusätzliche öffentliche Freigabe ohne Anmeldung lässt sich getrennt einschalten; für interne Link-Sammlungen sollte sie aus bleiben.
REST-API und Browser-Erweiterung
API-Token erzeugt jeder Benutzer unter Settings, Integrations, Create API token. Jedes Token bekommt einen Namen, sinnvoll ist eines pro Gerät oder Skript. Das Token wird nur direkt nach dem Anlegen angezeigt; lädt man die Seite neu, ist es nicht mehr sichtbar. Ein Token hat die vollen Rechte seines Benutzers.
export TOKEN=hier-das-Token-einsetzen
# Lesezeichen mit Tags anlegen und für das Team freigeben
curl -s -H "Authorization: Token $TOKEN" -H 'Content-Type: application/json' \
-d '{"url":"https://linkding.link/","title":"linkding Doku","tag_names":["doku","selfhosting"],"shared":true}' \
https://links.example.com/api/bookmarks/
# Liste, archivierte Einträge und Suche nach Tag
curl -s -H "Authorization: Token $TOKEN" https://links.example.com/api/bookmarks/
curl -s -H "Authorization: Token $TOKEN" https://links.example.com/api/bookmarks/archived/
curl -s -H "Authorization: Token $TOKEN" "https://links.example.com/api/bookmarks/?q=%23doku"
# Prüfen, ob eine URL schon gespeichert ist
curl -s -H "Authorization: Token $TOKEN" "https://links.example.com/api/bookmarks/check/?url=https%3A%2F%2Flinkding.link%2F"
Der POST lieferte im Test HTTP 201 samt ID, Tags und automatisch gesetztem Wayback-Link. Ohne Titel füllt linkding Titel und Beschreibung aus den Metadaten der Seite. Ohne Token antwortet die API mit HTTP 401 und {"detail":"Authentication credentials were not provided."}, mit ungültigem Token mit 401 und {"detail":"Invalid token."}. Weitere Endpunkte gibt es für Tags, Bundles, Assets und das Benutzerprofil.
Die offizielle Erweiterung für Firefox und Chrome speichert die aktuelle Seite mit Tags und sucht in linkding direkt aus der Adressleiste. Einzutragen sind die Basis-URL der Instanz und ein eigenes API-Token. Für Archivierung ohne Plus-Image empfiehlt die Doku die Kombination mit der Erweiterung SingleFile: Diese lädt die Seite so, wie sie im Browser sichtbar ist, an /api/bookmarks/singlefile/ hoch (Datenfeld file, URL-Feld url). In der linkding-Erweiterung schaltet die Option Run Singlefile after adding new bookmark das automatische Archivieren ein. Beide Erweiterungen müssen aus demselben Store stammen, sonst finden sie sich nicht, was vor allem Edge-Nutzer betrifft.
Persistente Daten und Rechte
Alles Wichtige liegt im gemounteten data-Ordner. SQLite läuft im WAL-Modus, neben db.sqlite3 liegen deshalb -wal- und -shm-Dateien. Die Datenbank im laufenden Betrieb einfach zu kopieren ist laut Doku nicht transaktionssicher. Die Rechte richtet der Container beim Start selbst ein: Im Test gehörte eine vom Host mit UID 1000 zurückgespielte Datenbank nach dem Start wieder UID 33. Beim Löschen des Ordners vom Host aus braucht es deshalb sudo.
Netzwerkfreigabe, Reverse Proxy und TLS
linkding spricht selbst nur HTTP auf Port 9090. Für den Teambetrieb gehört ein Reverse Proxy mit TLS davor, der Port bleibt wie in der compose.yaml an 127.0.0.1 gebunden. Wichtig ist der CSRF-Schutz: Passt der Origin-Header nicht zum Host-Header, schlägt die Anmeldung fehl. Im Test mit abweichendem Origin lautete die Antwort HTTP 403 mit CSRF verification failed. Request aborted. Abhilfe schafft entweder LD_CSRF_TRUSTED_ORIGINS=https://links.example.com oder das Durchreichen der Header. Für Nginx nennt die Doku:
location / {
proxy_pass http://127.0.0.1:9090;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
Caddy und Apache reichen den Host-Header ohne Zusatzkonfiguration durch. Wer einen Authentifizierungs-Proxy wie Authelia vorschaltet, muss die API-Pfade daran vorbeiführen, sonst scheitern Erweiterung und Apps an der Weiterleitung auf die Login-Seite.
Backup und Restore
Der offizielle Weg ist der Befehl full_backup. Er erzeugt ein ZIP mit einer transaktionssicheren Kopie der Datenbank sowie den Ordnern assets, favicons und previews:
# Backup im Container erstellen
sudo docker compose exec linkding python manage.py full_backup /etc/linkding/data/backup.zip
# auf den Host kopieren und datieren
sudo docker cp linkding:/etc/linkding/data/backup.zip /srv/backup/linkding-$(date +%F).zip
Im Test meldete der Befehl Copied 65 of 65 pages... und Backup created at /etc/linkding/data/backup.zip. Da ohne Plus-Image keine Assets und noch keine Favicons vorhanden waren, enthielt das ZIP nur db.sqlite3. Auffällig: secretkey.txt ist nicht enthalten.
Für den Restore wird das ZIP in einen leeren data-Ordner entpackt:
cd /opt/linkding
sudo docker compose down
sudo mv data data.alt
sudo mkdir data
sudo unzip -q /srv/backup/linkding-2026-09-28.zip -d data
sudo docker compose up -d
Im Test wurde der data-Ordner vollständig gelöscht und aus dem ZIP wiederhergestellt. Danach meldete das Log No migrations to apply., alle drei Benutzerkonten, beide aktiven und das archivierte Lesezeichen waren da, und das vor dem Backup erzeugte API-Token funktionierte weiter, weil Token in der Datenbank liegen. Da linkding eine neue secretkey.txt erzeugte, waren aber alle bestehenden Browser-Sitzungen ungültig; die alte Sitzung landete auf der Login-Seite. Wer das vermeiden will, sichert secretkey.txt zusätzlich per Dateikopie. Für eine reine Lesezeichenliste bietet die Oberfläche zudem einen HTML-Export, der jedoch nur die eigenen Lesezeichen ohne Benutzer, Snapshots und Favicons enthält.
Updates und Rollback-Grenzen
Updates bestehen aus neuem Tag, Pull und Neustart; Migrationen laufen automatisch beim Start. Vorher gehört immer ein full_backup dazu:
sudo docker compose exec linkding python manage.py full_backup /etc/linkding/data/vor-update.zip
# in compose.yaml den Tag anheben, zum Beispiel 1.46.2 auf 1.47.0
sudo docker compose pull
sudo docker compose up -d
curl -s http://127.0.0.1:9090/health
Im Test wurde eine Instanz mit 1.46.2 und einem Lesezeichen auf 1.47.0 gehoben. /health meldete danach "version": "1.47.0", das Log No migrations to apply., und das Lesezeichen war unverändert vorhanden. Zwischen diesen beiden Versionen gab es also keine Schemaänderung. Bei größeren Sprüngen mit Migrationen ist ein Downgrade durch bloßes Zurücksetzen des Tags nicht vorgesehen; die Doku beschreibt keinen Downgrade-Weg. Der verlässliche Rollback ist der alte Tag zusammen mit dem vor dem Update erstellten Backup. latest statt fester Versionen macht Updates bequemer, aber unkontrollierbar.
Typische Fehler mit Diagnose und Lösung
| Symptom | Ursache | Lösung |
|---|---|---|
Login mit HTTP 403 CSRF verification failed. Request aborted. | Reverse Proxy schreibt den Host-Header um | LD_CSRF_TRUSTED_ORIGINS setzen oder proxy_set_header Host $host |
API antwortet 401 Authentication credentials were not provided. | Header fehlt oder falsches Schema | Header exakt als Authorization: Token <token> senden |
API antwortet 401 Invalid token. | Token gelöscht oder falsch kopiert | neues Token unter Integrations erzeugen |
HTTP 400 {"url":["Enter a valid URL."]} bei http://intranet_wiki/start | Djangos URL-Validator lehnt Unterstriche und Namen ohne TLD ab | LD_DISABLE_URL_VALIDATION=True |
Interne Adresse ohne Titel und Vorschaubild, Log: Blocked request to http://192.168.x.x/: Refusing to connect ... Use the LD_ALLOWED_INTERNAL_HOSTS option | SSRF-Schutz blockiert private Netze | betroffene Hosts oder Netze in LD_ALLOWED_INTERNAL_HOSTS freigeben |
Bind for 127.0.0.1:9090 failed: port is already allocated | Port von anderem Dienst belegt | freien Hostport wählen, Belegung mit ss -ltnp prüfen |
| Nach Restore sind alle Benutzer abgemeldet | secretkey.txt nicht im Backup, neuer Schlüssel erzeugt | Datei separat sichern oder einmal neu anmelden |
| Snapshots fehlen trotz Plus-Image oder brechen ab | Bot-Erkennung, Login-Seiten, Timeout | LD_SINGLEFILE_TIMEOUT_SEC erhöhen oder SingleFile im Browser nutzen |
Saubere Deinstallation
Achtung: Die folgenden Schritte löschen alle Lesezeichen, Benutzer, Token und Snapshots unwiderruflich. Vorher ein full_backup erstellen und außerhalb des Hosts ablegen.
cd /opt/linkding
sudo docker compose down -v --remove-orphans
# Dateien gehören UID 33, deshalb mit sudo löschen
sudo rm -rf /opt/linkding
# nur das eigene Image entfernen, keine globalen Prune-Befehle
sudo docker rmi sissbruecker/linkding:1.47.0
Anschließend sollte sudo docker ps -a --filter name=linkding nichts mehr ausgeben. Im Browser die Erweiterung entfernen und die API-Token gelten ohnehin nicht mehr, da die Datenbank gelöscht ist.
Testumfang
Getestet mit linkding 1.47.0 (Standard-Image) auf einem Linux-Host mit Docker: Start, /health, Superuser per Umgebungsvariable und per createsuperuser, Login inklusive falschem Passwort, API-Token, Anlegen und Abfragen per REST-API, 401 ohne und mit falschem Token, geteilte Lesezeichen für ein zweites Konto, CSRF-, URL-Validierungs-, SSRF- und Portkonflikt-Fehler, full_backup mit Restore nach Löschen sowie das Update von 1.46.2 auf 1.47.0. Nur aus der Doku übernommen: Plus-Image mit Chromium-Snapshots, Browser-Erweiterungen und SingleFile, Reverse Proxy mit TLS, OIDC, Authentifizierungs-Proxy und PostgreSQL.
Passende Anleitungen auf S-EDV
- Karakeep mit Docker installieren: Die Alternative mit KI-Tagging und Volltextsuche, wenn der Funktionsumfang wichtiger ist als der Ressourcenbedarf.
- Linkwarden mit Docker installieren: Collections mit Teamrechten und automatische Archivierung, dafür mit PostgreSQL.
- Nginx als Reverse Proxy mit TLS einrichten: Der passende Unterbau, um linkding verschlüsselt im Firmennetz bereitzustellen.
Quellen
- sissbruecker/linkding auf GitHub: Repository, Lizenz MIT, 11.239 Sterne laut GitHub-API, abgerufen am 28.09.2026.
- Release v1.47.0: veröffentlicht am 13.09.2026.
- linkding-Doku: Installation: Image-Varianten, Benutzeranlage, Reverse Proxy.
- linkding-Doku: Backups:
full_backup, Restore und alternative Wege. - linkding-Doku: Archivierung: Plus-Image, RAM-Bedarf, SingleFile-Integration.
- linkding-Doku: Optionen: alle
LD_*-Variablen einschließlichLD_ALLOWED_INTERNAL_HOSTS. - linkding-Doku: REST-API: Endpunkte und Token-Authentifizierung.
- sissbruecker/linkding auf Docker Hub: Tags, Architekturen und Imagegrößen.


