- Nix 100%
|
|
||
|---|---|---|
| apps/navidrome | ||
| flake.nix | ||
| README.md | ||
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.
- 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 rungit init && git add -A && git commitinside it. - Open the HomeFree admin panel and go to Developers → Custom Flakes.
- 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).
- Optionally click Validate to confirm the flake is reachable and exposes a module.
- Click Register flake.
- Click Apply Changes in the sidebar to rebuild the system.
- 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 forhomefree-config.jsonand the source the admin Services page reads.homefree.service-options.<name>— what the app'sconfigblock reads;module.nixmirrors 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:
- Declare both option namespaces. Define the user-facing options
once and splice them into
homefree.services.<name>andhomefree.service-options.<name>— see the "Option namespaces" section above for why a plugin must do this itself. - Run the workload. Add a
virtualisation.oci-containers.containersentry (or a native service) gated onenable, plus asystemd.services.podman-<name>block orderedafter/requiresdns-ready.serviceso image pulls don't race DNS. - Contribute a
homefree.service-configentry. 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). - Integrate SSO the right way for the app. Navidrome shows the
caddy_gatedpattern: gate the web UI withreverse-proxy.oauth2 = true, and declare any non-browser API paths that must skip the gate viareverse-proxy.sso-bypass-paths. The base repo stays app-agnostic — your plugin owns the app-specific knowledge (which paths, why). - Expose it from the flake. Point
flake.nix'snixosModules.defaultat your app directory (or import several app modules from onedefaultto ship multiple apps in one plugin). - 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).