Skip to notes

Public Homelab Notes / 03

Networking

How I connect the lab: opt-in DNS, private remote access, and separate paths for public services.

5 notes · Current setups, past observations, and future plans stay as recorded.

Networking / Note 01

Network Topology

In this note

Master overview of the current and planned Open Lab Frame homelab network.

Homelab Overview | Pi-hole | Tailscale | Nginx Proxy Manager | VPS Caddy | Proxmox Host


Current Network Overview

The homelab currently uses:

Starlink

as the Internet connection.

Starlink is configured in bypass mode, allowing the:

TP-Link Archer BE550

to act as the primary household router.

Current high-level topology:

Internet
   ↓
Starlink
   ↓
TP-Link Archer BE550
   ↓
Home LAN
   │
   ├── HP ProDesk 600 G4 / Proxmox
   ├── Raspberry Pi 5
   ├── Raspberry Pi 3
   ├── Personal PC
   ├── Wi-Fi devices
   └── Normal household devices

The homelab is designed to remain:

Opt-in
+
Administratively private

Normal household users should not need to participate in:

  • Pi-hole DNS
  • Tailscale
  • Internal homelab DNS
  • Nginx Proxy Manager
  • Proxmox administration
  • Homelab experiments

Internet Connection

Primary Internet connection:

Starlink

Current design:

Internet
   ↓
Starlink
   ↓
BE550

The network is intentionally being kept simple and uses a single Starlink connection with the Archer BE550 as the primary router.


Current Router

Current router:

TP-Link Archer BE550

Responsibilities:

  • NAT
  • DHCP
  • LAN routing
  • Wi-Fi
  • General household Internet access
  • Main home-network gateway

Starlink remains in bypass mode while the BE550 performs the actual router role.

The BE550 is also planned to remain the main router and Wi-Fi device in the future.


Current Switching

Current small switch:

TP-Link TL-SG105S-M2

Capabilities:

5 ports
2.5GbE
Unmanaged

It currently provides wired connectivity for selected homelab equipment.

A managed Omada switch is planned as the future wired distribution layer.


Current Core Systems

SystemNetworkPrimary Role
Proxmox HostPrivate / internalVirtualization host
VM100 - JellyfinPrivate / internalMedia services
VM101 - OpenLabFrame DevPrivate / internalOn-demand website development
VM102 - ManagementPrivate / internalNPM / Portainer / management
Raspberry Pi 5Private / internalDocker / website / private services
Raspberry Pi 3Private / internalPi-hole DNS / Tailscale subnet routing

Current Logical Topology

                         Internet
                            ↓
                         Starlink
                            ↓
                    TP-Link Archer BE550
                            ↓
                      <private home LAN>
                            │
        ┌───────────────────┼───────────────────┐
        │                   │                   │
        ↓                   ↓                   ↓
 HP ProDesk             Raspberry Pi 5      Raspberry Pi 3
 <private Proxmox host>           <private Pi 5 host>       <private Pi 3 host>
        │                   │                   │
        │                   │                   ├── Pi-hole
        │                   │                   ├── Tailscale DNS
        │                   │                   └── Subnet routing
        │                   │
        │                   ├── Open Lab Frame
        │                   ├── Uptime Kuma
        │                   ├── Beszel
        │                   ├── Grafana
        │                   ├── Prometheus
        │                   └── Cloudflare Tunnel
        │
        └── Proxmox
             │
             ├── VM100 - <private Jellyfin host>
             ├── VM101 - <private development host>
             └── VM102 - <private management host>

DNS Architecture

Pi-hole

Raspberry Pi 3 hosts Pi-hole.

Current Pi 3:

Hostname: <pi3-hostname>
Network: Private LAN
SSH:      ssh <pi3-alias>

Pi-hole is intentionally:

OPT-IN

It is not the required DNS server for the entire household.


Normal Household DNS

Friends, family, guests, TVs, and normal household devices can continue using ordinary router/network behavior.

Normal Household Device
        ↓
Archer BE550
        ↓
Normal DNS
        ↓
Starlink
        ↓
Internet

These clients should not depend on:

  • Pi-hole
  • Tailscale
  • Proxmox
  • NPM
  • Internal homelab DNS

Homelab-Aware Devices

Selected personal/admin devices can use Pi-hole intentionally.

Personal Device
      ↓
Pi 3 / Pi-hole
      ↓
Homelab-aware DNS
      ↓
Internal services
      +
Internet

This allows private service names without changing normal household behavior.


Private Internal DNS

Most private service names use:

<private internal wildcard>

Pi-hole generally resolves these hostnames to:

<private management host>

which is:

VM102 - Management

running:

Nginx Proxy Manager


Internal DNS Architecture

Client
   ↓
Pi-hole
   ↓
service.<private internal domain>
   ↓
<private management host>
   ↓
VM102 / NPM
   ↓
Actual backend service

Pi-hole answers:

Where is the service?

NPM answers:

Which backend should receive the HTTPS request?

Current Internal Service Names

Examples:

<private development hostname>
<private monitoring hostname>
<private proxy-admin hostname>
<private container-admin hostname>
<private Beszel hostname>

Nginx Proxy Manager

Nginx Proxy Manager runs on:

VM102
<private management host>

Its responsibilities include:

  • Private HTTPS
  • Friendly internal hostnames
  • Reverse proxying
  • Central internal service routing

General path:

Client
   ↓
Pi-hole
   ↓
<private management host>
   ↓
NPM
   ↓
Backend

NPM Route - Beszel

Friendly URL:

<private Beszel URL>

Path:

<private Beszel hostname>
        ↓
<private management host>
        ↓
NPM
        ↓
<private Pi 5 host>:<SERVICE_PORT>
        ↓
Beszel Hub

NPM Route - Grafana

<private monitoring hostname>
        ↓
<private management host>
        ↓
NPM
        ↓
<private Pi 5 host>:<SERVICE_PORT>
        ↓
Grafana

NPM Route - Portainer

<private container-admin hostname>
        ↓
<private management host>
        ↓
NPM
        ↓
Portainer

NPM Route - NPM

<private proxy-admin hostname>
        ↓
<private management host>
        ↓
NPM
        ↓
NPM Admin Interface

NPM Route - Open Lab Frame Development

<private development hostname>
        ↓
<private management host>
        ↓
NPM
        ↓
<private development host>:<SERVICE_PORT>
        ↓
VM101

VM101 - OpenLabFrame Dev is normally powered off unless website development is actively taking place.

Therefore:

<private development hostname> offline

is normally expected while VM101 is off.


Tailscale

Tailscale provides the private remote-access layer.

It allows selected personal devices to reach private homelab services while away from home.


Raspberry Pi 3 Tailscale Role

Raspberry Pi 3 provides:

Pi-hole DNS
Tailscale DNS
Subnet routing
Private LAN reachability

The Pi 3 helps remote personal devices resolve and reach:

<private home LAN>

services.


Raspberry Pi 5 Tailscale Role

Raspberry Pi 5 provides:

Tailscale connectivity
Exit-node functionality
Private application access

It also hosts several private applications that may be reached through Tailscale.


VM100 Tailscale

VM100 - Jellyfin has a Tailscale address:

<private Jellyfin Tailscale endpoint>

This is used for:

  • Private Jellyfin access
  • VPS → Jellyfin connectivity
  • Secure remote communication

Remote Personal Access

When a personal device is away from home:

iPhone / Laptop
      ↓
Tailscale
      ↓
Pi 3 DNS + subnet routing
      ↓
Home LAN
      ↓
Private services

Example:

iPhone on 5G
      ↓
