Linkwarden ist ein Bookmark Manager, welcher euch die Möglichkeit gibt eure Links für euch oder in Benutzergruppen freizugeben. Natürlich Open Source und schnell als SelfHosted System.
In dieser Anleitung zeige ich euch, wie ihr Linkwarden für Docker und Traefik installieren könnt.
| Datum | Änderungen |
|---|---|
| 31.10.2023 | Erstellung dieser Anleitung |
| 02.01.2026 | Update der Anleitung @christian |
1. Grundvoraussetzung
- Docker & Docker Compose v2 (Debian / Ubuntu)
- Traefik ab v3.6 mit CrowdSec installieren und konfigurieren
2. Ordner anlegen
Wir legen einen Ordner in der vorhandenen Struktur an:
mkdir -p /opt/containers/linkwarden
3. Docker Compose anlegen
Wir erstellen das Docker File:
nano /opt/containers/linkwarden/docker-compose.yml
und fügen folgenden Inhalt ein:
services:
postgres:
image: postgres:16-alpine
env_file: .env
restart: always
volumes:
- ./pgdata:/var/lib/postgresql/data
networks:
- default
linkwarden:
env_file: .env
environment:
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres
restart: always
image: ghcr.io/linkwarden/linkwarden:latest
volumes:
- ./data:/data/data
depends_on:
- postgres
- meilisearch
networks:
- proxy
- default
labels:
- "traefik.enable=true"
- "traefik.http.routers.linkwarden.entrypoints=websecure"
- "traefik.http.routers.linkwarden.rule=(Host(`linkwarden.euredomain.de`))"
- "traefik.http.routers.linkwarden.tls=true"
- "traefik.http.routers.linkwarden.tls.certresolver=http_resolver"
- "traefik.http.routers.linkwarden.service=linkwarden"
- "traefik.http.services.linkwarden.loadbalancer.server.port=3000"
- "traefik.docker.network=proxy"
- "traefik.http.routers.linkwarden.middlewares=default@file"
meilisearch:
image: getmeili/meilisearch:v1.12.8
restart: always
env_file:
- .env
volumes:
- ./meili_data:/meili_data
networks:
- default
networks:
proxy:
external: true
Code-Sprache: PHP (php)
Noch anzupassen:
- URL anpassen! – linkwarden.euredomain.de
3.1 Docker .env File anlegen
Wir erstellen das .env File um die Variablen zu definieren:
nano /opt/containers/linkwarden/.env
Und fügen folgenden Inhalt ein. Dies ist nur ein kleiner Teil aller Parameter. Die komplette Übersicht findet ihr in der offiziellen .env:
NEXTAUTH_URL=
NEXTAUTH_SECRET=
DATABASE_URL=
# Docker installation database settings
POSTGRES_PASSWORD=
# Additional Optional Settings
PAGINATION_TAKE_COUNT=
STORAGE_FOLDER=
AUTOSCROLL_TIMEOUT=
NEXT_PUBLIC_DISABLE_REGISTRATION=
NEXT_PUBLIC_CREDENTIALS_ENABLED=
DISABLE_NEW_SSO_USERS=
MAX_LINKS_PER_USER=
ARCHIVE_TAKE_COUNT=
BROWSER_TIMEOUT=
IGNORE_UNAUTHORIZED_CA=
IGNORE_HTTPS_ERRORS=
IGNORE_URL_SIZE_LIMIT=
NEXT_PUBLIC_DEMO=
NEXT_PUBLIC_DEMO_USERNAME=
NEXT_PUBLIC_DEMO_PASSWORD=
NEXT_PUBLIC_ADMIN=
NEXT_PUBLIC_MAX_FILE_BUFFER=
PDF_MAX_BUFFER=
SCREENSHOT_MAX_BUFFER=
READABILITY_MAX_BUFFER=
PREVIEW_MAX_BUFFER=
MONOLITH_MAX_BUFFER=
MONOLITH_CUSTOM_OPTIONS=
IMPORT_LIMIT=
PLAYWRIGHT_LAUNCH_OPTIONS_EXECUTABLE_PATH=
PLAYWRIGHT_WS_URL=
MAX_WORKERS=
DISABLE_PRESERVATION=
NEXT_PUBLIC_RSS_POLLING_INTERVAL_MINUTES=
RSS_SUBSCRIPTION_LIMIT_PER_USER=
TEXT_CONTENT_LIMIT=
SEARCH_FILTER_LIMIT=
INDEX_TAKE_COUNT=
MEILI_TIMEOUT=
# AI Settings
NEXT_PUBLIC_OLLAMA_ENDPOINT_URL=
OLLAMA_MODEL=
# https://ai-sdk.dev/providers/openai-compatible-providers
OPENAI_API_KEY=
OPENAI_MODEL=
# Optional: Set a custom OpenAI base URL and name (for third-party providers)
CUSTOM_OPENAI_BASE_URL=
CUSTOM_OPENAI_NAME=
# https://sdk.vercel.ai/providers/ai-sdk-providers/azure
AZURE_API_KEY=
AZURE_RESOURCE_NAME=
AZURE_MODEL=
# https://sdk.vercel.ai/providers/ai-sdk-providers/anthropic
ANTHROPIC_API_KEY=
ANTHROPIC_MODEL=
# https://github.com/OpenRouterTeam/ai-sdk-provider
OPENROUTER_API_KEY=
OPENROUTER_MODEL=
# https://ai-sdk.dev/providers/ai-sdk-providers/perplexity
PERPLEXITY_API_KEY=
PERPLEXITY_MODEL=
# MeiliSearch Settings
MEILI_HOST=http://meilisearch:7700
MEILI_MASTER_KEY=
# AWS S3 Settings
SPACES_KEY=
SPACES_SECRET=
SPACES_ENDPOINT=
SPACES_BUCKET_NAME=
SPACES_REGION=
SPACES_FORCE_PATH_STYLE=
# SMTP Settings
NEXT_PUBLIC_EMAIL_PROVIDER=
EMAIL_FROM=
EMAIL_SERVER=
BASE_URL=
# Proxy settings
PROXY=
PROXY_USERNAME=
PROXY_PASSWORD=
PROXY_BYPASS=
# PDF archive settings
PDF_MARGIN_TOP=
PDF_MARGIN_BOTTOM=Code-Sprache: PHP (php)
Damit wir die Änderungen nicht von Hand durchführen müssen, erstellen wir uns ein kleines Skript:
nano /opt/containers/linkwarden/config-skript.sh
Inhalt:
#!/bin/bash
# Dateinamen definieren
COMPOSE_FILE="docker-compose.yml"
ENV_FILE=".env"
# Überprüfen, ob Dateien existieren
if [[ ! -f "$COMPOSE_FILE" ]]; then
echo "Fehler: $COMPOSE_FILE nicht gefunden."
exit 1
fi
if [[ ! -f "$ENV_FILE" ]]; then
echo "Fehler: $ENV_FILE nicht gefunden."
exit 1
fi
echo "--- Starte Konfiguration ---"
# 1. Domain aus docker-compose.yml extrahieren
# Sucht nach der Traefik Rule Zeile und extrahiert den Text zwischen den Backticks (`...`)
DOMAIN=$(grep "traefik.http.routers.linkwarden.rule" "$COMPOSE_FILE" | sed -n 's/.*Host(`\([^`]*\)`).*/\1/p')
if [[ -z "$DOMAIN" ]]; then
echo "Fehler: Konnte Domain nicht aus $COMPOSE_FILE extrahieren."
exit 1
fi
FULL_URL="http://$DOMAIN"
echo "Domain gefunden: $DOMAIN"
echo "Setze NEXTAUTH_URL auf: $FULL_URL"
# 2. Sichere Kennwörter generieren (32 Zeichen, Hexadezimal)
# Alternativ kann 'openssl rand -base64 24' genutzt werden, hier nutzen wir hex für Kompatibilität
GEN_NEXTAUTH_SECRET=$(openssl rand -hex 32)
GEN_POSTGRES_PASSWORD=$(openssl rand -hex 16)
GEN_MEILI_MASTER_KEY=$(openssl rand -hex 32)
echo "Kennwörter generiert."
# 3. DATABASE_URL zusammenbauen
# Format: postgresql://USER:PASSWORD@HOST:PORT/DB_NAME
# User ist standardmäßig 'postgres', Host ist laut Anforderung 'postgres'
DB_USER="postgres"
DB_HOST="postgres"
DB_NAME="linkwarden"
DB_URL="postgresql://${DB_USER}:${GEN_POSTGRES_PASSWORD}@${DB_HOST}:5432/${DB_NAME}"
# 4. .env Datei bearbeiten
# Wir nutzen sed mit dem Trennzeichen |, um Konflikte mit / in URLs zu vermeiden
# NEXTAUTH_URL setzen
sed -i "s|^NEXTAUTH_URL=.*|NEXTAUTH_URL=$FULL_URL|" "$ENV_FILE"
# NEXTAUTH_SECRET setzen
sed -i "s|^NEXTAUTH_SECRET=.*|NEXTAUTH_SECRET=$GEN_NEXTAUTH_SECRET|" "$ENV_FILE"
# POSTGRES_PASSWORD setzen
sed -i "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$GEN_POSTGRES_PASSWORD|" "$ENV_FILE"
# MEILI_MASTER_KEY setzen
sed -i "s|^MEILI_MASTER_KEY=.*|MEILI_MASTER_KEY=$GEN_MEILI_MASTER_KEY|" "$ENV_FILE"
# DATABASE_URL setzen
sed -i "s|^DATABASE_URL=.*|DATABASE_URL=$DB_URL|" "$ENV_FILE"
echo "--- Konfiguration abgeschlossen ---"
echo "Die .env Datei wurde erfolgreich aktualisiert."Code-Sprache: PHP (php)
Nun starten wir das Skript noch:
chmod +x /opt/containers/linkwarden/config-skript.sh
cd /opt/containers/linkwarden/
./config-skript.sh
Jetzt ist unsere Konfig soweit fertig.
4. Linkwarden starten
Die Linkwarden -Instanz könnt ihr mit folgendem Befehl starten:
docker compose -f /opt/containers/linkwarden/docker-compose.yml up -d
5. Linkwarden einrichten
Benutzer einrichten und los legen. 🙂
Bei Fragen versuche ich gerne zu helfen, schreibt in die Kommentare, wie immer.
Viel Erfolg
Rene

