Self-Host Linkding for Bookmarks
Deploy Linkding, the self-hosted bookmark manager, with Docker Compose, configure the browser extension, enable HTML snapshots, and organise bookmarks with tags.
Before you start
- ▸A Linux server or VM with at least 512 MB RAM and 2 GB free disk space
- ▸A user account with sudo privileges
- ▸Basic familiarity with the terminal and editing text files
- ▸A domain name or local hostname if you plan to enable HTTPS with a reverse proxy
Linkding is a lean, self-hosted bookmark manager with a clean UI, tag-based organisation, browser extensions for every major browser, and automatic HTML snapshots so pages you save never vanish. It ships as a single Docker image, making deployment straightforward even if you are new to containers. This guide walks through a production-ready Docker Compose deployment behind a local port, covers the browser extension setup, explains archival snapshots, and shows how to get the most out of tags.
Prerequisites and Architecture
Linkding runs as a Django application backed by SQLite (default) or PostgreSQL. For most personal or small-team use, SQLite is perfectly adequate and simplest to manage. The Docker image bundles everything: the app, static files, and singlefile for creating snapshots. You will expose it on a local port and optionally reverse-proxy it through Nginx or Caddy.
- Docker Engine 24+ and the
docker composeplugin (not the legacydocker-composebinary) - A directory on persistent storage for Linkding data
- Ports: 9090 (or any free port) on the host
Step 1 — Install Docker
Skip this step if Docker is already running on your host.
Debian / Ubuntu
sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Fedora / RHEL / Rocky
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
Arch
sudo pacman -S docker docker-compose
sudo systemctl enable --now docker
Add your user to the docker group so you do not need sudo for every command. Log out and back in for it to take effect.
sudo usermod -aG docker $USER
Step 2 — Create the Compose File
Create a dedicated directory and write the Compose definition.
mkdir -p ~/linkding && cd ~/linkding
cat > compose.yaml <<'EOF'
services:
linkding:
image: sissbruecker/linkding:latest
container_name: linkding
restart: unless-stopped
ports:
- "127.0.0.1:9090:9090"
volumes:
- ./data:/etc/linkding/data
environment:
- LD_SUPERUSER_NAME=admin
- LD_SUPERUSER_PASSWORD=changeme
- LD_ENABLE_AUTH_PROXY=False
- LD_REQUEST_TIMEOUT=60
- LD_SERVER_PORT=9090
EOF
Change LD_SUPERUSER_PASSWORD before you start. Binding to 127.0.0.1 means the port is not reachable from other hosts until you put a reverse proxy in front. If you want LAN access temporarily, change the port binding to 0.0.0.0:9090:9090, but secure it before exposing it to the internet.
Step 3 — Start Linkding
docker compose up -d
Watch the logs for a clean startup. You should see Django migrations complete and uWSGI start.
docker compose logs -f linkding
Open http://localhost:9090 in your browser and log in with the credentials you set above.
Step 4 — Secure It with a Reverse Proxy (Recommended)
For HTTPS — strongly recommended even on a home server — put Caddy or Nginx in front. Caddy is the easiest because it handles TLS automatically with Let's Encrypt or a local certificate.
Caddy (add to your Caddyfile)
bookmarks.example.com {
reverse_proxy localhost:9090
}
Nginx snippet
server {
listen 443 ssl;
server_name bookmarks.example.com;
ssl_certificate /etc/ssl/certs/bookmarks.crt;
ssl_certificate_key /etc/ssl/private/bookmarks.key;
location / {
proxy_pass http://127.0.0.1:9090;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Step 5 — Install the Browser Extension
Linkding has official extensions for Firefox and Chromium-based browsers. They let you save the current page with one click, pre-filling the title and URL.
- Firefox: Search "linkding extension" on addons.mozilla.org or install directly from the GitHub releases page.
- Chrome / Edge / Brave: Available on the Chrome Web Store — search "linkding extension".
After installation, open the extension options and fill in:
- Server URL:
https://bookmarks.example.com(orhttp://localhost:9090for local-only) - API token: Generate one in Linkding under Settings → Integrations → REST API → Generate token, then paste it into the extension.
Click Save and test by visiting any page and clicking the extension icon. The save dialog pre-fills title and URL; add tags before clicking Save Bookmark.
Step 6 — Using Tags Effectively
Tags are the primary navigation mechanism in Linkding. There are no folders, so a consistent tagging strategy matters. A few practices that work well:
- Use kebab-case for multi-word tags:
self-hosting,linux-kernel. Linkding treats tag names as case-insensitive. - Apply at least one topic tag (e.g.,
networking) and one type tag (e.g.,tutorial,reference,tool) to every bookmark. This makes filtering far more useful. - The sidebar tag cloud shows all tags with a count. Clicking a tag filters the list instantly.
- Use the search bar with the
#tagnamesyntax to combine tag filters with full-text search, for example:#docker composefinds bookmarks taggeddockerthat also contain the word "compose" in title or description.
Step 7 — Enable and Use Archival Snapshots
Linkding bundles singlefile, a tool that saves a complete self-contained HTML snapshot of a page at the time you bookmark it. Snapshots are stored inside the ./data volume.
Enable snapshotting globally in Settings → General → Enable automatic HTML snapshots. Once on, every new bookmark automatically triggers a background snapshot. For existing bookmarks you can trigger a snapshot manually from the bookmark's action menu (the three-dot icon).
To check snapshot storage usage:
du -sh ~/linkding/data/assets/
Snapshots can accumulate significant disk space over time. Monitor ~/linkding/data/ and prune old or unneeded bookmarks periodically.
Step 8 — Backup and Restore
Because Linkding uses SQLite by default, a backup is a simple file copy. Stop the container first to avoid a partial write, or use SQLite's online backup tool.
docker compose stop linkding
cp ~/linkding/data/db.sqlite3 ~/linkding/db-backup-$(date +%Y%m%d).sqlite3
docker compose start linkding
For automated daily backups, drop a systemd timer on the host:
sudo tee /etc/systemd/system/linkding-backup.service <<'EOF'
[Unit]
Description=Linkding SQLite backup
[Service]
Type=oneshot
User=%i
ExecStart=/bin/bash -c 'cp /home/youruser/linkding/data/db.sqlite3 /home/youruser/linkding/backups/db-$(date +\%%Y\%%m\%%d).sqlite3'
EOF
sudo tee /etc/systemd/system/linkding-backup.timer <<'EOF'
[Unit]
Description=Daily Linkding backup
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now linkding-backup.timer
Verification
Confirm the container is healthy and the API is responding:
docker compose ps
curl -s http://localhost:9090/health | python3 -m json.tool
A healthy response looks like {"status": "OK"}. If you get a connection refused, check that the container started with docker compose logs linkding.
Troubleshooting
- Port 9090 already in use: Change the left side of the port mapping in
compose.yaml, e.g.,9091:9090. - Snapshots not saving: The
singlefilebinary requires internet access from inside the container. Make sure your Docker host can reach the external URL being archived. Checkdocker compose logsforsinglefileerrors. - Extension cannot connect: Verify the API token is correct. If using HTTPS with a self-signed certificate, you must add a browser exception or trust the CA; the extension will otherwise refuse the connection.
- Superuser password forgotten: Run a management command inside the container to reset it.
docker exec -it linkding python manage.py changepassword adminFrequently asked questions
- Can I use PostgreSQL instead of SQLite?
- Yes. Set the LD_DB_ENGINE environment variable to postgres and supply LD_DB_HOST, LD_DB_NAME, LD_DB_USER, and LD_DB_PASSWORD. Add a postgres service to your Compose file and make linkding depend_on it.
- Does Linkding support importing bookmarks from a browser?
- Yes. Export your browser bookmarks as an HTML file (Netscape format, supported by all major browsers), then go to Settings → Import and upload the file. Tags are preserved if present.
- How do I update Linkding to a newer version?
- Pull the latest image and recreate the container. Run docker compose pull followed by docker compose up -d. Migrations run automatically on startup, so no manual database changes are needed.
- Is there a mobile-friendly interface?
- The web UI is responsive and works acceptably on mobile browsers. For iOS and Android you can add the site to your home screen as a PWA. There is no dedicated native app, but the REST API is available if you want to build one.
- Can multiple users share one Linkding instance?
- Yes. Create additional accounts in Admin → Users. Each user has a completely separate bookmark list and tag namespace. The admin account can create users but cannot see other users' bookmarks.
Related guides
Configure Prometheus Alertmanager
Configure Prometheus Alertmanager with routing trees, receivers, inhibition rules, grouping, Go templates, and PagerDuty/Slack on-call integrations.
Build an Intranet Server on Linux
Set up a complete small-office intranet on one Linux box: Nginx web server, dnsmasq local DNS, Samba file sharing, and a Wiki.js team wiki.
Build an nftables Firewall Script
Build a complete nftables firewall from scratch: tables, chains, sets, default-deny input policy, service allowlisting, and persistent systemd configuration.
Caddy as a Reverse Proxy
Set up Caddy as a reverse proxy with automatic HTTPS, load balancing, WebSocket passthrough, reusable snippets, and header control — no certbot required.