Published on by 0np, 935 words
On 16th December 2022 I've added my first bookmark to my local Linkding instance. It was an article in The Intercept titled "Beware of ”Watermarking“ If You Leak from Elon Musk’s Twitter" tagged with #forensics, #opsec, #security.
My 1.000th bookmark was saved yesterday on 21st March 2025. It's the homepage of Laura E. DeNardis becaue I want to read her book "The Internet in Everything". This one was tagged with #netpolitics and #must-try.
Linkding is a simple yet powerfull bookmark manager that you can selfhost. You can save your websites and tag them. You can setup auto tagging rules. For example, everything from Github is automatically tagged with #git. There is also an integration with the Internet Archive available that saves a copy of your bookmarks there for later reference, in case it will be a dead link in the future (and it will be dead eventually). It's also possible to have a local archive of the website you bookmark, where Linkding will save an HTML snapshot via a headless Chromium + uBlock setup. I haven't tried it myself, but it sounds neat!
There is only one feature I kinda miss in Linkding: The possibility to properly save PDF documents. While you can save a link to a PDF as a bookmark, Linkding will not be able to determine the title or description of the file. If the original link is dead in the future, you don't have a local copy (except with the Internet Archive integration enabled). I've stumbled across PdfDing that looks like it can fill this feature gap. Funnily enough, this link is saved as a bookmark in my Linkding instance since 16th January 2025 and tagged with #git (auto tagging ftw), #must-try, #selfhosted and #tool.
These tags and the search function in general really help me find old links months and years after the fact. I can find old essays I enjoyed, better research topics and reference back to my sources more easily, find previous camping grounds I've been to and much more.
I'm using Linkding so much, I'm currently developing an API client in Rust. I have so many ideas:
Thanks to the Linkding REST API, I can directly access everything I need for that. Writing this blog post makes me want to jump into my editor and continue coding again on this project.
I'm running Linkding on my homelab server at home. On the go I can access it with Tailscale. It took me quite a while to figure out how I can combine my selfhosted services with tailscale on the same machine. Luckily Tailscale supports Docker, so I can have a single Docker Compose file which directly attaches the different containers on a joint network and Tailscale exposes them automatically with tailscale serve
on my Tailnet.
This is my Docker Compose file for my Linkding setup (with inline explanation as comments):
services:
ts-linkding:
image: tailscale/tailscale:latest
container_name: tailscale_linkding
hostname: linkding
environment:
# Generate a new Auth Key here:
# https://login.tailscale.com/admin/settings/keys
- TS_AUTHKEY=tskey-auth-nonofyourbusiness-:)
# This is where the magic happens
- TS_SERVE_CONFIG=/config/tailscale.json
- TS_STATE_DIR=/var/lib/tailscale
volumes:
- ./volume/tailscale/state:/var/lib/tailscale
- ./volume/tailscale/config:/config
- /dev/net/tun:/dev/net/tun
cap_add:
- net_admin
- sys_module
restart: unless-stopped
linkding:
container_name: "linkding"
image: sissbruecker/linkding:latest
# This brings linkding on the same network as tailscale
network_mode: service:ts-linkding
depends_on:
- ts-linkding
# Ports don't need to be published as usual.
# Linkding listens on 127.0.0.1:9090 by default,
# and in the TS_SERVE_CONFIG you define which
# endpoint gets published via `tailscale serve`,
# see below.
#ports:
# - "9090:9090"
volumes:
- "./volume/linkding/data:/etc/linkding/data"
env_file:
- .env
restart: unless-stopped
# I don't remember why I've put this here. I think this was due
# to a bug on a depencendy of Linkding causing performance issues.
# This fixed it.
ulimits:
nproc: 65535
nofile:
soft: 20000
hard: 40000
The environment variable TS_SERVE_CONFIG
is what triggers the tailscale serve
accordingly. It's a JSON config and fairly straightforward:
{
"TCP": {
"443": {
"HTTPS": true
}
},
"Web": {
"${TS_CERT_DOMAIN}:443": {
"Handlers": {
"/": {
"Proxy": "http://127.0.0.1:9090"
}
}
}
},
"AllowFunnel": {
"${TS_CERT_DOMAIN}:443": false
}
}
Overall quite an easy and maintainable setup. The next 2.000 bookmark milestone will surely be there in no time!