Tailscale
      ↓
Pi 3 DNS
      ↓
      ↓
<private management host>
      ↓
NPM
      ↓

This allows private hostnames to work both:

At Home
+
Away Through Tailscale

without publicly exposing the service.


Home Wi-Fi Is Still LAN

A device does not need to be physically connected by Ethernet to use the local network.

Example:

iPhone
   ↓
BE550 Wi-Fi
   ↓
Home LAN
   ↓
<private home LAN> services

Home Wi-Fi clients can communicate locally unless intentionally isolated.


Jellyfin Networking

Local Jellyfin Path

At home:

iPhone on Wi-Fi
      ↓
Pi 3 DNS
      ↓
<private Jellyfin host>
      ↓
VM100 / Jellyfin

A Jellyfin session was observed from:

<private local client>

confirming that the iPhone was communicating with Jellyfin directly through the home LAN.


Private Remote Jellyfin Path

Away from home, a personal device can use Tailscale.

Observed iPhone Tailscale address:

<private remote Tailscale client>

Path:

iPhone
   ↓
Tailscale
   ↓
VM100
<private Jellyfin Tailscale endpoint>
   ↓
Jellyfin

Public Jellyfin / Friend Path

Friends and other normal remote users use the public VPS path.

Remote User
      ↓
Internet
      ↓
jellyfin.openlabframe.com
      ↓
VPS
      ↓
Caddy
      ↓
Tailscale
      ↓
VM100
<private Jellyfin Tailscale endpoint>:<SERVICE_PORT>
      ↓
Jellyfin

Nginx Proxy Manager is not part of this path.


VPS

Current VPS public address:

<public VPS address omitted>

Windows SSH alias:

ssh <vps-alias>

The VPS provides a publicly reachable ingress point because the Starlink home connection operates behind CGNAT.


VPS Caddy

VPS Caddy provides the public Jellyfin reverse proxy.

Conceptual configuration:

jellyfin.openlabframe.com {
    reverse_proxy <private Jellyfin Tailscale endpoint>:<SERVICE_PORT>
}

The VPS connects to VM100 through Tailscale.


Why VPS Is Used

Starlink CGNAT prevents the normal inbound IPv4 port-forwarding model from being the preferred public-access method.

Therefore:

Public Internet
      ↓
VPS
      ↓
Tailscale
      ↓
VM100

provides the required public ingress without directly opening the home network.


Cloudflare

Cloudflare is used separately for the public Open Lab Frame website.

Architecture:

Visitor
   ↓
Cloudflare
   ↓
Cloudflare Tunnel
   ↓
Raspberry Pi 5
   ↓
Nginx / ehworld
   ↓
Open Lab Frame

This is separate from both:

VPS / Jellyfin

and:

NPM / Private homelab services

Architecture Separation

The homelab intentionally uses different ingress technologies for different purposes.

Open Lab Frame

Cloudflare
→ Cloudflare Tunnel
→ Pi 5

Public Jellyfin

VPS Caddy
→ Tailscale
→ VM100

Private Internal Web Services

Pi-hole
→ VM102 NPM
→ Backend

Private Remote Access

Tailscale
→ Pi 3 DNS / subnet route
→ Home LAN

These paths should remain separate unless there is a real technical reason to combine them.


Monitoring

Uptime Kuma

Uptime Kuma runs on Raspberry Pi 5.

It monitors important infrastructure and application availability.

Examples:

  • Proxmox
  • VM100
  • VM102
  • Jellyfin
  • Gluetun
  • ARR Stack
  • NPM
  • Portainer
  • Open Lab Frame

Alert flow:

Problem
   ↓
Uptime Kuma
   ↓
Discord
   +
ntfy
   ↓
iPhone

Beszel

Beszel provides lightweight resource visibility.

Current monitored systems:

HP ProDesk - Proxmox
Pi 5
VM100 - Jellyfin
VM102 - Management

The Pi 3 is intentionally not included.

Beszel is used for:

  • CPU
  • Memory
  • Disk
  • Network
  • Temperature
  • Uptime
  • Docker/container visibility

Administrative Privacy

The homelab is administratively private.

Being connected to the home LAN does not automatically grant administrative access.

Protected systems include:

  • Proxmox
  • NPM
  • Portainer
  • Pi-hole
  • Uptime Kuma
  • Beszel
  • SSH
  • Other management services

Authentication remains required.


Future Managed Switch

Planned managed switch:

TP-Link Omada SG2210XMP-M2

Target features:

2.5GbE
PoE+
2 × 10Gb SFP+
Managed / Omada

It will become the primary wired distribution layer for the homelab.


Future Wired Topology

Internet
   ↓
Starlink
   ↓
TP-Link Archer BE550
   ↓
TP-Link Omada SG2210XMP-M2
   │
   ├── NAS / Compute
   ├── HP ProDesk 600 G4
   ├── Raspberry Pi 5
   ├── Raspberry Pi 3
   ├── Homelab / Admin Clients
   └── Other selected wired systems

The Archer BE550 remains:

Router
+
Wi-Fi
+
DHCP
+
NAT
+
Main household gateway

Future NAS / Compute

NAS Compute Build will eventually connect through the Omada managed switch.

Starlink
   ↓
BE550
   ↓
Omada Switch
   ↓
NAS / Compute

The NAS / compute system will eventually handle heavier:

  • Storage workloads
  • Media storage
  • Virtualization
  • Immich
  • Game-server storage
  • Backups
  • Other compute-heavy applications

Opt-In Homelab Design

The future network keeps the same basic philosophy as the current network.

Normal Household Devices

Phone / TV / Guest Device
        ↓
BE550
        ↓
Starlink
        ↓
Internet

They should not depend on:

  • Pi-hole
  • Tailscale
  • NPM
  • Internal DNS
  • Homelab services

Selected Homelab / Personal Devices

Personal Device
      ↓
Normal LAN
      +
Optional Pi-hole
      +
Optional Tailscale
      ↓
Homelab services

The fundamental rule is:

Household Network
≠
Mandatory Homelab Participation

Future VLANs

The Omada managed switch may eventually be used for VLAN experimentation.

Possible logical groups could include:

Management
Servers
Trusted Personal Devices
General Household Devices
IoT
Guests

VLANs are optional.

They should only be introduced when there is a clear benefit and should not unnecessarily complicate normal household Internet access.


Current vs Future

Current

Internet
   ↓
Starlink
   ↓
Archer BE550
   ↓
Current LAN
   │
   ├── HP ProDesk / Proxmox
   ├── Raspberry Pi 5
   ├── Raspberry Pi 3
   └── Clients

Future

Internet
   ↓
Starlink
   ↓
Archer BE550
   ↓
Omada SG2210XMP-M2
   │
   ├── NAS / Compute
   ├── HP ProDesk
   ├── Raspberry Pi 5
   ├── Raspberry Pi 3
   └── Homelab / Admin Clients

Design Principles

The network should continue following these rules:

  • Starlink remains the single Internet/WAN connection.
  • The Archer BE550 remains the primary router.
  • The Archer BE550 remains the main Wi-Fi device.
  • The Omada switch becomes the future managed wired distribution layer.
  • Homelab participation remains opt-in.
  • Household Internet should function independently of Pi-hole.
  • Household Internet should function independently of Tailscale.
  • Pi-hole remains optional for selected devices.
  • Tailscale remains the preferred private remote-access method.
  • NPM remains primarily for private/internal reverse proxying.
  • VPS/Caddy remains the public Jellyfin ingress path.
  • Cloudflare Tunnel remains the public Open Lab Frame ingress path.
  • Administrative services remain authenticated.
  • Private services should not be publicly exposed without a clear reason.
  • DNS, reverse proxying, routing, and remote access should remain clearly separated.
  • Network changes should be introduced gradually and tested before becoming permanent.

