Bookmark management with Linkding

{
  config,
  lib,
  ...
}:
let
  cfg = config.mjm.linkding;
in
{
  options.mjm.linkding = {
    enable = lib.mkEnableOption "linkding";
  };

  config = lib.mkIf cfg.enable {
    <<config>>
  };

  _class = "nixos";
}

Linkding is a nice little self-hosted bookmarks manager, like a self-hosted del.icio.us or Pinboard.

Service settings

mjm.services.linkding = {
  http = {
    inherit (config.services.linkding) port;
    health.path = "/health";

    ingress = {
      subdomain = "links";
      authMode = "oidc";
      oidc = {
        name = "Linkding";
        clientId = "FADn3qU4zXW25fR2QbIF66c3uA1AvhtZW9SYyjPvUFGkhNTYwPbeVOkX1Yie8Bt7";
        clientSecret = "$argon2id$v=19$m=65536,t=3,p=4$OSd95Hl3sb+sCwkMak5Jyg$6MpUtp0s7wXMIHD7SQWNQALRT/4eOEGZ9KdobyOb8Kw";
        redirectUris = [ "https://links.midna.dev/oidc/callback/" ];
        tokenEndpointAuthMethod = "client_secret_post";
      };
    };
  };

  postgresql.enable = true;
  secrets.enable = true;
  secrets.templates.env = {
    text = ''
      OIDC_RP_CLIENT_SECRET=''${secret_linkding_managed_oidc_client_secret}
    '';
    secrets = [ "managed/oidc_client_secret" ];
  };
};

Linkding is exposed externally as https://links.midna.dev. It has a dedicated health check endpoint that Consul uses to monitor the service. It's using OpenID Connect for authentication with Authelia.

Linkding also uses PostgreSQL and Vault secrets, the latter to get the client secret to use for OIDC.

Config

services.linkding.enable = true;
services.linkding.port = 8080;

Linkding of course needs to be enabled, and the default port needs to be changed since 9090 is already used by the Prometheus tunnel for Alloy.

services.linkding.database.type = "postgres";
services.linkding.database.host = "/run/postgresql";

Linkding will use SQLite by default, but my install has been using PostgreSQL for a while.

services.linkding.settings = {
  LD_SUPERUSER_NAME = "mjm";
  LD_ENABLE_OIDC = "True";
  OIDC_OP_AUTHORIZATION_ENDPOINT = "https://auth.midna.dev/api/oidc/authorization";
  OIDC_OP_TOKEN_ENDPOINT = "https://auth.midna.dev/api/oidc/token";
  OIDC_OP_USER_ENDPOINT = "https://auth.midna.dev/api/oidc/userinfo";
  OIDC_OP_JWKS_ENDPOINT = "https://auth.midna.dev/jwks.json";
  OIDC_RP_CLIENT_ID = config.mjm.services.linkding.http.ingress.oidc.clientId;
  OIDC_USERNAME_CLAIM = "preferred_username";
};
services.linkding.environmentFile = "/run/linkding-secrets/env";
systemd.services.linkding-setup = {
  requires = [ "linkding-secrets.service" ];
  after = [ "linkding-secrets.service" ];
};

Linkding needs a little configuration for authentication. I'm using OpenID Connect for this. I need to use an environment file to pass the client secret to it from Vault, so that uses the template above, and service dependencies are added so that the environment file exists before any of the services that need it are started.

Proxy Information
Original URL
gemini://midna.dev/homelab/services/linkding/
Status Code
Success (20)
Meta
text/gemini;lang=en-US
Capsule Response Time
19.225621 milliseconds
Gemini-to-HTML Time
0.176885 milliseconds

This content has been proxied by September (UNKNO).