This guide explains how to make apps on umbrelOS 2.0 accessible exclusively within a Tailscale tailnet using custom HTTPS subdomains.
German Version of this guide is in the next comment!
Example:
https://excalidraw.example.com
https://immich.example.com
https://umbrel.example.com
The services remain unreachable from the public internet.
Target architecture
*.example.com
│ DNS: Umbrel's Tailscale IP
▼
100.x.y.z:443
│ Tailscale Serve (TCP forwarder)
▼
Umbrel LAN IP:40443
│ Nginx Proxy Manager
▼
docker-host:<Umbrel app port>
│ Umbrel App Gateway
▼
Umbrel app
Requirements
- umbrelOS 2.0
- Tailscale app installed on the Umbrel
- Nginx Proxy Manager installed on the Umbrel
- your own domain with access to its DNS settings
- a certificate for the domain configured in Nginx Proxy Manager:
example.com
*.example.com
- all devices that should access the apps are members of the same tailnet
No public router port forwarding is required. Tailscale Funnel is not required either.
1. Determine the IP addresses
Two addresses are needed for the configuration:
- local Umbrel LAN IP, for example
192.168.178.12 - Umbrel Tailscale IP, for example
100.x.y.z
The Tailscale IP is reachable only from within the tailnet.
2. Configure DNS
Create a wildcard A record with your DNS provider:
Type: A
Hostname: *.example.com
Value: 100.x.y.z
TTL: 1 hour
Optionally, point the apex domain to the Umbrel as well:
Type: A
Hostname: @
Value: 100.x.y.z
Replace 100.x.y.z with your own Tailscale IP.
Important: use a DNS record, not web forwarding
Your domain provider must use a real DNS A record. HTTP or domain forwarding is unsuitable because it can change the hostname. Nginx Proxy Manager needs the original hostname to route the request to the correct Proxy Host.
Remove any conflicting wildcard A, AAAA, or CNAME records.
3. Understand Nginx Proxy Manager
The Umbrel version of Nginx Proxy Manager uses these ports by default:
Management interface: Port 4498
HTTP entry point: Port 40080
HTTPS entry point: Port 40443
The management interface is normally available at:
http://umbrel.local:4498
Because NPM does not listen directly on host port 443, Tailscale must forward incoming tailnet connections from port 443 to port 40443.
4. Forward Tailscale port 443 to NPM
First, check the current configuration in the Tailscale app terminal:
tailscale serve status
If no Serve configuration exists, the output is:
No serve config
Then configure the TCP forwarder:
tailscale serve --bg --tcp=443 tcp://192.168.178.12:40443
Replace 192.168.178.12 with the Umbrel’s local LAN IP.
Verify the configuration:
tailscale serve status
The output should look similar to this:
Available within your tailnet:
|-- tcp://100.x.y.z:443
|--> tcp://192.168.178.12:40443
What the TCP forwarder does
Tailscale IP:443 → Umbrel LAN IP:40443
The forwarder does not terminate the TLS connection. The TLS certificate is still served by Nginx Proxy Manager.
Remove the forwarder when port forwarding is no longer needed
tailscale serve --tcp=443 off
5. Effect on the Umbrel dashboard
The TCP forwarder takes over the entire port 443 of the Tailscale IP. Therefore, the same address cannot provide both the Umbrel dashboard directly and NPM at the same time:
https://100.x.y.z
After enabling the forwarder, this address reaches NPM instead of the dashboard directly.
The clean solution is to create a dedicated NPM Proxy Host for the Umbrel dashboard, for example:
https://umbrel.example.com
6. Determine an Umbrel app’s port
The relevant port is defined in the app’s umbrel-app.yml, for example:
port: 4422
This port is the public entry point of the Umbrel App Gateway and is the correct target port for NPM.
Do not confuse it with internal container ports. An app may internally use port 80 or 4000 while Umbrel exposes it through port 4422:
NPM → Umbrel port 4422 → App Gateway → container port 80
The port can often also be read from the app’s local launch URL:
http://umbrel.local:4422
7. Create a Proxy Host for an Umbrel app
In Nginx Proxy Manager, go to:
Hosts → Proxy Hosts → Add Proxy Host
Using Excalidraw as an example:
Domain Names: excalidraw.example.com
Scheme: http
Forward Hostname / IP: docker-host
Forward Port: 4422
Recommended options:
- enable
Block Common Exploits - enable
Websockets Supportif the app uses WebSockets
Why docker-host?
The Umbrel version of NPM includes a docker-host companion container. It makes the Umbrel host’s ports available to NPM, so the LAN IP does not have to be entered separately for every Proxy Host.
If docker-host is unavailable in a different installation, use the Umbrel LAN IP instead:
Forward Hostname / IP: 192.168.178.12
8. Configure SSL in NPM
In the Proxy Host’s SSL tab:
- select the existing wildcard certificate
- enable
Force SSL - enable
HTTP/2 Support - leave HSTS disabled initially
The certificate must match the requested hostname, for example:
*.example.com
A certificate installed only on a server operated by the domain provider is not automatically used by NPM. The certificate must be available in NPM itself.
9. Umbrel App Proxy Authentication
umbrelOS 2.0 frequently protects most apps with its own dashboard login. When using a custom domain, this may cause the Umbrel dashboard or login page to appear instead of the app.
Based on my experience, this option must be disabled to access the app directly through NPM.
Internally, this is represented in settings.yml, for example:
appProxyAuthEnabled: false
Security notice
If Umbrel authentication is disabled and the app has no authentication of its own, every member of the tailnet can access the app.
Only disable authentication if:
- the tailnet contains trusted members only, or
- NPM provides an additional Access List or HTTP authentication, or
- the app has its own secure authentication
10. Make the Umbrel dashboard available via a subdomain
Create an additional Proxy Host:
Domain Names: umbrel.example.com
Scheme: http
Forward Hostname / IP: docker-host
Forward Port: 80
Recommended options:
- enable
Websockets Support - enable
Block Common Exploits - select the wildcard certificate
- enable
Force SSL - enable
HTTP/2 Support
The dashboard is then available at:
https://umbrel.example.com
11. Common problems and solutions
The Umbrel dashboard appears instead of the app
Possible causes:
- Umbrel App Proxy Authentication is still enabled.
- NPM uses the wrong target port.
- The browser is using a previously cached redirect.
- The domain uses web forwarding instead of a DNS A record.
- The request reaches the Umbrel dashboard directly on port 443 instead of reaching NPM.
Diagnostic steps:
http://umbrel.local:<APP-PORT>
If the app appears there, the app and its Umbrel port are working.
Then test NPM directly:
https://app.example.com:40443
If that works, the problem is the Tailscale forwarder from port 443 to port 40443.
Also test in a private browser window to rule out cached redirects.
NPM shows “Bad Gateway” or error 502
Check:
- Is the app running?
- Is the port from
umbrel-app.ymlcorrect? - Does
http://umbrel.local:<APP-PORT>work directly? - Is
docker-hostused as the Forward Host? - If
docker-hostdoes not work, try the Umbrel LAN IP.
Port mapping entered as a custom environment variable
An entry such as:
Ports: '8182:4422'
is not a Docker port mapping. It only creates an environment variable named Ports inside the container.
A real Compose port mapping would use the ports key. However, Compose files generated by Umbrel should not be edited manually because changes may be overwritten during updates or restarts.
NPM normally does not require an additional port mapping. The official app port from umbrel-app.yml is sufficient.
Direct access through the tailnet IP no longer works
This is expected after configuring the TCP forwarder:
Tailscale IP:443 → NPM:40443
Port 443 on a single IP cannot be used directly by both the dashboard and NPM at the same time. The dashboard should therefore also be configured as a dedicated subdomain in NPM.
DNS does not work on individual devices
Check:
- The device is connected to Tailscale.
- The DNS A record returns the correct
100.x.y.zaddress. - No conflicting AAAA record exists.
- The router, browser, or DNS resolver does not block private or CGNAT addresses through DNS rebinding protection.
- The DNS cache has been cleared or the TTL has expired.
12. Checklist for each additional app
- Determine the app port from
umbrel-app.ymlor its local app URL. - Test the app locally at
http://umbrel.local:<PORT>. - Disable Umbrel App Proxy Authentication if necessary.
- Create an NPM Proxy Host:
Domain: app.example.com
Scheme: http
Forward Host: docker-host
Forward Port: <APP-PORT>
- Select the wildcard certificate.
- Enable Force SSL.
- Enable WebSockets if required.
- Test from a device connected to the tailnet.
- Check whether the app has its own authentication.
13. Security model
This configuration does not make the apps publicly accessible. However, they are accessible to every device and user whose Tailscale access rules allow access to the Umbrel.
Recommendations:
- restrict Tailscale ACLs or Grants
- remove tailnet members that are no longer needed
- use strong app passwords
- use NPM Access Lists for unprotected apps
- disable Umbrel authentication only when necessary
- do not forward ports 80, 443, 40080, or 40443 on the internet router
- do not enable Tailscale Funnel when private access only is intended