Related Notes

Homelab Overview
Pi-hole
Tailscale
Nginx Proxy Manager
VPS Caddy
Proxmox Host
HP ProDesk 600 G4
Raspberry Pi 5
Raspberry Pi 3
VM100 - Jellyfin
VM102 - Management
NAS Compute Build

Back to Networking

Networking / Note 02

Pi-hole

In this note

Private, opt-in DNS filtering and homelab name-resolution service running on the Raspberry Pi 3.

Homelab Overview | Raspberry Pi 3 | Tailscale | Network Topology | Nginx Proxy Manager


Status

State: In use
Host: Raspberry Pi 3
Hostname: private / internal
Network: Private LAN
Role: DNS filtering + local DNS + Tailscale DNS integration
Operation: Always on


Purpose

Pi-hole provides DNS services for selected personal and homelab devices.

Its responsibilities include:

  • DNS filtering
  • Local DNS records
  • Friendly homelab hostnames
  • Internal service discovery
  • DNS for selected Tailscale clients
  • Integration with private remote access

Pi-hole is intentionally not the mandatory DNS server for the entire household.


Design Philosophy

The homelab DNS design is opt-in.

Normal Household Devices

Friends / Family
      ↓
Normal home Wi-Fi
      ↓
TP-Link router DNS
      ↓
Internet

These devices should continue working normally even if:

  • Pi-hole is restarted
  • Homelab DNS records change
  • Tailscale is unavailable
  • Internal services are being tested

Selected Personal Devices

Personal Device
      ↓
Pi-hole
      ↓
DNS filtering
      ↓
Local homelab record if applicable
      ↓
Internet / Internal service

This keeps homelab experimentation isolated from the rest of the household.


Host

Pi-hole runs on:

Raspberry Pi 3

Current Pi 3 details:

Hostname: <pi3-hostname>
Network: Private LAN
User:     <admin-user>

SSH:

ssh <pi3-alias>

Upstream DNS

Pi-hole has used Cloudflare resolvers:

<public DNS resolver>
<public DNS resolver>

General resolution path:

Client
   ↓
Pi-hole
   ↓
Cloudflare DNS
   ↓
Internet

Local DNS records are answered internally without needing to query the upstream resolver.


Local DNS

Pi-hole provides local DNS records for homelab services.

The main internal pattern is:

<private internal wildcard>

Many of these names resolve to:

<private management host>

which is VM102 - Management running Nginx Proxy Manager.


Current Internal Records

Examples include:

<private development hostname>
    → <private management host>

<private monitoring hostname>
    → <private management host>

<private proxy-admin hostname>
    → <private management host>

<private container-admin hostname>
    → <private management host>

    → <private management host>

<private Beszel hostname>
    → <private management host>

NPM then forwards each hostname to the appropriate backend.


Why DNS Points to VM102

Pi-hole usually does not point these service names directly at the application server.

Instead:

Client
   ↓
Pi-hole
   ↓
service.<private internal domain>
   ↓
<private management host>
   ↓
VM102 / Nginx Proxy Manager
   ↓
Correct backend

This gives the homelab:

  • Friendly hostnames
  • Central HTTPS handling
  • Cleaner backend changes
  • One reverse-proxy entry point

Example: Beszel

Pi-hole record:

<private Beszel hostname>
    → <private management host>

Traffic path:

Client
   ↓
Pi-hole
   ↓
<private management host>
   ↓
NPM
   ↓
<private Pi 5 host>:<SERVICE_PORT>
   ↓
Beszel on Pi 5

Friendly URL:

<private Beszel URL>

Example: Grafana

Pi-hole record:

<private monitoring hostname>
    → <private management host>

NPM backend:

<private Pi 5 host>:<SERVICE_PORT>

Path:

Client
   ↓
Pi-hole
   ↓
VM102 / NPM
   ↓
Pi 5 / Grafana

Example: Open Lab Frame Development

Pi-hole record:

<private development hostname>
    → <private management host>

NPM backend:

<private development host>:<SERVICE_PORT>

Target:

VM101 - OpenLabFrame Dev

Because VM101 is intentionally powered off when not developing:

<private development hostname> unavailable

is normal while VM101 is off.


Jellyfin Local DNS

Pi-hole is also used to help selected devices take a direct local Jellyfin path.

Local Jellyfin server:

<private Jellyfin host>

Target:

VM100 - Jellyfin

At home:

iPhone on Wi-Fi
      ↓
Pi-hole DNS
      ↓
<private Jellyfin host>
      ↓
Jellyfin

An observed iPhone LAN address was:

<private local client>

which confirmed that the device was reaching Jellyfin directly over the home LAN instead of unnecessarily going out through the VPS and back.


Home Wi-Fi and LAN

A device does not need to be connected by Ethernet to use local DNS and local services.

A phone on the normal home Wi-Fi is still part of the home LAN unless it is intentionally isolated.

Example:

iPhone
   ↓
Wi-Fi
   ↓
Home LAN
   ↓
Pi-hole
   ↓
Internal service

Tailscale DNS Integration

Pi-hole also participates in the Tailscale design.

Selected remote devices can continue using homelab DNS while away from home.

Example:

iPhone on 5G
      ↓
Tailscale
      ↓
Pi 3 / Pi-hole
      ↓
      ↓
<private management host>
      ↓
NPM
      ↓

This lets the same private hostname work:

At home
+
Away through Tailscale

without publicly exposing the backend.


Pi 3 Subnet Routing

The Raspberry Pi 3 also provides Tailscale subnet-routing functionality.

This allows selected Tailscale clients to reach private LAN addresses such as:

<private Proxmox host>
<private Jellyfin host>
<private Pi 5 host>
<private management host>

while away from home.

DNS and routing work together:

Pi-hole
→ tells client which private IP to use

Tailscale subnet route
→ provides a path to that private IP

DNS vs Routing

Pi-hole does not carry the application traffic itself.

Its job is primarily:

Hostname
   ↓
IP address

For example:

        ↓
<private management host>

The actual HTTPS traffic then travels through the normal LAN or Tailscale network path.


Pi-hole and NPM

Pi-hole and Nginx Proxy Manager have different jobs.

Pi-hole

Name resolution
service.<private internal domain>
        ↓
<private management host>

NPM

HTTPS request arrives
        ↓
Read requested hostname
        ↓
Forward to correct backend

Together:

Client
   ↓
Pi-hole
   ↓
VM102
   ↓
NPM
   ↓
Application

Pi-hole and Cloudflare

The Pi-hole DNS service is separate from Cloudflare's role in the public Open Lab Frame website.

Pi-hole

Used for:

  • Private DNS
  • Filtering
  • Internal homelab names
  • Tailscale-connected personal devices

Cloudflare

Used for:

  • Public Open Lab Frame DNS
  • Cloudflare Tunnel
  • Public website delivery
  • CDN/caching
  • Public-facing protections

These systems should not be confused with each other.


Pi-hole and Jellyfin Public Access

Pi-hole is primarily relevant for the user's private/local Jellyfin access.

Friends using public Jellyfin access instead use:

Internet
   ↓
VPS Caddy
   ↓
Tailscale
   ↓
VM100

They do not need the Pi 3 to use the public Jellyfin service.


Monitoring

Pi-hole can be monitored through Uptime Kuma.

The Pi 3 itself is intentionally not currently included in Beszel.

