Navidrome plugin for HomeFree
Find a file
2026-06-12 18:29:29 -07:00
apps/navidrome dashboard: declare category + description on service-config 2026-06-12 18:02:17 -07:00
flake.nix Add navidrome app flake 2026-05-18 00:53:14 -07:00
README.md Add navidrome app flake 2026-05-18 00:53:14 -07:00

homefree-navidrome

A reference implementation of a HomeFree flake plugin. It adds Navidrome — a self-hosted music server (Subsonic-compatible) — as a HomeFree app, and is meant to double as the worked example for anyone building their own plugin.

It is packaged as a standalone Nix flake so it can be registered through the HomeFree admin panel without forking the HomeFree base repo. You keep receiving upstream HomeFree updates while running this custom app alongside them.

Navidrome is a deliberately good teaching case: it exercises the full range of what a plugin has to do — declaring HomeFree service options, running an OCI container, wiring the reverse proxy, integrating SSO (including an API-path bypass for non-browser clients), and configuring backups — all from a flake that the base repo knows nothing about. The How it works and Writing your own HomeFree plugin sections below walk through the patterns; apps/navidrome/default.nix is heavily commented so it can be read as documentation.

What it adds

Navidrome runs as the official deluan/navidrome container, reverse- proxied at music.<your-domain>. Once registered it appears in the admin panel under Services → Navidrome with these options:

  • enable — turn the service on/off
  • public — expose it on the WAN port
  • music-path — directory holding the music library (defaults to /var/lib/navidrome-podman/music; point it at a mounted disk)

The web UI is gated by HomeFree's Zitadel SSO. Subsonic mobile/desktop clients (DSub, Symfonium, Feishin, …) reach /rest/* directly and use Navidrome's native credentials, since they can't do an interactive OAuth login.

How to add it to HomeFree

This plugin is registered through the admin panel — no command line, no editing /etc/nixos by hand.

  1. Put this repository on the HomeFree machine (it must be a git repository — a git+file:// flake requires one). Either clone it there, or copy the directory and run git init && git add -A && git commit inside it.
  2. Open the HomeFree admin panel and go to Developers → Custom Flakes.
  3. Click Add a custom flake and choose the type:
    • Local repository — use the file browser to select this repository's directory on the machine.
    • Remote URL — if you host this repo somewhere, paste its flake reference instead (e.g. github:owner/homefree-navidrome).
  4. Optionally click Validate to confirm the flake is reachable and exposes a module.
  5. Click Register flake.
  6. Click Apply Changes in the sidebar to rebuild the system.
  7. After the rebuild, open Services, enable Navidrome, set the music library path if needed, and Apply Changes again.

To remove it later: delete the entry on the Custom Flakes page and Apply Changes.

How it works

flake.nix              # exposes nixosModules.default
apps/navidrome/
  default.nix          # the NixOS module — a plain HomeFree app
  icon.svg

flake.nix's nixosModules.default is what HomeFree composes into the system build (the admin panel writes it into /etc/nixos/custom-flakes.nix). apps/navidrome/default.nix is an ordinary NixOS module, evaluated with the same config / lib / pkgs as an in-tree HomeFree app — so it declares HomeFree service options and contributes a homefree.service-config entry exactly like a built-in app.

Option namespaces

HomeFree splits a service's options across two namespaces, and the base repo's module.nix normally declares both halves per in-tree app:

  • homefree.services.<name> — the binding target for homefree-config.json and the source the admin Services page reads.
  • homefree.service-options.<name> — what the app's config block reads; module.nix mirrors one into the other.

A plugin flake cannot edit the base module.nix, so this app declares both halves itself (see apps/navidrome/default.nix). The shared user-facing options are defined once and spliced into each namespace; HomeFree's mirror still runs and copies the JSON-driven values across, so the app behaves exactly like a built-in one.

Writing your own HomeFree plugin

Use this repo as a template. Copy it, replace apps/navidrome/ with your own app, and point flake.nix at it. The patterns to copy from apps/navidrome/default.nix:

  1. Declare both option namespaces. Define the user-facing options once and splice them into homefree.services.<name> and homefree.service-options.<name> — see the "Option namespaces" section above for why a plugin must do this itself.
  2. Run the workload. Add a virtualisation.oci-containers.containers entry (or a native service) gated on enable, plus a systemd.services.podman-<name> block ordered after/requires dns-ready.service so image pulls don't race DNS.
  3. Contribute a homefree.service-config entry. This drives the reverse proxy (reverse-proxy.subdomains / host / port), the admin UI catalog (label, name, icon, options-metadata), backups (backup.paths), and SSO (sso.kind).
  4. Integrate SSO the right way for the app. Navidrome shows the caddy_gated pattern: gate the web UI with reverse-proxy.oauth2 = true, and declare any non-browser API paths that must skip the gate via reverse-proxy.sso-bypass-paths. The base repo stays app-agnostic — your plugin owns the app-specific knowledge (which paths, why).
  5. Expose it from the flake. Point flake.nix's nixosModules.default at your app directory (or import several app modules from one default to ship multiple apps in one plugin).
  6. Register it through Developers → Custom Flakes as above.

Keep app-specific details (image names, callback paths, API path globs) inside your plugin — never expect them to be added to the base HomeFree repo. If you find yourself wanting to change the base repo to support your app, look for a generic primitive it can expose instead (sso-bypass-paths is an example: a generic "these paths skip the gate" option, not a Navidrome-specific one).