A Django bookmark manager with tags, notes, full-text search, browser extensions and an optional local HTML archive of every page you save. It replaces Raindrop.io, Pinboard and the browser bookmarks folder nobody can navigate anymore.
Our verdict on linkding#
linkding is small, fast and boring, and that is the entire recommendation. It is a Django app on SQLite in one container on port 9090; adding a bookmark is instant, search is instant, and there is no AI, no recommendation engine and no social layer to configure or disable. It is exactly right for someone who wants Pinboard's model with their own disk under it, who intends to still be running the same install in five years, and who will actually use the Firefox or Chrome extension. Two decisions matter. Pick the image tag deliberately: latest is the lean one, latest-plus bundles Chromium for local HTML snapshots and the docs are explicit that it costs significantly more memory and disk. And back up with the full_backup management command, because the docs say plainly that copying db.sqlite3 is not transaction safe and can leave you a corrupted database. If you want AI tagging, PDF ingestion and full-text search across archived content, that is Karakeep, and Karakeep costs you three containers.
Who linkding is for#
Choose it if
- Someone leaving Pinboard or Raindrop who wants the same tag-and-search workflow with no subscription and no vendor
- A person with 5,000 bookmarks who wants search results before they finish typing, on a box that also runs six other things
- Anyone who wants offline copies of pages that keep disappearing, using the
-plusimage or the SingleFile browser extension - Users of read-it-later and feed tools, since Miniflux and several mobile clients can save straight into it over the REST API
Look elsewhere if
- You want automatic tagging, summarization or semantic search; linkding does exactly what you type and nothing more
- You want a visual grid of cover images as the primary view; previews exist but this is a list-first, text-first tool
- You need per-user shared collections with fine-grained permissions beyond sharing bookmarks with other users and guests
What running it actually looks like#
One container on port 9090 with a single volume at /etc/linkding/data. Create the first account either by setting LD_SUPERUSER_NAME before the first start or by running docker exec -it linkding python manage.py createsuperuser afterwards; if you are using OIDC, do not rely on LD_SUPERUSER_NAME alone, because OIDC matches on email address rather than username and you can end up locked into a second, powerless account. Choose the tag at this point: latest for the lean build, latest-plus if you want server-side HTML snapshots, and there are experimental Alpine variants. Behind nginx you will hit a 403 CSRF failure on login unless the proxy forwards the right headers or you set LD_CSRF_TRUSTED_ORIGINS to the external URL. Upgrades are a pull and a restart; migrations apply on boot.
Resource profile#
- Memory
- 90 MB idle, around 150 MB in ordinary use. Plain Django under gunicorn with SQLite. The `-plus` image is a different animal: it bundles Chromium for HTML snapshots and the docs warn it needs substantially more runtime memory, realistically several hundred megabytes more while a snapshot is being taken.
- CPU and acceleration
- Idle almost always. Background tasks fetch titles, favicons and preview images per bookmark; on the `-plus` image each snapshot spins up a headless Chromium, which is the only thing here that will make a small VPS notice.
- Storage growth
- The database stays tiny, tens of megabytes for tens of thousands of bookmarks. Snapshots are the growth: each SingleFile HTML archive with inlined assets runs from a few hundred kilobytes to several megabytes, so archiving everything means gigabytes over a few years.
- Operational load
- 1 of 5, Set and forget. One container, one data directory, upgrades are a pull and the Django migrations run themselves on boot. There is a documented `full_backup` management command that produces a single consistent zip, so the whole operational surface is one cron line.
Figures describe a small single-household install and are the working assumptions behind our stack planner. Your numbers will differ with library size, user count and hardware. See methodology.
Data and backup surface#
Everything lives under /etc/linkding/data: db.sqlite3, assets (the HTML snapshots), favicons and previews. The documented method is docker exec -it linkding python manage.py full_backup /etc/linkding/data/backup.zip, which produces a single consistent archive of the lot; the docs explicitly warn against just copying the SQLite file because it is not transaction safe and can yield a corrupted database. Favicons and preview images are regenerable and safe to exclude if you want a smaller archive; assets is not, because those snapshots are the only copy of pages that may already be gone. If you moved to PostgreSQL with LD_DB_ENGINE, dump the database and still keep the assets directory.
Derived backup shape
SQLite backup. sqlite3 <db> ".backup out.db" while running, or stop the container and copy. A live cp of a WAL-mode SQLite file can restore corrupt.
Traps and surprises#
Copying db.sqlite3 is documented as unsafe
The backup documentation says directly that copying the database file is not transaction safe and may result in a corrupted database. A snapshot taken while a background task is writing favicons is exactly the case that bites. Use manage.py full_backup, which zips the database and the data folder consistently, and verify the zip opens before you trust the cron job.
403 CSRF verification failed behind a reverse proxy
linkding checks that the Origin header matches Host. Terminate TLS at nginx without forwarding X-Forwarded-Proto and Host correctly and every login POST returns a 403 while the page itself loads fine. Fix the proxy headers, or set LD_CSRF_TRUSTED_ORIGINS to the full external origin including scheme. This is the number one first-day support question.
latest-plus is a different resource profile
The -plus tag bundles Chromium for SingleFile archiving. The docs state it needs more runtime memory to run Chromium and more disk for the snapshots. On a 512 MB VPS the plain latest image is comfortable and -plus is not. If you want archives without the memory cost, use the SingleFile browser extension and upload snapshots from the client instead.
Auth proxies break the REST API
If you put Authelia or similar in front, it intercepts API requests before linkding sees them, and every mobile client and browser extension that authenticates with an API token starts failing. The documented fix is to configure the proxy to let external API requests bypass authentication, since linkding validates its own tokens on those routes.
URL validation rejects things you want to save
Domains containing underscores, non-standard protocols such as chrome://, and hosts with no TLD are rejected by Django's validator, which is surprising when you are trying to bookmark an internal service at http://media_server:8096. Set LD_DISABLE_URL_VALIDATION if your network naming does not fit, rather than renaming your hosts.
Specifications#
- Category
- Feeds, bookmarks and read-it-later
- Licence
- MIT (Permissive)
- Written in
- Python / Django
- First release
- 2019
- Maturity
- Mature
- Datastore
- SQLite by default; PostgreSQL supported via LD_DB_ENGINE
- Default ports
9090- Ships as
- docker, python from source, unraid app
- arm64 builds
- Yes
- Identity
- Native OIDC. `LD_ENABLE_OIDC` turns on OpenID Connect and `LD_ENABLE_AUTH_PROXY` plus `LD_AUTH_PROXY_USERNAME_HEADER` covers Authelia-style header auth; note OIDC matches users by email address, not username.
- Replaces
- Raindrop.io, Pinboard, Pocket, del.icio.us
- Project site
- linkding.link
- Source
- github.com/sissbruecker/linkding
- Documentation
- linkding.link/installation
Alternatives to linkding#
Everything else in feeds, bookmarks and read-it-later, closest in operational weight first.
| Tool | Ops load | Idle RAM | Licence |
|---|---|---|---|
| FreshRSS | 2, Light | 90 MB | AGPL-3.0 |
| Karakeep | 3, Moderate | 700 MB | AGPL-3.0 |
| Miniflux | 3, Moderate | 40 MB | Apache-2.0 |
| wallabag | 3, Moderate | 180 MB | MIT |
Where linkding comes up elsewhere#
Head to head
Guides
Research
Reference
Sources#
Facts on this page were checked against the project's own material. Where a number is our own estimate rather than a published figure, the text says so.
- Installation: image, port 9090, data volume, tags including latest-plus
- Options reference: LD_DB_ENGINE, LD_ENABLE_OIDC, auth proxy, CSRF origins
- Backups: full_backup command and the warning against copying the SQLite file
- Troubleshooting: CSRF, auth proxy and API, archiving, URL validation
- Project README and release history
Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.