This keeps the Beszel dashboard focused on:

Pi-hole is lightweight enough that detailed resource monitoring is not currently considered necessary.


Upstream DNS Troubleshooting

Pi-hole has previously logged upstream errors involving:

<public DNS resolver>#53
<public DNS resolver>#53

with messages similar to:

TCP connection failed while receiving payload length from upstream
Resource temporarily unavailable

These should be treated separately from local DNS records.

For example:

This example was not included in the public notes.

can still be a valid local record even if an unrelated upstream Cloudflare DNS request experiences a temporary failure.


Useful Troubleshooting Concepts

Local DNS Record Works

If:

This example was not included in the public notes.

resolves to:

<private management host>

then Pi-hole has done its job.

If the website still does not open, investigate:

  • NPM
  • Backend application
  • TLS
  • Routing
  • Firewall
  • Tailscale

rather than assuming DNS is the problem.


Basic DNS Test

From a client using Pi-hole:

This example was not included in the public notes.

Expected general result:

Address: <private management host>

The exact formatting depends on the operating system.


Security

Pi-hole is infrastructure and should remain private.

Important rules:

  • Do not expose the Pi-hole admin interface publicly.
  • Keep administration authenticated.
  • Keep SSH public-key-only.
  • Use Tailscale for private remote access.
  • Do not make Pi-hole mandatory for the household without deliberately changing the network design.
  • Avoid storing passwords or secrets in this Obsidian vault.

Failure Philosophy

Because Pi-hole is opt-in:

Pi-hole failure

should not take down normal Internet access for the entire household.

That isolation is intentional.

Selected homelab-aware devices may temporarily lose:

  • DNS filtering
  • Internal hostname resolution
  • Private .<private internal domain> names

while normal household clients continue using regular DNS.


Relationship With Raspberry Pi 3

Raspberry Pi 3 is the physical host for Pi-hole.

Current host details:

Hostname: <pi3-hostname>
Network: Private LAN
User:     <admin-user>
SSH:      ssh <pi3-alias>

The Pi 3 is intentionally dedicated primarily to lightweight DNS/networking responsibilities.


Relationship With Raspberry Pi 5

Raspberry Pi 5 hosts many of the applications that Pi-hole helps clients locate indirectly.

Examples:

Beszel
Grafana
Uptime Kuma
Open Lab Frame

Pi-hole may resolve the friendly hostname to VM102/NPM, which then proxies to the Pi 5.


Relationship With VM102

VM102 - Management is the central reverse-proxy target for most private service hostnames.

Pi-hole
   ↓
<private management host>
   ↓
VM102
   ↓
NPM

This is one of the central pieces of the current internal-service design.


Future Network Changes

Future homelab networking may add:

  • Managed switching
  • Optional VLAN segmentation for homelab services

The Archer BE550 remains the main router, Wi-Fi, DHCP, and NAT device in the current plan.

Pi-hole can continue providing DNS for selected networks/devices.

Its exact network placement can be redesigned later without changing the overall principle:

Homelab DNS should remain deliberate and controlled.

Related Notes

Homelab Overview
Raspberry Pi 3
Tailscale
Network Topology
Nginx Proxy Manager
VM102 - Management
Raspberry Pi 5
VM100 - Jellyfin
Beszel

Back to Networking

Networking / Note 03

Tailscale

In this note

Private remote-access network connecting selected personal devices, homelab systems, the VPS, and the home LAN without exposing internal services directly to the Internet.

Homelab Overview | Network Topology | Pi-hole | Raspberry Pi 3 | Raspberry Pi 5 | VM100 - Jellyfin | VPS Caddy


Status

State: In use
Role: Private remote access / secure overlay network
Design: Opt-in
Primary Home DNS / Subnet Router: Raspberry Pi 3
Exit Node: Raspberry Pi 5

Tailscale is intentionally used only by selected devices and homelab systems.

Normal household devices do not need Tailscale.


Purpose

Tailscale provides private connectivity between devices that may be:

  • On the home LAN
  • On cellular data
  • On another Wi-Fi network
  • Behind CGNAT
  • Running on the public VPS
  • Physically remote from the homelab

The main goal is:

Private remote connectivity
without publicly exposing
internal homelab services

Why Tailscale Is Important

The home Internet connection uses Starlink.

Starlink operates behind CGNAT, so normal inbound IPv4 port forwarding is not the preferred method for reaching private services.

Tailscale creates a private encrypted network between authorized devices.

Conceptually:

Remote Device
      ↓
Tailscale
      ↓
Private Tailscale Network
      ↓
Home Homelab

Current Tailscale Roles

Raspberry Pi 3

Raspberry Pi 3 is the primary Tailscale networking node for:

Pi-hole DNS
Subnet routing
Private LAN reachability

Current LAN details:

Hostname: <pi3-hostname>
Network: Private LAN

The Pi 3 is responsible for allowing selected Tailscale devices to reach private LAN services.


Raspberry Pi 5

Raspberry Pi 5 also participates in Tailscale.

Its primary Tailscale-related role includes:

Exit-node functionality
Private service access

The Pi 5 also hosts services including:

  • Open Lab Frame
  • Uptime Kuma
  • Beszel
  • Grafana
  • Prometheus

VM100 - Jellyfin

VM100 - Jellyfin has its own Tailscale connection.

Tailscale endpoint:

<private Jellyfin Tailscale endpoint>

This address is used for:

  • Private Jellyfin access
  • VPS → Jellyfin connectivity
  • Secure remote media-server communication

Subnet Routing

The Pi 3 acts as the Tailscale subnet-routing node for the home LAN.

This allows selected remote Tailscale devices to reach private addresses such as:

<private Proxmox host>
<private Jellyfin host>
<private Pi 5 host>
<private management host>
<private Pi 3 host>

without exposing those systems publicly.

General path:

Remote Tailscale Device
        ↓
Tailscale
        ↓
Raspberry Pi 3
        ↓
Home LAN
        ↓
<private home LAN> service

DNS Integration

Pi-hole runs on the Raspberry Pi 3 and provides DNS for selected Tailscale devices.

This allows private homelab names to work remotely.

Example:

This example was not included in the public notes.

can resolve to:

<private management host>

even while the client is away from home.


DNS + Subnet Routing Together

These two functions solve different parts of remote access.

Pi-hole

Answers:

What IP address belongs to this hostname?

Example:

        ↓
<private management host>
Tailscale Subnet Router

Answers:

How does the remote device reach <private management host>?

Together:

Remote Client
      ↓
Pi-hole DNS
      ↓
<private management host>
      ↓
Tailscale subnet route
      ↓
Home LAN
      ↓
VM102 / NPM

Remote Private Service Example

At home:

iPhone
   ↓
Home Wi-Fi
   ↓
Pi-hole
   ↓
   ↓
<private management host>
   ↓
NPM
   ↓

Away from home:

iPhone on 5G
      ↓
Tailscale
      ↓
Pi 3 DNS / subnet routing
      ↓
      ↓
<private management host>
      ↓
NPM
      ↓

The same friendly hostname can therefore work both locally and remotely.


Internal .<private internal domain> Services

Selected private services include:

<private Beszel hostname>
<private monitoring hostname>
<private proxy-admin hostname>
<private container-admin hostname>
<private development hostname>

Most of these resolve through Pi-hole to:

<private management host>

which is VM102 - Management running Nginx Proxy Manager.

Tailscale allows remote authorized devices to reach that private address.


Jellyfin Private Access

Tailscale provides a direct private route to VM100 - Jellyfin.

VM100 Tailscale address:

<private Jellyfin Tailscale endpoint>