Danke für die Anleitung!
Habe die Anleitung eben durchgeführt und bin auf zwei Punkte gestoßen:
1) ab Posgresql 18+ muss
das Volume in der docker-compose.yml
volumes:
– linkwarden-db:/var/lib/postgresql
sein, statt:
volumes:
– linkwarden-db:/var/lib/postgresql/data
2) Traefik zeigt schon auf Port 3000
Der Container läuft auch ohne die Ports zu öffnen
Würde das auskommentieren in der docker-compose.yml
# ports:
# – 3000:3000
Nochmals danke für diese Anleitung!
Hallo Rene,
hast Du eine Idee wie man das neue AI tagging feature mit dem Traefik Setup zusammen zum Laufen bekommt? https://docs.linkwarden.app/self-hosting/ai-worker
http://localhost:11434 scheint nicht vom Container erreichbar zu sein.
Moin und danke für die Anleitung.
Kurze Frage: hat jemand eine Idee bzw. einfach Lösung wie ich den internen Port (3000) ändern kann ohne mir den Container selbst zu bauen? Scheinbar der yarn-Port nicht per env-Variable anpassbar implementiert. Oder übersehe ich etwas?
Es könnte sein, dass Du das schon weissst, aber meine Vorgehensweise war:
1.: Mit dem Linkwarden-User die Datenbank linkwarden sichern.
2.: linkwarden volume sichern
3.: docker compose down
4:: /var/lib/docker/volume/linkwarden* löschen
5.: docker compose up
Hattest Du das Datenbank-Volume gelöscht?
Hallo Rene, vielen Dank für die top Anleitung. Linkwarden ist wirklich sehr praktisch.
Bei der Umsetzung auf meinem Server bin ich allerdings über ein Problem gestolpert. Solange ich die Parameter für das DB Passwort und das Secret nicht änder, läuft der Container problemlos. Sobald ich hier aber eigene Werte verwende, erhalte ich beim nächsten Start des Containers Authentifizierungsfehler von Linkwarden auf die DB und die Applikation startet nicht. Ein Rebuild des Containers und komplett neu initialisieren hilft nicht.
Hast du hier eventuell eine Idee?
Viele Grüße
Patrick