Skip to content

Core & Portal

All active services are accessible remotely via Cloudflare Zero Trust (Tunnel) — no open ports, no exposed IP. The tunnel is deployed directly as a native systemd service (cloudflared) in NixOS.

ServiceLocal IP / PortLocal AliasPublic Route
Portal<homelab.ip>:80nixos.local:80homepage-home.javiersc.com
Home Assistant<homelab.ip>:8123nixos.local:8123home-assistant-home.javiersc.com
Jellyfin<homelab.ip>:8096nixos.local:8096jellyfin-home.javiersc.com
Transmission<homelab.ip>:9091nixos.local:9091transmission-home.javiersc.com
Sonarr<homelab.ip>:8989nixos.local:8989sonarr-home.javiersc.com
Radarr<homelab.ip>:7878nixos.local:7878radarr-home.javiersc.com
Prowlarr<homelab.ip>:9696nixos.local:9696prowlarr-home.javiersc.com
Jackett<homelab.ip>:9117nixos.local:9117jackett-home.javiersc.com
Backrest<homelab.ip>:9898nixos.local:9898backrest-home.javiersc.com
DocumentationCloudflare PagesN/Adocs-home.javiersc.com
LiteLLM<homelab.ip>:14000nixos.local:14000litellm-home.javiersc.com
Ollama<homelab.ip>:11434nixos.local:11434ollama-home.javiersc.com
PicoClaw<homelab.ip>:18800nixos.local:18800picoclaw-home.javiersc.com
Cockpit<homelab.ip>:9090nixos.local:9090cockpit-home.javiersc.com
OTBR<homelab.ip>:8091nixos.local:8091otbr-home.javiersc.com

The Homelab SSH MCP endpoint intentionally does not appear in this table. It binds to 127.0.0.1:19000 and must not be registered in homelab.proxies.

  • Jackett: Acts as a proxy between our indexers (Torznab/RSS) and the Arr stack (Sonarr/Radarr). It is essential for searching content in private/public trackers that are not natively supported by Prowlarr or the Arr apps themselves.
  • Local/Remote Access Toggle: To solve the issue of accessing services natively on LAN vs. via Cloudflare URLs externally, we developed a custom toggle injected via Nginx. It dynamically rewrites all dashboard links to point to either the internal IP (<homelab.ip>) or the public domain.

Note: Multicast DNS (mDNS) is enabled via Avahi, making the .local domains available on the LAN without extra DNS configuration.

The entry point for the homelab is built using Homepage, natively declared in modules/nixos/services/portal.nix.

UI & Structure:

  • Layout: Organized into four main categories: Automation (Home Assistant), Media & Entertainment (Jellyfin, Transmission), Management (Sonarr, Radarr, Prowlarr), and Infrastructure (Documentation, Cloudflare).
  • Aesthetics: Dark theme, blurred cards (cardBlur: "sm"), and an Unsplash background image.
  • Widgets: Includes system monitoring (CPU/Memory) and date/time.

The dashboard integrates with Home Assistant to show live data. This requires a Long-Lived Access Token, which is managed securely:

  • Storage: Stored in the Homelab vault in 1Password.
  • Injection: Fetched at runtime via ExecStartPre using the homelab.fetchSecretFile helper.
  • Sandboxing: Due to Homepage’s strict security, the systemd unit sandbox is slightly relaxed to allow the 1Password CLI to run.

Nginx Proxy and Local/Remote Access Toggle

Section titled “Nginx Proxy and Local/Remote Access Toggle”

The Homelab handles dynamic routing depending on where you access it from. We use Nginx (modules/nixos/services/proxy/default.nix) to serve the dashboard and proxy local services through port 80. This allows a single local entry point for the Cloudflare Tunnel.

To solve the issue of accessing services natively on LAN vs. via Cloudflare URLs externally, we developed a Local/Remote Access Toggle:

  • A custom JavaScript file (modules/nixos/services/proxy/toggle.js) is injected into the Homepage UI via Nginx (location = /api/config/custom.js).
  • It renders a toggle button next to the system clock on the dashboard.
  • Remote Mode (Default): Links point to public Cloudflare domain routes (e.g., https://jellyfin-home.javiersc.com).
  • Local Mode: Toggling the button saves the state in localStorage and dynamically rewrites all service links on the DOM to point to their direct local IP and port (e.g., http://<homelab.ip>:8096).

Service icons and the browser favicon are served locally — no external CDN dependencies.

Icon serving:

  • Icons live in assets/icons/ (SVG or WebP) and are served by Nginx via a location alias at /icons/.
  • Referenced in Homepage config as /icons/<name>-logo.svg (the -logo suffix prevents Homepage from resolving them via its built-in CDN).
  • Services without a local icon fall back to the Homepage CDN automatically.

Favicon:

  • The browser favicon is the NixOS snowflake logo (assets/icons/nixos-logo.svg).
  • Nginx intercepts all favicon-related paths (/homepage.ico, /favicon-32x32.png, /favicon-16x16.png, /android-chrome-*.png) before proxying to Homepage.
  • PNG variants are generated at build time from the SVG using librsvg via a pkgs.runCommand derivation in modules/nixos/services/proxy/default.nix.
  • A custom /site.webmanifest is also served so PWA installs use the NixOS icon.

To optimize network performance and ISP ratios, Transmission follows an automated schedule:

  • Day Mode (08:00 - 00:00): Download capped at 50MB/s, Upload disabled (0 kb/s). This ensures maximum bandwidth for home use.
  • Night Mode (00:00 - 08:00): Download 50MB/s, Upload 50MB/s. Maximum seeding performance during off-peak hours.
  • Manual Overdrive: The “Turtle” button in the UI is reserved for emergency manual pausing.

The following services were part of the previous architecture and are currently pending integration into the new NixOS native deployment:

  • Appwrite (Backend)
  • Immich (Photos)
  • Penpot (Design)
  • Postiz (Social)