Example:

Personal Remote Device
        ↓
Tailscale
        ↓
<private Jellyfin Tailscale endpoint>
        ↓
Jellyfin

This private path is useful for personal access.


Observed iPhone Tailscale Connection

An iPhone Jellyfin session was previously observed using:

<private remote Tailscale client>

This showed that the iPhone had connected to Jellyfin through the Tailscale network rather than through the local <private home LAN> LAN path.


Local vs Tailscale Jellyfin

Home Wi-Fi
iPhone
   ↓
<private home LAN> LAN
   ↓
VM100
<private Jellyfin host>
Away From Home
iPhone
   ↓
Tailscale
   ↓
VM100
<private Jellyfin Tailscale endpoint>

Both can reach the same Jellyfin server.


Network Route vs Direct Play

Tailscale determines the network path.

It does not determine whether Jellyfin Direct Plays or transcodes media.

These are separate concepts:

Direct/private network route
≠
Jellyfin Direct Play

For example:

iPhone
→ Tailscale
→ Jellyfin

may still result in:

Transcoding

if the client does not support the:

  • Container
  • Audio codec
  • Subtitle codec
  • Video format
  • Bitrate

VPS / Jellyfin Integration

The public VPS Caddy server also participates in the Tailscale network.

Public Jellyfin architecture:

Remote Friend
      ↓
Internet
      ↓
jellyfin.openlabframe.com
      ↓
VPS
      ↓
Caddy
      ↓
Tailscale
      ↓
<private Jellyfin Tailscale endpoint>:<SERVICE_PORT>
      ↓
VM100 / Jellyfin

This allows the VPS to reach Jellyfin without exposing Jellyfin directly from the home network.


Why NPM Is Not in the Public Jellyfin Path

The public Jellyfin route is intentionally:

VPS
   ↓
Caddy
   ↓
Tailscale
   ↓
VM100

not:

VPS
   ↓
NPM
   ↓
VM100

This avoids an unnecessary additional proxy hop.

Nginx Proxy Manager remains focused primarily on private/internal homelab web services.


Exit Node

Raspberry Pi 5 is used as the Tailscale exit-node component of the homelab.

An exit node allows a selected Tailscale device to route general Internet traffic through the Pi 5 / home connection when deliberately enabled.

Conceptually:

Remote Device
      ↓
Tailscale
      ↓
Pi 5 Exit Node
      ↓
Home Internet
      ↓
Internet

Using the exit node is optional.

It is different from subnet routing.


Exit Node vs Subnet Router

Pi 3 - Subnet Router

Used to reach:

<private home LAN>

home LAN systems.

Pi 5 - Exit Node

Can route general Internet traffic through the home Internet connection.

Conceptually:

Pi 3
→ Reach my private LAN

Pi 5
→ Route general traffic through home

Opt-In Design

Tailscale is intentionally opt-in.

Normal household devices do not need it.

Normal Household Device
        ↓
BE550
        ↓
Starlink
        ↓
Internet

Selected personal devices can additionally use:

Personal Device
      ↓
Tailscale
      ↓
Private homelab access

Administrative Privacy

Tailscale connectivity does not replace application authentication.

Being connected to the Tailnet does not automatically grant administrative access.

Services still require their normal authentication.

Examples:

  • Proxmox
  • Portainer
  • NPM
  • Pi-hole
  • Beszel
  • Uptime Kuma
  • SSH

The model is:

Tailscale
→ provides network access

Application authentication
→ determines permission

Security Principles

Important rules:

  • Do not expose private Tailscale authentication keys.
  • Do not store reusable Tailscale auth keys in Obsidian.
  • Keep administrative services authenticated.
  • Use Tailscale instead of public exposure where practical.
  • Only authorize trusted devices.
  • Remove old devices from the Tailnet when no longer needed.
  • Use subnet routing only where required.
  • Keep household participation optional.

Relationship With Pi-hole

Pi-hole provides private DNS resolution.

Tailscale provides the route to the resulting private address.

Example:

Pi-hole:
<private Beszel hostname>
        ↓
<private management host>

Tailscale:
Remote device
        ↓
Route to <private management host>

Together they provide remote friendly-name access.


Relationship With NPM

Nginx Proxy Manager handles private HTTPS application routing.

Example:

Remote Device
      ↓
Tailscale
      ↓
Pi-hole
      ↓
<private Beszel hostname>
      ↓
<private management host>
      ↓
NPM
      ↓
Beszel

Tailscale gets the client to the private network.

NPM routes the web request to the correct application.


Relationship With Starlink

Tailscale is particularly useful because Starlink uses CGNAT.

Instead of requiring inbound home port forwarding:

Internet
   X
Direct inbound connection

Tailscale creates an outbound-established private overlay:

Home Device
   ↓
Tailscale
   ↕
Remote Device

This works even when the home connection is behind CGNAT.


Failure Behavior

If Tailscale becomes unavailable:

Normal household Internet

Should continue operating normally.

Local LAN services

Should continue working locally.

Remote private access

May stop working until Tailscale recovers.

Public Open Lab Frame

Should remain unaffected because it uses:

Cloudflare Tunnel

Public Jellyfin

May be affected because the VPS → VM100 route depends on Tailscale.


Current Architecture Summary

                     TAILSCALE
                         │
        ┌────────────────┼────────────────┐
        │                │                │
        ↓                ↓                ↓
 Raspberry Pi 3    Raspberry Pi 5      VM100
 Subnet Router     Exit Node           Jellyfin
 Pi-hole DNS       Services            <private Jellyfin Tailscale endpoint>
        │
        ↓
 <private home LAN>
 Home LAN

Related Notes

Homelab Overview
Network Topology
Pi-hole
Raspberry Pi 3
Raspberry Pi 5
VM100 - Jellyfin
VM102 - Management
Nginx Proxy Manager
VPS Caddy
Jellyfin

Back to Networking

Networking / Note 04

Nginx Proxy Manager

In this note

Private/internal reverse-proxy layer for friendly HTTPS homelab service names.

Homelab Overview | Network Topology | Pi-hole | Tailscale | VM102 - Management | VPS Caddy


Status

State: In use
Host: VM102 - Management
Network: Private LAN
Role: Internal reverse proxy / HTTPS service routing
Operation: Always on

Nginx Proxy Manager is intentionally used primarily for private homelab web services.

It is not the main public ingress path for Jellyfin.


Host System

NPM runs on:

VM102 - Management

LAN IP:

<private management host>

VM102 also hosts:

  • Portainer
  • Beszel Agent
  • Other lightweight management tooling

Purpose

Nginx Proxy Manager provides:

  • Friendly internal URLs
  • HTTPS
  • Reverse proxying
  • Centralized internal service routing
  • TLS certificate handling
  • WebSocket proxying where required

Instead of remembering:

<private Pi 5 host>:<SERVICE_PORT>
<private Pi 5 host>:<SERVICE_PORT>
<private Pi 5 host>:<SERVICE_PORT>
<private development host>:<SERVICE_PORT>
<private management host>:<SERVICE_PORT>

services can use names such as:

<private Beszel hostname>
<private monitoring hostname>
<private development hostname>
<private container-admin hostname>

Core Architecture

The normal private service path is:

Client
   ↓
Pi-hole DNS
   ↓
service.<private internal domain>
   ↓
<private management host>
   ↓
VM102 / NPM
   ↓
Backend application

Pi-hole performs DNS resolution.

NPM performs HTTP/HTTPS routing.

These are separate jobs.


DNS Role

Pi-hole answers:

Where should this hostname go?

