* move secondary sections to separate files * add reverse proxy section * relocate files into directories, update architectural diagram * consolidate update steps for docker installations * add missing llama.cpp dependency * standardize indentation for all yaml blocks * add detailed inference engine comparison, update AI usage disclosure * update diagram
19 KiB
Remote Access
Remote access refers to the ability to access your server outside of your home network. For example, when you leave the house, you aren't going to be able to access http://<your_server_ip>, because your network has changed from your home network to some other network (either your mobile carrier's or a local network in some other place). This means that you won't be able to access the services running on your server. There are many solutions on the web that solve this problem and we'll explore some of the easiest-to-use here.
Table of Contents
Prerequisites
This guide assumes the following:
- A running server with at least one service exposed on a port (e.g., Open WebUI on port 3000).
- Basic comfort with the terminal (running commands, editing files with
nanoor a similar editor). - A Tailscale account (free tier is sufficient for personal use).
- (Optional, for reverse proxy section) A domain name registered with any registrar (~$2–15/year). You'll also need a Cloudflare account to manage DNS records.
To find your server's Tailscale IP, log in to the Tailscale Admin Console and look up your device. Its Tailscale IP is displayed next to the device name (it will be in the 100.x.y.z range).
Tailscale
Tailscale is a peer-to-peer VPN service that combines many services into one. Its most common use-case is to bind many different devices of many different kinds (Windows, macOS, iOS, Android, etc.) on one virtual network. This way, all these devices can be connected to different networks but still be able to communicate with each other as if they were all on the same local network. Tailscale is not completely open source (its GUI is proprietary), but it is based on the Wireguard VPN protocol and the remainder of the actual service is open source. Comprehensive documentation on the service can be found here and goes into many topics not mentioned here - I would recommend reading it to get the most out of the service.
On Tailscale, networks are referred to as tailnets. Creating and managing tailnets requires creating an account with Tailscale (an expected scenario with a VPN service) but connections are peer-to-peer and happen without any routing to Tailscale servers. This connection being based on Wireguard means 100% of your traffic is encrypted and cannot be accessed by anyone but the devices on your tailnet.
Installation
First, create a tailnet through the Admin Console on Tailscale. Download the Tailscale app on any client you want to access your tailnet from. For Windows, macOS, iOS, and Android, the apps can be found on their respective OS app stores. After signing in, your device will be added to the tailnet.
For Linux, the steps required are as follows.
-
Install Tailscale
curl -fsSL https://tailscale.com/install.sh | sh -
Start the service
sudo tailscale up
For SSH, run sudo tailscale up --ssh.
Exit Nodes
An exit node allows access to a different network while still being on your tailnet. For example, you can use this to allow a server on your network to act as a tunnel for other devices. This way, you can not only access that device (by virtue of your tailnet) but also all the devices on the host network its on. This is useful to access non-Tailscale devices on a network.
To advertise a device as an exit node, run sudo tailscale up --advertise-exit-node (or sudo tailscale set --advertise-exit-node). To allow clients using this exit node to access the local network, configure those clients with --exit-node-allow-lan-access when connecting to the exit node.
Local DNS
If one of the devices on your tailnet runs a DNS-sinkhole service like Pi-hole, you'll probably want other devices to use it as their DNS server. Assume this device is named poplar. This means every networking request made by any device on your tailnet will send this request to poplar, which will in turn decide whether that request will be answered or rejected according to your Pi-hole configuration. However, since poplar is also one of the devices on your tailnet, it will send networking requests to itself in accordance with this rule and not to somewhere that will actually resolve the request. Thus, we don't want such devices to accept the DNS settings according to the tailnet but follow their otherwise preconfigured rules.
To reject the tailnet's DNS settings, run sudo tailscale up --accept-dns=false.
Third-Party VPN Integration
Tailscale offers a Mullvad VPN exit node add-on with their service. This add-on allows for a traditional VPN experience that will route your requests through a proxy server in some other location, effectively masking your IP and allowing the circumvention of geolocation restrictions on web services. Assigned devices can be configured from the Admin Console. Mullvad VPN has proven their no-log policy and offers a fixed $5/month price no matter what duration you choose to pay for.
To use a Mullvad exit on one of your devices, first find the exit node you want to use by running sudo tailscale exit-node list. Note the IP and run sudo tailscale up --exit-node=<your_chosen_exit_node_ip>.
Warning
Ensure the device is allowed to use the Mullvad add-on through the Admin Console first.
Accessing Services
Now that Tailscale is set up, we can access services outside of the local network in two different ways. Say we have Open WebUI running on port 3000.
- Reference a device's Tailscale IP:
http://100.x.y.z:3000 - Reference the tailnet name:
https://<device>.<tailnet_name>.ts.net:3000
For example, if the device running Open WebUI is called poplar and the tailnet is called beach-volleyball, the URL would be https://poplar.beach-volleyball.ts.net:3000.
However, this is limited to Tailscale: their domain, based on your tailnet name. In the next section, we will go over the industry-standard way of accessing services behind a secure HTTPS connection: using a custom fully-qualified domain name, or FQDN.
Reverse Proxy
We will introduce a reverse proxy engine into our setup here. A reverse proxy enables obfuscation of the actual IPs that services run on and makes it easy to access services by their name, instead of their port. We do this by assigning a subdomain to a port. A subdomain is a domain that is part of another domain. For example, shop.example.com is a subdomain of example.com. By mapping a subdomain to a port, we can reach services in a far more intuitive way than using device IP + port.
Let's take the Open WebUI on port 3000 example again. This section requires the ownership of a domain; for this example, we'll use example.com. The flow of information is as follows:
Request -> https://owui.example.com -> 100.x.y.z:3000 -> Open WebUI
There are many reverse proxy engines available but one, in particular, is ubiquitous and very easy to work with: Caddy.
Caddy
🌟 GitHub
📖 Documentation
Caddy is a web server and reverse proxy written in Go. Its primary advantage over other reverse proxies is automatic HTTPS certificate provisioning via Let's Encrypt, which happens with zero configuration. Caddy also supports HTTP/3 out of the box and uses a human-friendly configuration format called Caddyfile.
Installation
On Debian/Ubuntu-based Linux distributions:
-
Install prerequisites and add Caddy's GPG key:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg -
Add the Caddy repository:
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list -
Update packages and install Caddy:
sudo apt update sudo apt install caddy
On other platforms, refer to the official installation guide.
Configuration
Caddy's main configuration file is the Caddyfile, typically located at /etc/caddy/Caddyfile. Its syntax is straightforward: each site block specifies a domain and the directives for handling requests.
-
Modify the Caddyfile:
sudo nano /etc/caddy/Caddyfile -
Add a route for a subdomain to a local service:
owui.example.com { reverse_proxy 100.x.y.z:3000 }Caddy will automatically obtain and renew an SSL certificate for
owui.example.comand serve traffic over HTTPS. No manual certificate configuration is required. -
Configure other services (if required):
owui.example.com { reverse_proxy 100.x.y.z:3000 } dashboard.example.com { reverse_proxy 192.168.a.b:3001 } grafana.example.com { reverse_proxy 100.x.y.z:3002 } -
After editing the Caddyfile, format and reload the configuration:
sudo caddy fmt --overwrite /etc/caddy/Caddyfile sudo systemctl reload caddyThe
caddy fmtcommand formats the Caddyfile in-place. Thesystemctl reloadcommand sends a signal to the running Caddy service to reload its configuration without dropping connections.
Caddy runs as a systemd service by default on Linux. Use sudo systemctl status caddy to check its state and journalctl -u caddy to view logs. To ensure Caddy starts automatically on boot, run:
sudo systemctl enable caddy
Tip
IPs are not limited to Tailscale-specific IPs or a single device! You can route to any device, with or without Tailscale, that Caddy can reach via IP. Tailscale simply makes a setup like this more secure: only users in your tailnet can access
100.x.y.z, so only those users have their requests resolved. Any unauthorized actor attempting to go to*.example.comwill be met with a "Server not found" response. This automatic rejection of unauthorized users requires extra configuration without Tailscale but is definitely doable. Caddy supports built-in rate limiting (rate_limit) and access control (request_body,respond) for securing exposed services.
Cloudflare
Before Caddy can route traffic to your services, your domain's Domain Name System (DNS) records need to resolve to your server. Machines only communicate in IPs; a machine does not inherently "know" where a certain domain should point. Thus, a DNS provider is a huge registry of pairs of IP addresses and domains: a way for devices to chart what domains point to what IPs. This section covers setting up DNS with Cloudflare and obtaining Let's Encrypt certificates using the DNS-01 challenge. Cloudflare is a large compan that provides a variety of internet-related services, DNS being one of the largest.
Important
Exposing services to the public Internet via custom domains requires opening ports 80 and 443 on your router, which can be dangerous if misconfigured. If you're unfamiliar with firewall rules and service hardening, stick to Tailscale IPs as backend routes. Tailscale uses hole punching to bypass port forwarding entirely, keeping your services accessible only to authorized tailnet users.
Setting up Cloudflare
-
Add your domain to Cloudflare: Create a free account at Cloudflare and add your domain. Cloudflare will provide nameservers to replace your registrar's defaults.
-
Create a wildcard DNS record: In Cloudflare's DNS dashboard, add an A record with the name
*pointing to your server's public IP address. This single record will resolve all subdomains (owui.example.com,dashboard.example.com, etc.) to the same IP.Type Name IPv4 address Proxy status A *<your_tailscale_ip>DNS only (grey cloud) Set Proxy status to DNS only (grey cloud) so traffic is routed directly to your server. This will most likely happen automatically because Tailscale IPs are considered "reserved" by Cloudflare.
-
Verify propagation: Run
dig owui.example.comor visit DNS Checker to confirm the wildcard record resolves correctly.
Note
Cloudflare isn't the only option. Caddy supports DNS-01 challenges with 75+ DNS providers through the caddy-dns project, including AWS Route 53, DigitalOcean, Google Cloud DNS, and more. Replace
cloudflarein the Caddyfile and installation steps with your provider's module name.
Obtaining Let's Encrypt Certificates with Caddy
ACME (Automatic Certificate Management Environment) is a protocol that automates SSL/TLS certificate issuance and renewal. It offers three types of challenges to verify domain ownership: HTTP-01, TLS-ALPN-01, and DNS-01. The HTTP-01 challenge serves a file on port 80, and TLS-ALPN-01 uses port 443, which the certificate authority (CA) uses to verify ownership. However, with Tailscale, neither port is directly accessible from the public Internet. In this case, we use the DNS-01 challenge with Caddy's Cloudflare module, which verifies ownership by creating temporary TXT records in your domain's DNS:
-
Create a Cloudflare API token: Go to Profile > API Tokens > Create Token, and use the Edit zone DNS template. Select your domain and save the token.
-
Export the token as an environment variable:
echo 'export CLOUDFLARE_API_TOKEN=your-token-here' >> ~/.bashrc source ~/.bashrc -
Install Caddy with the Cloudflare DNS module:

