linkding bookmarking on NixOS and Hugo
This site’s hyperlinks section is now powered by linkding.
This weekend, I installed linkding on my NixOS server to replace Raindrop.io. While Raindrop is fine, it can be a bit slow when saving links, and owning my data is always a plus.
I also really enjoy linkding’s minimal design. It reminds me of Miniflux, which I use as my feed reader.
There’s no official iOS app, but the web interface is fast and Josh Dick’s iOS shortcut works very well for adding bookmarks from the share sheet.
Hugo Integration
Instead of having to create a new Markdown file for each bookmark, I can now easily retrieve that data from linkding.
To integrate with the blog, the bookmarks tagged #hyperlinks are fetched from my linkding instance’s REST API using a custom, but quite simple Hugo content adapter which has the correct access token.
links/_content.gotmpl
{{/* Fetch bookmarks from linkding */}}
{{/* Set using HUGO_PARAMS_LINKDING_URL, HUGO_PARAMS_LINKDING_TOKEN or in Hugo config */}}
{{/* Full endpoint URL with any filters, e.g. "https://linkding.example.com/api/bookmarks/?q=%23hyperlinks" */}}
{{ $URL := .Site.Params.linkding.url }}
{{ $TOKEN := .Site.Params.linkding.token }}
{{/* Get remote data. */}}
{{ $data := dict }}
{{ $headers := dict "Authorization" (print "Token " $TOKEN) }}
{{ $opts := (dict "headers" $headers) }}
{{ with try (resources.GetRemote $URL $opts) }}
{{ with .Err }}
{{ errorf "Unable to get remote resource %s: %s" $URL . }}
{{ else with .Value }}
{{ $data = . | transform.Unmarshal }}
{{ end }}
{{ else }}
{{ errorf "Unable to get remote resource %s" $URL }}
{{ end }}
{{/* Add pages and page resources. */}}
{{ range $data.results }}
{{/* Add page. */}}
{{ $content := dict "mediaType" "text/markdown" "value" .notes }}
{{ $dates := dict
"date" (time.AsTime .date_added)
"lastmod" (time.AsTime .date_modified)
}}
{{/* For tags, remove `hyperlink` */}}
{{ $tags := slice }}
{{ range .tag_names }}
{{ if ne . "hyperlinks" }}
{{/* Dashes to spaces */}}
{{ $tag := replace . "-" " " }}
{{ $tags = $tags | append $tag }}
{{ end }}
{{ end }}
{{ $params := dict
"href" .url
"tags" $tags
}}
{{ $page := dict
"path" .title
"kind" "page"
"title" .title
"dates" $dates
"params" $params
"content" $content
}}
{{ $.AddPage $page }}
{{ end }}
Note that the href
(as in .Params.href
) is a custom parameter used in my blog theme to link to the original URL.
I’m not using linkding’s public bookmark sharing feature, but I might in the future. That way, I wouldn’t need to use an access token, and the API would be read-only.
NixOS Configuration
linkding focuses on deployment with Docker, which is easy to integrate into NixOS. Here’s how I set it up:
linkding.nix
{ ... }:
let
port = 9090;
domain = "linkding.example.com";
in
{
virtualisation.oci-containers.containers.linkding = {
autoStart = true;
image = "sissbruecker/linkding@sha256:7becc0680a5e7e4012bd0bd4232ded6eb2cc86fcc527f961f17f9b586d8011dc";
volumes = [ "/var/lib/linkding/data:/etc/linkding/data" ];
ports = [ "${builtins.toString port}:${builtins.toString port}" ];
};
# Ensure our mount point exists
systemd.tmpfiles.settings."10-linkding" = {
"/var/lib/linkding/data" = {
d = {
mode = "0755";
user = "root";
group = "root";
};
};
};
# Reverse proxy
services.caddy = {
virtualHosts."${domain}".extraConfig = ''
reverse_proxy localhost:${builtins.toString port}
'';
};
}
NixOS will automatically set up the linkding container using Podman (instead of Docker) as a backend. It conveniently also generates a systemd service podman-linkding.service
.
I have also added a Caddy reverse proxy definition to serve the linkding instance on my domain.
As with any NixOS OCI container definition, the image will not update on rebuilds unless the version label changes.
I use the SHA256 digest of the latest-plus
tag to ensure that the image is reproducible.
This hash must be updated manually when a new version is released.
You can find an overview of the available tags on Docker Hub. To get the digest on the command line, you can use:
curl https://hub.docker.com/v2/namespaces/sissbruecker/repositories/linkding/tags/latest-plus | jq -r .digest
There’s currently an open PR to add linkding to nixpkgs, which would make this setup even easier (basically just services.linkding.enable = true;
, so fingers crossed).
Future work
I need to find a way to automatically trigger a Hugo rebuild when new bookmarks are added, but I haven’t found a convenient solution yet. If linkding adds webhooks at some point, that would work great (Issue #492).