For most private .<private internal domain> services:

service.<private internal domain>
        ↓
<private management host>

NPM Role

NPM receives the request and determines:

Which backend application should receive it?

Example:

<private Beszel hostname>
        ↓
NPM
        ↓
<private Pi 5 host>:<SERVICE_PORT>

Current Proxy Hosts

Current important Proxy Hosts include:

<private development hostname>
<private monitoring hostname>
<private proxy-admin hostname>
<private container-admin hostname>
<private Beszel hostname>

<private development hostname>

Friendly URL:

<private development URL>

Backend:

<private service endpoint>

Target:

VM101 - OpenLabFrame Dev

Path:

Client
   ↓
Pi-hole
   ↓
<private management host>
   ↓
NPM
   ↓
<private development host>:<SERVICE_PORT>
   ↓
VM101

VM101 is intentionally powered off when website development is not taking place.

Therefore:

<private development hostname> unavailable

is normal while VM101 is off.


<private monitoring hostname>

Friendly URL:

<private monitoring URL>

Backend:

<private service endpoint>

Target:

Raspberry Pi 5

Path:

Client
   ↓
Pi-hole
   ↓
NPM
   ↓
<private Pi 5 host>:<SERVICE_PORT>
   ↓
Grafana

<private proxy-admin hostname>

Friendly URL:

<private proxy-admin URL>

Backend:

<internal proxy endpoint>

This allows NPM to proxy its own admin interface.

The direct NPM admin endpoint remains available at:

<private service endpoint>

This direct address is useful for troubleshooting if the friendly hostname is unavailable.


<private container-admin hostname>

Friendly URL:

<private container-admin URL>

NPM backend:

<internal container-admin endpoint>

Direct recovery/backend access:

<private service endpoint>

The direct HTTPS backend is especially useful if NPM itself is unavailable.


Beszel

Friendly URL:

<private Beszel URL>

Backend:

<private service endpoint>

Target:

Beszel Hub on Raspberry Pi 5

Architecture:

Client
   ↓
Pi-hole
   ↓
<private Beszel hostname>
   ↓
<private management host>
   ↓
NPM
   ↓
<private Pi 5 host>:<SERVICE_PORT>
   ↓
Beszel

Wildcard TLS Certificate

NPM uses the internal wildcard certificate:

<private internal wildcard>
<private internal domain>

This allows the same certificate to cover services such as:

<private proxy-admin hostname>
<private container-admin hostname>
<private monitoring hostname>
<private Beszel hostname>
<private development hostname>

Typical SSL Settings

Current normal configuration:

SSL Certificate:
<private internal wildcard>, <private internal domain>

Force SSL:
ON

HTTP/2 Support:
ON

HSTS:
OFF

HSTS Subdomains:
OFF

HSTS has intentionally been left off rather than enabling it without a specific need.


Typical Proxy Host Settings

Normal internal Proxy Host configuration:

Scheme:
http

Forward Hostname / IP:
Backend-specific

Forward Port:
Backend-specific

Cache Assets:
OFF

Block Common Exploits:
ON

Websockets Support:
ON where applicable

WebSockets

WebSocket support is enabled for applications that require or benefit from it.

Examples include applications such as:

  • Beszel
  • Other interactive web applications

This allows long-lived application connections to pass correctly through NPM.


Publicly Accessible

Some NPM Proxy Hosts use:

Access List:
Publicly Accessible

This wording can be misleading.

Within NPM, it means:

NPM does not add an additional Access List login

It does not automatically mean:

The service is exposed to the public Internet

Actual reachability still depends on:

  • DNS
  • Routing
  • Firewall configuration
  • Router exposure
  • Tailscale
  • Public tunnels
  • Port forwarding

The private .<private internal domain> architecture remains internal.


Why Not Add NPM Authentication Everywhere

Adding a separate NPM Access List in front of some applications may interfere with:

  • Mobile applications
  • API clients
  • WebSockets
  • Native login flows

Therefore, native application authentication is preferred where appropriate.


Pi-hole Integration

Pi-hole provides the internal DNS records used by NPM.

Example:

        ↓
<private management host>

NPM then forwards to:

<private Pi 5 host>:<SERVICE_PORT>

General architecture:

Pi-hole
   ↓
VM102
   ↓
NPM
   ↓
Backend

Tailscale Integration

Tailscale allows authorized remote personal devices to reach private NPM services while away from home.

Example:

iPhone on 5G
      ↓
Tailscale
      ↓
Pi 3 DNS / subnet route
      ↓
      ↓
<private management host>
      ↓
NPM
      ↓

The NPM hostname does not need to become publicly exposed for this to work.


Home LAN Access

At home:

Laptop / iPhone
      ↓
Wi-Fi or Ethernet
      ↓
Pi-hole DNS
      ↓
VM102 / NPM
      ↓
Backend

Wi-Fi clients are still LAN clients unless intentionally isolated.


NPM vs VPS Caddy

NPM and VPS Caddy have different roles.

NPM

Primarily used for:

Private/internal homelab services

Examples:

Beszel
Grafana
Portainer
Development site
NPM itself

VPS Caddy

Used for:

Public Jellyfin ingress

Path:

Internet
   ↓
VPS
   ↓
Caddy
   ↓
Tailscale
   ↓
VM100

Jellyfin Does Not Normally Use NPM

Friends accessing Jellyfin use:

jellyfin.openlabframe.com
        ↓
VPS Caddy
        ↓
Tailscale
        ↓
VM100

They do not normally use:

NPM

in that route.

This reduces unnecessary proxy hops.


Cloudflare Is Also Separate

Open Lab Frame uses:

Cloudflare
   ↓
Cloudflare Tunnel
   ↓
Pi 5

rather than NPM.

Current ingress responsibilities are therefore:

Open Lab Frame
→ Cloudflare Tunnel

Public Jellyfin
→ VPS Caddy

Private Web Services
→ NPM

Private Remote Access
→ Tailscale

This separation is intentional.


Direct Backend Access

Friendly NPM hostnames are convenient, but direct backend access remains valuable for troubleshooting.

Examples:

NPM:
<private service endpoint>

Portainer:
<private service endpoint>

Beszel:
<private service endpoint>

<private service endpoint>

Grafana:
<private service endpoint>

These should normally be used only from authorized internal devices.


Monitoring

Uptime Kuma monitors important VM102 services independently.

Current examples include:

VM102 - Management
NPM - VM102
Portainer - VM102

VM102 Monitor

Purpose:

Is VM102 itself reachable?

Target:

<private management host>

Monitor type:

Ping

NPM Monitor

Purpose:

Is NPM responding?

Target:

<private service endpoint>

Healthy response:

HTTP 200

Portainer Monitor

Purpose:

Is Portainer responding?

Target:

<private service endpoint>

Healthy response:

HTTP 200

TLS verification can be ignored for the direct Portainer backend monitor because the local Portainer certificate is not the NPM wildcard certificate.


Why Separate Host and Service Monitoring

Example:

VM102       UP
NPM         DOWN
Portainer   UP

means:

VM is healthy
NPM itself has a problem

Whereas:

VM102       DOWN
NPM         DOWN
Portainer   DOWN

suggests a VM-level or host-level failure.


Beszel Monitoring

Beszel also monitors VM102.

Typical observed values:

CPU:    < 1%
Memory: ~15–16%
Disk:   ~11%

This confirms that the management VM is lightweight.

Beszel and Uptime Kuma have different roles:

Uptime Kuma
→ Is NPM up?

Beszel
→ How much resource is VM102 using?

Recovery Test

A controlled reboot of Proxmox Host was used to validate recovery.

