Feed aggregation with Miniflux

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

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

  _class = "nixos";
}

Feed aggregators are a dime a dozen, but the one I've preferred to use for a while now is Miniflux. The web app is pretty nice for use at a computer, and it supports enough APIs that it works with basically any mobile app.

Service settings

mjm.services.miniflux = {
  <<service-settings>>
};

Miniflux uses a few different service infrastructure features, each described below.

postgresql.enable = true;

Miniflux supports exclusively PostgreSQL for its database.

http = {
  port = 8080;
  health.path = "/healthcheck";

  metrics.enable = true;

  ingress = {
    subdomain = "feeds";
    authMode = "oidc";
    oidc = {
      name = "Miniflux";
      clientId = "4dVtVDFB6wqBTqqE1hJzVe2shJDaMiEH3wY9BjN9IQ44lrnFmcxiOwzdBDHmk3zB";
      clientSecret = "$argon2id$v=19$m=65536,t=3,p=4$F6tAZnVxae+QgvGjCC6GhQ$tVXggATlvY5qpsMut62Ap5B8RcRPi4HPZwrfz02uJ7Q";
      redirectUris = [ "https://feeds.midna.dev/oauth2/oidc/callback" ];
    };
  };
};

Miniflux is exposed externally at https://feeds.midna.dev. It has a health check endpoint that Consul monitors, and it exposes metrics for Prometheus to scrape. Since it supports OpenID Connect, I use that for authentication.

secrets.enable = true;

Miniflux gets its OIDC client secret from Vault, so it needs secrets support.

Config

services.miniflux.enable = true;

The Miniflux service of course needs to be enabled.

services.miniflux.createDatabaseLocally = false;
services.miniflux.config.DATABASE_URL = "user=miniflux host=/run/postgresql dbname=miniflux";

The PostgreSQL database will be running in its own microVM, so Miniflux should not create it itself. That also means needing to provide a database URL explicitly, since it's only set by default if it's also creating a local database.

services.miniflux.config.BASE_URL = "https://feeds.midna.dev/";

Miniflux needs to be configured with the correct base URL for where it's being served from.

services.miniflux.config = {
  METRICS_COLLECTOR = 1;
  METRICS_ALLOWED_NETWORKS = "0.0.0.0/0,::/0";
};

Prometheus metrics for Miniflux are not enabled by default. Even when enabled, they are only allowed be scraped from local addresses, so I need to open that up so that Prometheus can scrape them.

services.miniflux.config = {
  CREATE_ADMIN = 0;
  OAUTH2_PROVIDER = "oidc";
  OAUTH2_CLIENT_ID = config.mjm.services.miniflux.http.ingress.oidc.clientId;
  OAUTH2_CLIENT_SECRET_FILE = "%d/miniflux.managed.oidc_client_secret";
  OAUTH2_REDIRECT_URL = lib.head config.mjm.services.miniflux.http.ingress.oidc.redirectUris;
  OAUTH2_OIDC_DISCOVERY_ENDPOINT = "https://auth.midna.dev";
  OAUTH2_USER_CREATION = 1;
};

systemd.services.miniflux.credentials.miniflux = [ "managed/oidc_client_secret" ];

Miniflux needs to be configured for OpenID Connect. I've disabled automatic creation of the admin user, so I don't need to provide a secret for the password. OIDC config values are pulled from the mjm.services config where possible. The client secret can't be, because it needs to be the secret version, so that is added as a credential to the systemd service.

mjm.deploy.tests = {
  inherit (pkgs.nixosTests) miniflux;
};

My deploy tooling will run the Miniflux VM test from Nixpkgs in CI.

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

This content has been proxied by September (UNKNO).