- Visit the Caddy download page.
- Select your platform (Linux amd64, for this guide).
- Click on the
caddy-dns/cloudflaremodule. Its element box will turn blue. - Right-click the download button and copy its URL.
- After getting the URL, run the following:
sudo curl -o /usr/bin/caddy -L "<url_from_step_4>"
-
Configure the Caddyfile to use DNS-01 challenges:
*.example.com { tls { dns cloudflare {env.CLOUDFLARE_API_TOKEN} } reverse_proxy 100.x.y.z:3000 }Caddy will automatically create and verify TXT records through the Cloudflare API to prove domain ownership, then issue a wildcard certificate for
*.example.com.
Tip
A wildcard certificate (
*.example.com) is very convenient when running many subdomains. Going this route, compared to having a DNS record for each subdomain separately, you only need to provision and renew a single certificate instead of one per subdomain. It is technically less secure in case of a breach but, if you're using any kind of peer-to-peer VPN, this should not really be a problem.
Warning
If your setup is like mine, Caddy may send a malformed DNS-01 challenge to Cloudflare. In case your certificate isn't issued automatically, log in to the Cloudflare dashboard, navigate to your domain's DNS records, open the TXT record for
*, and wrap the challenge in""(double quotation marks). This will allow the challenge to be read by Cloudflare successfully.
Troubleshooting
-
Caddy fails to start. Run
sudo systemctl status caddyto check for errors. Runsudo caddy validate --config /etc/caddy/Caddyfileto check for issues before reloading. -
Certificate isn't being issued. Check Caddy's logs with
journalctl -u caddy -n 50to see the ACME challenge output. Ensure your Cloudflare API token has the Edit zone DNS permission and that theCLOUDFLARE_API_TOKENenvironment variable is set correctly (echo $CLOUDFLARE_API_TOKEN). Also make sure that your DNS-01 challenge is not malformed - it should be wrapped in double quotation marks. -
Domain doesn't resolve. Run
dig owui.example.comor visit DNS Checker to verify your wildcard A record propagated. If you recently changed the record, DNS propagation can take up to 24 hours (though Cloudflare typically propagates within minutes). -
Service returns a 502/504 error. This means Caddy can reach the domain but cannot connect to the backend service. Verify the service is running on the expected port and that the IP address in the Caddyfile is correct. You can test from the Caddy host with
curl http://100.x.y.z:3000. -
Can't access the domain from outside your network. Ensure port 443 is forwarded on your router to your server's local IP. With DNS-01 challenges, only port 443 needs to be open (port 80 is not required). If you're unsure about port forwarding, stick to accessing services via Tailscale IPs directly.
Updating
Tailscale
Rerun the installation script:
curl -fsSL https://tailscale.com/install.sh | sh
Caddy
Update and reinstall the package:
sudo apt update
sudo apt install --only-upgrade caddy
Next Steps
With remote access configured, your self-hosted services are reachable from anywhere. Consider the following to further improve your setup:
- Monitor your services with tools like Uptime Kuma or Healthchecks to get notified if a service goes down.
- Back up your configuration files, especially the Caddyfile and any service-specific configs.
- Explore Tailscale Funnel (
tailscale funnel) for selectively exposing services to the public Internet without a domain or reverse proxy. - Read the Tailscale KB and Caddy documentation for advanced topics not covered here.