During the reboot:

VM102
NPM
Portainer

became temporarily unavailable.

Uptime Kuma detected failures such as:

NPM - VM102
DOWN

Portainer - VM102
DOWN

After the Proxmox host returned:

  • VM102 started automatically
  • Docker started automatically
  • NPM returned
  • Portainer returned
  • Beszel Agent returned
  • Uptime Kuma changed the monitors back to Up

No manual service startup was required.


Recovery Sequence

HP ProDesk boots
      ↓
Proxmox
      ↓
VM102
      ↓
Linux
      ↓
Docker
      ↓
NPM
Portainer
Beszel Agent
      ↓
Internal services available

Failure Considerations

Because many internal hostnames point to:

<private management host>

an NPM or VM102 outage can affect access to several friendly internal URLs simultaneously.

However, the backend applications may still be healthy.

Example:

NPM DOWN
      ↓

while:

<private Pi 5 host>:<SERVICE_PORT>

may still be working.

This is why direct backend addresses are useful for troubleshooting.


Security

NPM is an administrative system and should remain private.

Important rules:

  • Do not expose the NPM admin interface publicly.
  • Keep VM102 SSH key-only.
  • Keep administrative access authenticated.
  • Do not store NPM passwords in Obsidian.
  • Do not store wildcard private keys in Obsidian.
  • Do not publicly expose private .<private internal domain> services unnecessarily.
  • Use Tailscale for private remote access.
  • Preserve direct backend access for recovery when appropriate.

VM102 Relationship

VM102 - Management is the underlying host.

VM102
├── Nginx Proxy Manager
├── Portainer
└── Beszel Agent

NPM therefore depends on:

HP ProDesk
   ↓
Proxmox
   ↓
VM102
   ↓
Docker
   ↓
NPM

Raspberry Pi 5 Relationship

Several NPM backends run on Raspberry Pi 5.

Examples:

Grafana
→ <private Pi 5 host>:<SERVICE_PORT>

→ <private Pi 5 host>:<SERVICE_PORT>

Beszel
→ <private Pi 5 host>:<SERVICE_PORT>

The Pi 5 hosts the applications.

NPM provides the friendly HTTPS frontend.


VM101 Relationship

VM101 - OpenLabFrame Dev uses:

<private development hostname>

Backend:

<private development host>:<SERVICE_PORT>

VM101 can remain off while NPM remains running.


Architecture Summary

                      Pi-hole
                         ↓
                  <private management host>
                         ↓
                  VM102 / NPM
                         │
       ┌─────────────────┼─────────────────┐
       ↓                 ↓                 ↓
 Raspberry Pi 5       VM101            VM102
     services        dev site       management

Current Friendly URLs

<private development URL>
<private monitoring URL>
<private proxy-admin URL>
<private container-admin URL>
<private Beszel URL>

Related Notes

Homelab Overview
Network Topology
Pi-hole
Tailscale
VM102 - Management
Raspberry Pi 5
VM101 - OpenLabFrame Dev
Beszel
VPS Caddy
Uptime Kuma

Back to Networking

Networking / Note 05

VPS Caddy

In this note

Public reverse-proxy ingress for Jellyfin, bridging the public Internet to VM100 through Tailscale.

Homelab Overview | Network Topology | Tailscale | VM100 - Jellyfin | Jellyfin | Nginx Proxy Manager


Status

State: In use
Role: Public Jellyfin reverse proxy
Public Address: Omitted from public copy
SSH User: <admin-user>
SSH Administration: Public-key authentication
Reverse Proxy: Caddy
Backend Transport: Tailscale
Primary Public Service: Jellyfin


Purpose

The VPS provides a publicly reachable endpoint for services that genuinely need public Internet access.

Its main current role is:

Public Jellyfin ingress

The VPS accepts HTTPS traffic from remote Jellyfin clients and sends that traffic securely back to VM100 - Jellyfin through Tailscale.


Why the VPS Exists

The home Internet connection uses:

Starlink

Starlink operates behind CGNAT.

This means normal inbound IPv4 port forwarding from the public Internet to the home network is not the preferred architecture.

Instead of:

Internet
   ↓
Home Public IPv4
   ↓
Router Port Forward
   ↓
Jellyfin

the homelab uses:

Internet
   ↓
Public VPS
   ↓
Tailscale
   ↓
VM100 / Jellyfin

This avoids depending on inbound connectivity through Starlink CGNAT.


Public Jellyfin URL

Public Jellyfin hostname:

https://jellyfin.openlabframe.com

Remote users connect to this hostname rather than directly to a home IP address.


Full Jellyfin Path

Remote User
      ↓
Internet
      ↓
jellyfin.openlabframe.com
      ↓
Public VPS
<public VPS address omitted>
      ↓
Caddy
      ↓
Tailscale
      ↓
VM100
<private Jellyfin Tailscale endpoint>:<SERVICE_PORT>
      ↓
Jellyfin

VM100 Backend

VM100 - Jellyfin has a Tailscale address:

<private Jellyfin Tailscale endpoint>

Jellyfin listens on:

8096

Therefore, the VPS backend is:

<private Jellyfin Tailscale endpoint>:<SERVICE_PORT>

Caddy

Caddy runs on the VPS and acts as the public HTTPS reverse proxy.

Its role includes:

  • Accepting public HTTPS requests
  • TLS certificate handling
  • Forwarding requests to Jellyfin
  • Maintaining the public jellyfin.openlabframe.com endpoint
  • Using Tailscale to reach VM100 privately

Conceptual Caddy Configuration

The essential Jellyfin configuration is conceptually:

jellyfin.openlabframe.com {
    reverse_proxy <private Jellyfin Tailscale endpoint>:<SERVICE_PORT>
}

This means:

Request for:
jellyfin.openlabframe.com

        ↓

Caddy forwards to:
<private Jellyfin Tailscale endpoint>:<SERVICE_PORT>

Why Tailscale Is Used Between VPS and VM100

The VPS is public.

VM100 is private.

Tailscale bridges those two systems without requiring Jellyfin to be directly exposed from the home router.

Architecture:

VPS
   ↓
Encrypted Tailscale connection
   ↓
VM100

This allows the public endpoint to exist while keeping the Jellyfin backend on the private homelab network.


Public vs Private Components

Public

jellyfin.openlabframe.com
VPS
Caddy HTTPS endpoint

Private

VM100
<private Jellyfin Tailscale endpoint>
Jellyfin backend
Home LAN

The VPS acts as the boundary between the public and private sides.


Why Nginx Proxy Manager Is Not Used Here

Nginx Proxy Manager is intentionally not part of the normal public Jellyfin path.

Current public path:

Remote User
      ↓
VPS Caddy
      ↓
Tailscale
      ↓
VM100 / Jellyfin

Not:

Remote User
      ↓
VPS
      ↓
NPM
      ↓
Jellyfin

Adding NPM would introduce another unnecessary proxy hop.

Caddy can already reach Jellyfin directly through Tailscale.


NPM Role Instead

NPM remains focused primarily on private/internal services such as:

<private Beszel hostname>
<private monitoring hostname>
<private proxy-admin hostname>
<private container-admin hostname>
<private development hostname>

This keeps the roles clean:

Caddy
→ Public Jellyfin

NPM
→ Private homelab services

Local Jellyfin Does Not Need VPS

When a personal device is at home, Jellyfin traffic can remain completely local.

iPhone
   ↓
Home Wi-Fi
   ↓
Pi-hole DNS
   ↓
<private Jellyfin host>
   ↓
VM100 / Jellyfin

The VPS is unnecessary for local playback.


Private Remote Jellyfin Does Not Need VPS

A personal device connected through Tailscale can also reach VM100 privately.

Personal Remote Device
        ↓
Tailscale
        ↓
<private Jellyfin Tailscale endpoint>
        ↓
Jellyfin

Therefore there are three different Jellyfin network paths.


Jellyfin Access Paths

Local Personal Access

Home Device
   ↓
LAN
   ↓
<private Jellyfin host>
   ↓
Jellyfin

Private Personal Remote Access

Remote Personal Device
        ↓
Tailscale
        ↓
<private Jellyfin Tailscale endpoint>
        ↓
Jellyfin

Public Remote / Friend Access

Remote Friend
      ↓
Internet
      ↓
VPS Caddy
      ↓
Tailscale
      ↓
VM100
      ↓
Jellyfin

These are deliberately separate.


Public Access for Friends / Family

Remote users do not need:

  • Tailscale
  • Pi-hole
  • NPM
  • Direct access to the home LAN
  • Knowledge of private VM100 addresses

They simply use:

https://jellyfin.openlabframe.com

The VPS handles the connection back to the home Jellyfin server.


SSH Access

Windows SSH alias:

ssh <vps-alias>

Current Windows SSH configuration:

Host <vps-alias>
    HostName <public VPS address omitted>
    User <admin-user>
    IdentityFile <SSH_KEY_FILE>
    IdentitiesOnly yes

This allows administration without repeatedly typing:

<admin-user>@<public VPS address omitted>

SSH Key

Windows private key:

<SSH_KEY_FILE>

Public-key authentication should remain the normal SSH method.

Do not store the private key contents in Obsidian.


VPS Administration

Typical administration flow:

Windows PC
    ↓
ssh <vps-alias>
    ↓
VPS
    ↓
Caddy / Tailscale / system services

Caddy Configuration

Caddy configuration should be treated as important infrastructure.

Changes should be:

  1. Edited carefully
  2. Validated
  3. Reloaded only after validation
  4. Tested from outside the home network

The exact active Caddyfile should remain documented separately if it becomes substantially more complex.


TLS

Caddy handles HTTPS for:

jellyfin.openlabframe.com

This allows remote Jellyfin clients to connect through a normal trusted HTTPS endpoint.

General TLS path:

Remote Client
      ↓
HTTPS
      ↓
Caddy
      ↓
Private backend

DNS

Public DNS for:

jellyfin.openlabframe.com

ultimately directs public clients toward the VPS.

This is different from the private Pi-hole DNS overrides used inside the homelab.


Split Local Behavior

Selected local devices can resolve Jellyfin differently through Pi-hole.

For the user's own devices at home:

jellyfin.openlabframe.com
        ↓
Pi-hole
        ↓
<private Jellyfin host>

This allows local clients to avoid:

Home
 ↓
Internet
 ↓
VPS
 ↓
Tailscale
 ↓
Back Home

and instead use:

Home Device
      ↓
VM100 directly

Why Local Routing Matters

Sending home Jellyfin traffic through the VPS would:

  • Add unnecessary latency
  • Use Internet upload/download bandwidth
  • Add additional proxying
  • Depend on the VPS unnecessarily

Local DNS allows the user's own devices to use the shortest available route.


Direct Route vs Direct Play

Using the VPS, Tailscale, or LAN determines the network route.

It does not determine whether Jellyfin Direct Plays the media.

For example:

Remote User
→ VPS
→ Jellyfin

could still:

Direct Play

or:

Transcode

depending on:

  • Media codec
  • Container
  • Audio format
  • Subtitle format
  • Client support
  • Bitrate limits

Relationship With Starlink

Starlink remains the single home Internet connection.

Current public Jellyfin architecture does not require changing that.

Home Jellyfin
      ↓
Outbound Tailscale connectivity
      ↓
VPS
      ↓
Public Internet

The Tailscale overlay avoids requiring inbound public IPv4 connectivity through Starlink.


Relationship With Tailscale

Tailscale is critical to the VPS architecture.

The VPS and VM100 must both maintain working Tailscale connectivity.

If the VPS cannot reach:

<private Jellyfin Tailscale endpoint>

then the public Jellyfin backend becomes unavailable even if Jellyfin itself is still healthy.


Failure Scenarios

VPS Down

Public Jellyfin
→ DOWN

while:

Local Jellyfin
→ May still work

Tailscale private Jellyfin
→ May still work

Caddy Down

Public HTTPS endpoint
→ DOWN

while VM100 itself may remain healthy.


VPS Tailscale Down

Caddy
→ Cannot reach VM100
→ Public Jellyfin fails

VM100 Down

Local Jellyfin
→ DOWN

Private Tailscale Jellyfin
→ DOWN

Public VPS Jellyfin
→ DOWN

because all paths ultimately depend on VM100.


Home Internet Down

The VPS may remain online, but its backend becomes unreachable.

VPS
   ↓
Tailscale
   X
Home Jellyfin

Public Jellyfin therefore fails until home connectivity returns.


Monitoring Philosophy

The architecture should ideally distinguish:

Is the VPS alive?
Is Caddy alive?
Can Caddy reach Jellyfin?
Is VM100 alive?
Is Jellyfin itself responding?

These are separate failure points.

Uptime Kuma can be expanded to monitor these individually if useful.


Security

The VPS is publicly reachable and should be treated as Internet-facing infrastructure.

Important rules:

  • Keep SSH public-key-only.
  • Keep the OS updated.
  • Expose only required services.
  • Avoid unnecessary open ports.
  • Keep Caddy updated.
  • Protect SSH credentials.
  • Do not store private SSH keys in Obsidian.
  • Do not store reusable Tailscale authentication keys in Obsidian.
  • Do not expose VM100 directly to the public Internet unnecessarily.
  • Keep the Caddy → VM100 path on Tailscale.

What the VPS Should Not Become

The VPS should not automatically become a catch-all entry point for every homelab service.

Private services such as:

  • Beszel
  • Grafana
  • Portainer
  • NPM
  • Proxmox

should generally remain private unless there is a deliberate reason to expose one publicly.

The current principle is:

Public when necessary
Private by default

Architecture Separation

Current ingress design:

Open Lab Frame

Internet
   ↓
Cloudflare
   ↓
Cloudflare Tunnel
   ↓
Pi 5

Public Jellyfin

Internet
   ↓
VPS Caddy
   ↓
Tailscale
   ↓
VM100

Private Services

Pi-hole
   ↓
VM102 / NPM
   ↓
Backend

Remote Personal Access

Tailscale
   ↓
Private homelab

These paths are intentionally independent.


Current Public Jellyfin Architecture

                      INTERNET
                          │
                          ↓
               jellyfin.openlabframe.com
                          │
                          ↓
                     Public VPS
                   <public VPS address omitted>
                          │
                        Caddy
                          │
                          ↓
                      Tailscale
                          │
                          ↓
                 <private Jellyfin Tailscale endpoint>
                          │
                          ↓
                       VM100
                          │
                          ↓
                      Jellyfin

Future Direction

The current VPS/Caddy architecture can remain even as the home homelab grows.

The future network is still planned around:

Starlink
   ↓
Archer BE550
   ↓
Omada Managed Switch
   ↓
Homelab

The VPS remains useful because the Starlink connection still operates behind CGNAT.


Related Notes

Homelab Overview
Network Topology
Tailscale
Pi-hole
Nginx Proxy Manager
VM100 - Jellyfin
Jellyfin
Raspberry Pi 3
Raspberry Pi 5
Uptime Kuma

Back to Networking