
Reach a Raspberry Pi through an outbound tunnel instead of opening the router
A Raspberry Pi often sits behind a home router, carrier-grade NAT, a cellular connection, or a network that does not permit inbound connections. With Localtonet, the client on the Pi establishes an outbound connection to one of our relay servers and publishes only the service you select. This tutorial uses Raspberry Pi SSH as the complete example: operating-system checks, installing the documented Linux package, running the client as a systemd service, creating and starting a TCP tunnel, testing from outside, hardening OpenSSH, and troubleshooting each layer. It also shows how VNC, xrdp and an HTTP dashboard fit the same pattern, and when File Server, UDP or VPN Manager is a better match.
๐ What's in this guide
How Raspberry Pi access works without port forwarding
Devices on a home or small-office network normally use private IP addresses that are not directly reachable from the public internet. The router translates outbound connections, but it does not know which internal device should receive an unsolicited inbound connection. Traditional remote access solves that with an inbound port-forwarding rule, a public IP address, and often dynamic DNS.
Localtonet reverses the direction of the first connection. The Localtonet client runs on the Raspberry Pi, or on another local device that can reach the Pi, and initiates an outbound connection to a Localtonet relay server. While the selected tunnel is running, remote traffic sent to its assigned public URL, or public host and port, is forwarded to the configured local target. You do not add an inbound router rule, change the firewall to accept internet traffic, configure a VPN, or obtain a public IP address.
This model works behind conventional NAT and in common carrier-grade NAT situations because the Pi never has to accept a new connection directly from the internet. It does need working outbound connectivity to the Localtonet service, so a restrictive network that blocks or interrupts that connection remains an operational limit.
Creating a Localtonet tunnel saves its configuration, but it does not make it available. You must start it with the Start button. The endpoint works only while the selected device is connected and the tunnel is running. Later you can stop the tunnel or delete its configuration.
Removing the router rule does not make the endpoint anonymous or open. The public host and port lead to your SSH server, and the SSH server still authenticates the user and controls the resulting operating-system session. For a web application, the application remains responsible for login, authorization, session handling and safe administrative routes. Treat every running tunnel as an internet-facing entry point and secure the service behind it accordingly.
Prerequisites and Raspberry Pi compatibility checks
This tutorial assumes a Raspberry Pi running Raspberry Pi OS (or another Debian-based system) with administrative access. Raspberry Pi OS comes in 32-bit and 64-bit variants, and the architecture of the installed operating system decides which Localtonet client you can use. Our documentation lists Linux (ARM) and Linux (ARM-64) downloads, and separate installable DEB packages for ARM64. The DEB packages have two system requirements that matter on a Pi: systemd 247 or newer and OpenSSL 3. Debian 12 and newer meet that bar, and so do Ubuntu 22.04 and newer.
Before installing anything, confirm the operating system, the package architecture and the library versions:
cat /etc/os-release
dpkg --print-architecture
uname -m
getconf LONG_BIT
systemctl --version | head -n 1
openssl version
Read the results as follows:
dpkg --print-architecturereportsarm64for a 64-bit Raspberry Pi OS andarmhffor a 32-bit one. This value decides which.debfile can be installed, so trust it over the other commands.uname -mreports the running kernel. A 32-bit Raspberry Pi OS can run a 64-bit kernel on newer boards, souname -mmay sayaarch64whiledpkgsaysarmhf. In that case you still have a 32-bit userland.systemctl --versionmust show systemd 247 or newer, andopenssl versionmust show OpenSSL 3.x, for the DEB package route.- A Debian 11 based Raspberry Pi OS ships OpenSSL 1.1.1, so it does not satisfy the OpenSSL 3 requirement for the packages. A Debian 12 based release does.
Never install an x64 or amd64 build on an ARM-based Raspberry Pi.
What you need before starting
- A powered Raspberry Pi with a maintained operating system and a user that can run
sudo. - A working Ethernet or Wi-Fi connection with outbound internet access.
- A Localtonet account. You can use the Localtonet registration page if you do not have one yet.
- A device-specific authentication token for the Pi, taken from your token list in the Localtonet dashboard.
- The Localtonet package that matches the Pi: the ARM64 DEB for a 64-bit Raspberry Pi OS, or the Linux (ARM) download for a 32-bit one.
- An SSH user on the Pi and an SSH client on the remote computer you will test from.
- Permission from the device and network owner to publish and access the service.
The target service must be running before the tunnel can deliver useful traffic. A tunnel forwards a connection to an IP address and port, but it cannot enable SSH, repair a stopped application, create an operating-system account or grant filesystem permissions.
A Localtonet authentication token identifies the client device that runs the tunnel. Do not paste it into public commands, screenshots, repositories, issue reports or shared documents. In this guide the token goes into a root-owned file with restricted permissions, never onto a command line that lands in shell history, and every example uses the placeholder DUMMY_AUTH_TOKEN instead of a real value.
Choose the access method by Raspberry Pi workload
"Remote Raspberry Pi access" can mean several different things. A terminal session uses SSH over TCP. A browser dashboard speaks HTTP. A remote desktop uses VNC or RDP, both TCP services. Some telemetry or real-time applications use UDP. A file-sharing task targets a folder rather than a listening application. Reaching several private systems is a network-connectivity problem rather than a single-service publishing problem.
| Localtonet option | Raspberry Pi use case | Local target | Public access model |
|---|---|---|---|
| HTTP/s tunnel | Web dashboards, development sites, HTTP APIs | Reachable local IP address and HTTP port | Public HTTPS address |
| TCP tunnel | SSH, VNC, xrdp and other raw TCP services | Reachable local IP address and TCP port | Public host and port |
| UDP tunnel | An application that explicitly requires UDP | Reachable local IP address and UDP port | UDP forwarding endpoint |
| Combined UDP/TCP | A service that requires both transports | Relevant local IP address and port | Both protocol paths |
| File Server | Logs, media, project artifacts, controlled file exchange | Local folder path | Default browser interface, SFTP or WebDAV workflow |
| VPN Manager | Private mesh connectivity or access across local LANs | Private network configuration | Private connectivity governed by firewall rules |
Standard HTTP, TCP, UDP, combined UDP/TCP and File Server tunnels are not VPN connections. VPN Manager is our actual private mesh VPN feature, with granular firewall rules and the ability to bridge local LANs. See our VPN Manager overview when the requirement is network-level private access rather than one SSH endpoint.
Enable and test SSH on the Raspberry Pi
Complete this section locally before configuring Localtonet. If SSH does not work on the Pi itself or across the local network, adding a tunnel will not fix the underlying service.
Enable SSH with Raspberry Pi OS configuration
On Raspberry Pi OS with a local terminal, open the configuration utility:
sudo raspi-config
Open Interface Options, select SSH, and enable the SSH server. Menu wording can change between releases, so consult the Raspberry Pi remote-access documentation if your utility differs. Raspberry Pi Imager can also preconfigure SSH while writing the operating-system image, which is useful for a headless Pi that has not booted yet. Do not assume a default user or password exists; set your own during imaging.
Confirm the SSH service state
sudo systemctl status ssh
sudo ss -ltnp
The status should show the service as active. If it is inactive or failed, read the error before going further, and enable it at boot with sudo systemctl enable --now ssh if needed. In the ss output, look for the port sshd is listening on. Port 22 is the default, but do not assume it if you changed the server configuration. The port entered in Localtonet must match the port on which sshd is actually listening.
Test SSH locally
From the Raspberry Pi itself, test the loopback path:
ssh <username>@127.0.0.1
Replace <username> with a real non-administrative account. On the first connection, inspect the host-key prompt before accepting it. To test from another computer on the same LAN, first identify the Pi's addresses:
hostname -I
ip -br addr
ssh <username>@<raspberry-pi-lan-address>
The LAN test matters when the Localtonet client will run on a different device. It proves that the client device's network can reach the Pi and that SSH listens on an appropriate interface.
Use 127.0.0.1 as the tunnel target when the Localtonet client and the SSH server run on the same Raspberry Pi. Loopback always refers to the machine running the client process. If the client runs on another computer, 127.0.0.1 refers to that computer, not the Pi, so enter the Pi's reachable LAN address and verify that connection from the client device first.
Install and connect the Localtonet client
For an unattended Raspberry Pi, we recommend the installable Linux package, because it runs the client as a systemd service that you can enable at boot, inspect with journalctl, and upgrade in place. The sequence below is the documented one for the Debian and Ubuntu ARM64 DEB package, which is the right choice for a 64-bit Raspberry Pi OS based on Debian 12 or newer. Download localtonet-linux-arm64.deb from the Linux packages area of the Localtonet documentation page.
Install the downloaded package
Open a terminal in the directory that contains localtonet-linux-arm64.deb and install it with apt so that dependencies are resolved: sudo apt install ./localtonet-linux-arm64.deb.
Set your AuthToken and check the configuration
Edit /etc/localtonet/auth-token as root and replace DUMMY_AUTH_TOKEN with your token on a single line. Then protect the file with sudo chmod 600 /etc/localtonet/auth-token and run sudo localtonet --headless --authtoken-file /etc/localtonet/auth-token --check-config.
Start the service
Once the configuration check succeeds, run sudo systemctl enable --now localtonet.service, then read the logs with sudo journalctl -u localtonet.service.
The commands in order
sudo apt install ./localtonet-linux-arm64.deb
sudo nano /etc/localtonet/auth-token
# replace DUMMY_AUTH_TOKEN with your token, on one line, then save
sudo chmod 600 /etc/localtonet/auth-token
sudo localtonet --headless --authtoken-file /etc/localtonet/auth-token --check-config
sudo systemctl enable --now localtonet.service
sudo journalctl -u localtonet.service
Any text editor that runs as root will do for the token file; nano is just a common choice on Raspberry Pi OS. The file holds the token and nothing else, on a single line. Do not wrap it in quotes, add a label, or leave the placeholder in place.
enable --now does two things at once. It marks the service to start at every boot, and it starts it immediately. That is the documented mechanism for starting the Localtonet client at boot, which is why reboot recovery of the client does not depend on improvised scripts. After the service starts, open your Localtonet dashboard and confirm that the token you used shows as connected before you create a tunnel.
The documented DEB and RPM packages are for x64 and ARM64. A 32-bit Raspberry Pi OS reports armhf from dpkg --print-architecture and cannot install the ARM64 DEB. Use the Linux (ARM) download from the documentation page instead, and follow the instructions shown there. The supplied documentation does not give a systemd service procedure for that archive, so we do not describe one here. If you need the documented service-based setup, install a 64-bit Raspberry Pi OS on a board that supports it, and check the Raspberry Pi documentation for your model.
If you use VPN Manager on the same Pi
Our documentation uses a separate service for VPN use. You load the tun module, disable localtonet.service, and enable localtonet-vpn.service with sudo systemctl enable --now localtonet-vpn.service; logs are read with sudo journalctl -u localtonet-vpn.service. The SSH tunnel in this guide uses the standard localtonet.service. Do not switch to the VPN service unless you intend to use VPN Manager, because the two services replace each other in that documented procedure.
If you intentionally run the client on another LAN computer instead of the Pi, use a token for that computer, keep it powered and connected, and ensure that it can reach the Pi. The tunnel depends on the selected client, not merely on a tunnel configuration in the dashboard.
Create a Localtonet TCP tunnel for Raspberry Pi SSH
Once SSH works locally and the device shows as connected, create the TCP tunnel. SSH is a raw TCP service, so it should not be placed behind an HTTP tunnel. The sequence below follows the same pattern as the documented File Server flow (open the tunnel page, select the token and server, enter the local target, then press Start from the list) adapted to a TCP target.
The official TCP tunnel page was not among the documentation we could confirm while updating this guide, so we do not claim exact labels, optional fields or plan-dependent settings for it. The steps below use only the inputs that every local-target tunnel needs: a token, a relay server, and a local IP address and port. If the live dashboard shows additional fields, leave them at their defaults unless you have a specific reason and have read the current documentation.
Open the TCP tunnel page
In the Localtonet dashboard, go to the TCP tunnel page. Confirm that you are creating a TCP tunnel rather than HTTP, UDP, File Server, Proxy Server or VPN Manager.
Select the AuthToken of the Raspberry Pi
Choose the token you placed in /etc/localtonet/auth-token on the Pi. If the client runs on another computer, select that computer's token instead.
Select a relay server
Pick a server or region from the values your dashboard currently offers. Do not copy a server code from an old tutorial.
Enter the SSH target IP address and port
With the client on the Pi, enter 127.0.0.1 and the verified SSH port, commonly 22. With the client on another device, enter the Pi's reachable LAN address and the verified port. The target must match what you tested in the SSH section.
Create the tunnel and press Start
Create the tunnel, then press the Start button in the tunnel list. Creating the configuration alone does not start it. Confirm that the device stays connected and that the tunnel shows a running state.
Record the assigned public host and port
Use the host and port shown for this running tunnel. Do not substitute the Pi's private LAN address, assume the public port is 22, or publish the real endpoint in screenshots.
Stopping and deleting the tunnel are lifecycle actions rather than part of creating it, so they are covered under operations later in this guide. Use the Localtonet documentation alongside the live dashboard whenever labels or options differ from this walkthrough. Relay, region, endpoint and plan availability should always come from the current product rather than a long-lived tutorial.
Verify the local service and public endpoint
Verification proceeds from the inside out. Prove that the SSH server runs, prove that the Localtonet client service runs and is connected, prove that the tunnel is running, and only then test from a genuinely external network. This order also tells you which layer is failing when something breaks.
| Layer, in startup order | How to check it | What healthy looks like |
|---|---|---|
| 1. Power and network | ip -br addr and curl -I https://localtonet.com |
The Pi has an address and reaches the internet outbound |
| 2. SSH listener | systemctl is-active ssh and sudo ss -ltnp |
active, and sshd listening on the expected port |
| 3. Localtonet service | systemctl is-active localtonet.service and sudo journalctl -u localtonet.service -n 50 --no-pager |
active, with no repeating errors in the log |
| 4. Device token | Dashboard token list | The token used on the Pi shows as connected |
| 5. TCP tunnel | Dashboard tunnel list | The tunnel shows a running state and a public host and port |
| 6. External SSH test | ssh -p <public-port> <user>@<public-host> from another network |
Host-key prompt or key authentication succeeds |
Keep this table next to you after a reboot or an outage. The first layer that does not look healthy is the one to fix; changing later layers will not help.
1. Recheck the local listener
systemctl is-active ssh
sudo ss -ltnp | grep sshd
If the service is not active or the expected port is absent, fix SSH before touching the tunnel. A TCP connection refused error generally means nothing listens at the configured target address and port.
2. Recheck the exact target path
With the client on the Pi, repeat the loopback test:
ssh <username>@127.0.0.1
With the client on another LAN device, run the LAN test from that device instead:
ssh <username>@<raspberry-pi-lan-address>
If the LAN test fails locally, the same address fails as a tunnel target. Check the Pi's address, network segmentation, SSH bind configuration and local firewall first.
3. Confirm the Localtonet service and runtime state
systemctl status localtonet.service --no-pager
sudo journalctl -u localtonet.service -n 50 --no-pager
Then verify two conditions in the dashboard: the selected device token reports connected, and the TCP tunnel is running after you pressed Start. A connected device does not imply that every saved tunnel is started, and a started tunnel cannot stay useful if its client disconnects.
4. Test from outside the Raspberry Pi network
Use a computer or phone tethered to a different internet connection, such as a mobile network. Testing from the same Wi-Fi network may not exercise the public path and can hide routing or name-resolution problems. On the remote computer:
ssh -p <assigned-public-port> <username>@<assigned-public-host>
Replace both placeholders with the host and port shown in the dashboard. The public port is not necessarily the Pi's local SSH port. On the first connection, verify the host key through a trusted channel before accepting it, and investigate rather than bypass a warning if a known-host entry later changes unexpectedly.
5. Test authorization boundaries
Confirm that the intended account can do only what the task requires, that invalid credentials are rejected, and that an ordinary user cannot perform administrative actions without the operating system's normal authorization. Network reachability is only one layer of the test.
SSH must still authenticate the user, but assigned public hosts and ports are operational information. Redact the endpoint, username, token, private IP addresses and host keys from screenshots or support material unless disclosure is necessary through an approved private channel.
Harden SSH and limit the exposure
Avoiding inbound router port forwarding removes a router rule, but the public TCP endpoint can still receive connection attempts while it is active. Secure SSH as though it were reachable from an untrusted network, because while the tunnel is running it is.
A TCP tunnel forwards traffic to SSH. It does not make weak passwords safe, bypass security policy, or decide what an authenticated account may do. SSH configuration, account security, updates and least privilege remain essential.
Create a key pair and authorize it
On the remote computer you will connect from, generate a modern key. Protect it with a passphrase:
ssh-keygen -t ed25519 -C "pi-admin"
Copy the public key to the Pi's account while you are still on the local network, using the LAN address:
ssh-copy-id -i ~/.ssh/id_ed25519.pub <username>@<raspberry-pi-lan-address>
ssh-copy-id appends the public key to ~/.ssh/authorized_keys on the Pi and sets suitable permissions. If you add the key by hand instead, make sure the directory and file are private to the account:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
Only the public key belongs on the Pi. Never copy the private key to the Pi, the Localtonet dashboard or a tunnel configuration. Test key login explicitly, and force the client to refuse a password so that you know the key is doing the work:
ssh -i ~/.ssh/id_ed25519 -o PasswordAuthentication=no <username>@<raspberry-pi-lan-address>
Disable password login safely
Once key login works, you can turn off password authentication. Do this from a session you can recover. Open a second SSH session and leave it connected for the whole procedure, so that a configuration mistake cannot lock you out. On current Raspberry Pi OS and Debian releases, /etc/ssh/sshd_config includes files from /etc/ssh/sshd_config.d/, which is the tidy place for your own settings. First look for existing settings that might override yours, because OpenSSH uses the first value it reads for most options:
sudo grep -ri "PasswordAuthentication\|PermitRootLogin" /etc/ssh/sshd_config /etc/ssh/sshd_config.d/
Create a drop-in file with your settings:
sudo nano /etc/ssh/sshd_config.d/10-localtonet-hardening.conf
PasswordAuthentication no
PermitRootLogin no
MaxAuthTries 3
LoginGraceTime 30
AllowUsers <username>
Set AllowUsers to the account or accounts that really need remote access; leave the line out if you are not ready to restrict users. Then validate the configuration before applying it, and confirm what sshd will actually use:
sudo sshd -t
sudo sshd -T | grep -E "passwordauthentication|permitrootlogin|maxauthtries|allowusers"
sudo systemctl reload ssh
sshd -t prints nothing when the syntax is valid. If it reports an error, fix the file before reloading. After the reload, open a third, new connection with your key to prove that logins still work, and only then close the spare session. If an earlier drop-in file sets the same option to a different value, the sshd -T output will show that your setting did not take effect, and you can rename or edit the conflicting file.
Firewall and brute-force tools: know what they can see
A host firewall is still worthwhile on a Pi, because it limits every other service to the local network. A minimal ufw setup might allow SSH from your LAN and deny everything else inbound:
sudo apt install ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from 192.168.1.0/24 to any port 22 proto tcp
sudo ufw enable
sudo ufw status verbose
Replace the subnet with your own LAN and add the rule before you enable the firewall, with a second session open. Outbound traffic stays allowed, which is what the Localtonet client needs to reach its relay. Traffic from the client on the same Pi arrives over the loopback interface, which ufw permits by default, so the tunnel keeps working.
When the Localtonet client runs on the Pi and targets 127.0.0.1, tunneled connections reach sshd from the Pi's own loopback address. Check sudo journalctl -u ssh -n 50 --no-pager after an external login to see the source address your logs record. If it is a local address, a tool such as fail2ban cannot tell remote clients apart, and its default configuration ignores loopback, so it adds little for tunneled traffic. For this path, the controls that matter are key-only authentication, a short AllowUsers list, a low MaxAuthTries, patching, and running the tunnel only when you need it. fail2ban remains useful for any other service that is exposed to your LAN.
Use a dedicated, least-privileged account
Connect as an ordinary named user rather than granting broader access than the task requires. Remove unused accounts, review group memberships with id <username>, and grant sudo only where necessary. A remote maintenance task should not become unrestricted operating-system access.
Keep the system patched
Apply security updates regularly and automate the routine ones:
sudo apt update && sudo apt full-upgrade
sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
Unattended upgrades install security updates without a manual step. Review the result with sudo journalctl -u ssh --since yesterday --no-pager and check free storage occasionally; a full SD card is a common reason a Pi becomes unreliable. A tunnel cannot compensate for an unmaintained service with known vulnerabilities.
Expose only the required protocol, and control the lifecycle
If SSH is the only requirement, publish only the SSH TCP port. Do not create HTTP, UDP, File Server or VPN access merely because the options exist. Stop temporary access when the work is done and delete abandoned configurations so they cannot be restarted by accident. A shorter availability window does not replace authentication, but it stops an unnecessary endpoint from staying active indefinitely.
Protect credentials
Device tokens, SSH private keys, passwords and application secrets should not appear in shell history, public repositories, screenshots or tickets. The token file is root-owned with mode 600 for that reason. If a secret is exposed, replace it using the current Localtonet dashboard or operating-system process, update /etc/localtonet/auth-token if the token changed, re-run the --check-config command, and restart the service with sudo systemctl restart localtonet.
Reach a Pi desktop or web dashboard
SSH is the minimum, but many Pi projects need a graphical desktop or a browser dashboard. The title promise of remote access covers both, and the same tunnel pattern applies. The only things that change are the protocol family, the local port and the verification command.
VNC desktop over a TCP tunnel
VNC shows the Pi's desktop in a remote client. Enable the VNC server through sudo raspi-config under Interface Options, following the current Raspberry Pi documentation for your release. VNC listens on a port that follows the display number, usually 5900 for display :0, then 5901, 5902 and so on. Verify the listener locally before creating any tunnel:
sudo ss -ltnp | grep -E ":590[0-9]"
If a port such as 5900 appears, a TCP tunnel can target 127.0.0.1 and that port when the client runs on the Pi. Test locally with a VNC viewer pointed at the Pi first.
xrdp (RDP) desktop over a TCP tunnel
xrdp provides a Remote Desktop Protocol server so that Windows' built-in Remote Desktop client, or any RDP client, can connect. It listens on TCP 3389:
sudo apt install xrdp
sudo systemctl enable --now xrdp
systemctl is-active xrdp
sudo ss -ltnp | grep 3389
Once xrdp is active and 3389 is listening, a TCP tunnel to 127.0.0.1 and port 3389 follows the same six steps as the SSH tunnel. In the RDP client, enter the assigned public host and port in the form host:port.
VNC and RDP are frequent targets of password guessing, and an extra public endpoint adds exposure. A safer pattern keeps the single SSH tunnel and forwards the desktop port through SSH from the remote computer. For VNC: ssh -p <public-port> -L 5901:127.0.0.1:5900 <user>@<public-host>, then point the VNC viewer at localhost:5901. For xrdp: ssh -p <public-port> -L 13389:127.0.0.1:3389 <user>@<public-host>, then connect your RDP client to localhost:13389. The desktop service stays unpublished, protected by SSH key authentication.
A Pi web dashboard over an HTTP tunnel
For a browser dashboard, use an HTTP/s tunnel. Dashboards commonly listen on ports such as 80, 3000, 8080 or an application-specific number. Confirm yours with sudo ss -ltnp and open it locally first. To rehearse the tunnel with a harmless page, serve a directory on a local-only port:
mkdir -p ~/demo-site && echo "hello from the Pi" > ~/demo-site/index.html
cd ~/demo-site
python3 -m http.server 8080 --bind 127.0.0.1
curl -I http://127.0.0.1:8080
Run the second command from another terminal. A 200 OK response means a tunnel targeting 127.0.0.1 and port 8080 has something to forward to. HTTP tunnels point to a reachable local IP address and port and give you a public HTTPS address, with Process Type options of Random Sub Domain, Custom Sub Domain and Custom Domain, subject to your dashboard and plan. Stop the demo server with Ctrl+C when finished.
Never publish an administrative dashboard without its own authentication. Some Pi dashboards can run commands, install packages or change network settings, and a public HTTPS address does not protect them. Turn on the application's login, use a strong password, and consider whether SSH forwarding to the dashboard would be enough instead of a public URL.
Plan for reboots, upgrades and routine operation
Remote access depends on independent layers: power, local networking, internet connectivity, the target service, the Localtonet service, the device connection and the tunnel's running state. Because the client runs as a systemd service enabled with enable --now, it is configured to start at boot, and you can prove that rather than assume it.
Run a controlled reboot test
Do this while you still have physical or local network access to the Pi. Before rebooting, confirm that the service is enabled:
systemctl is-enabled localtonet.service ssh
sudo reboot
After the Pi comes back, use this checklist in order:
- Confirm the Pi finished booting and has a valid local address with
ip -br addranduptime -p. - Confirm SSH with
systemctl is-active sshandsudo ss -ltnp | grep sshd. - Confirm the Localtonet service with
systemctl is-active localtonet.serviceand read this boot's log withsudo journalctl -b -u localtonet.service --no-pager. - Confirm in the dashboard that the Pi's token shows connected.
- Confirm in the dashboard that the TCP tunnel is running. If it shows stopped, press Start, and note it so you know how your account behaves after a reconnect.
- Repeat the external SSH test with the host and port currently shown in the dashboard.
The service configuration is documented, but the tunnel's running state after a reconnect is something we recommend you observe in this test rather than assume, because it is a dashboard-side state. Record what you see; that observation is your operating procedure.
Check the service during normal operation
systemctl status localtonet.service --no-pager
sudo journalctl -u localtonet.service -n 100 --no-pager
sudo journalctl -u localtonet.service -f
The first command gives the current state, the second the recent history, and the third follows new log lines live so that you can watch a reconnect while you start or stop a tunnel in the dashboard (press Ctrl+C to leave it).
Upgrade the Localtonet client
Our documented upgrade procedure is short. Download the newer package, install it over the old one, and restart the service. Your configuration is preserved, including the token file:
sudo apt install ./localtonet-linux-arm64.deb
sudo systemctl restart localtonet
systemctl is-active localtonet.service
sudo journalctl -u localtonet.service -n 30 --no-pager
If you use VPN Manager, restart localtonet-vpn.service instead. After upgrading, confirm that the token reconnects in the dashboard and run one external SSH test. Plan upgrades for a time when a brief interruption is acceptable, and keep a local or second path to the Pi in case the client does not return.
Stop, pause and delete access
Tunnel lifecycle is managed from the dashboard. Stop the tunnel when temporary remote access is finished; a stopped tunnel no longer provides the same active access path. Delete obsolete configurations so they cannot be restarted by accident. To take the whole Pi offline from Localtonet, stop the client service:
sudo systemctl stop localtonet.service
sudo systemctl disable localtonet.service
stop ends the connection now, and disable prevents it from starting at the next boot. Re-enable it later with sudo systemctl enable --now localtonet.service.
Protect the hardware
Raspberry Pi deployments are sensitive to power and storage. Use an appropriate power supply, shut down cleanly with sudo shutdown -h now when possible, watch free space with df -h, and consider signal quality if the Pi uses Wi-Fi. Many outages that look like tunnel problems are actually power dips, a full SD card or a changed network.
Troubleshoot common Raspberry Pi tunnel failures
Work from the local service toward the public endpoint, and follow the layer table in the verification section. Changing several settings at once makes it harder to identify the failing layer.
| Symptom | Likely area | What to check |
|---|---|---|
| Local SSH connection is refused | SSH disabled, stopped, or on another port | Run sudo systemctl status ssh and sudo ss -ltnp. Enable SSH and use the port actually shown. |
| Loopback SSH works, LAN SSH fails | Bind address, local firewall, isolation, wrong LAN address | Check ip -br addr and sudo ufw status, and test from the actual client device. |
| Client is on another device but targets 127.0.0.1 | Wrong target address | Replace loopback with the Pi's reachable LAN address. |
localtonet.service is failed or inactive |
Token file, package, or compatibility problem | Read sudo journalctl -u localtonet.service -n 100 --no-pager and follow the sections below. |
| Service is active, but the token shows disconnected | Wrong token, outbound restriction, DNS or clock problem | Confirm the file holds the intended token and test outbound access with curl -I https://localtonet.com. |
| Device connected, endpoint does not respond | Tunnel created but not started | Press Start in the tunnel list and confirm a running state. |
| Public connection refused or times out | Wrong target IP or port, stopped service | Repeat the exact local target test and compare it with the tunnel fields. |
| SSH connects but authentication fails | Key, password, permissions, or AllowUsers |
Test locally with the same key. Check ~/.ssh permissions and sudo sshd -T. Do not weaken authentication to pass a test. |
| Access stops after a reboot | A layer did not return | Run the reboot checklist: SSH, then localtonet.service, token, tunnel state. |
| Access fails intermittently | Power, Wi-Fi, ISP, thermal or storage instability | Check power quality, signal, df -h, and whether the Pi restarted (uptime -p). |
The service will not start
Start with the state and the log, which usually names the cause:
systemctl status localtonet.service --no-pager
sudo journalctl -u localtonet.service -n 100 --no-pager
sudo systemctl restart localtonet
If the log scrolls past the useful part, add -b to limit it to the current boot or use --since "10 minutes ago". Watch whether the service starts and immediately exits repeatedly, which points to configuration or compatibility, or stays running while the dashboard still shows the token as disconnected, which points to credentials or outbound connectivity.
Token file problems
Most first-time failures come from the token file. Check it without printing the secret:
sudo ls -l /etc/localtonet/auth-token
sudo wc -l /etc/localtonet/auth-token
sudo grep -c DUMMY_AUTH_TOKEN /etc/localtonet/auth-token
- The listing should show private permissions (
-rw-------). If not, runsudo chmod 600 /etc/localtonet/auth-token. - The
grep -ccount should be0. A1means the placeholder is still in the file. - The file should contain the token on a single line, with no quotes, label, extra words or blank lines. Remove anything extra.
- Use the token belonging to this Pi. A token that belongs to a different device will connect that device identity, not the one you selected in the tunnel.
After correcting the file, re-run the check and restart the service:
sudo localtonet --headless --authtoken-file /etc/localtonet/auth-token --check-config
sudo systemctl restart localtonet
The --check-config command fails
Do not start the service until this command succeeds, as the documented sequence requires. Read the message it prints. A configuration complaint usually traces back to the token file (placeholder, empty file, stray characters). If the command itself will not run, for example with an error about an executable format or a missing library, move to the architecture and compatibility sections below.
32-bit versus 64-bit mismatch
The ARM64 DEB only installs on an arm64 system. If dpkg --print-architecture prints armhf, attempting to install it produces a dpkg message that the package architecture (arm64) does not match the system (armhf). Do not force the install with dpkg overrides or by adding architectures; that leaves a package that cannot run. Your options are the Linux (ARM) download for a 32-bit system, using the instructions shown on the documentation page, or reinstalling a 64-bit Raspberry Pi OS on a board that supports it. Remember that a 64-bit kernel (uname -m showing aarch64) does not make a 32-bit userland 64-bit.
Unsupported systemd or OpenSSL version
The packages require systemd 247 or newer and OpenSSL 3. Check both:
systemctl --version | head -n 1
openssl version
grep VERSION /etc/os-release
If OpenSSL reports 1.1.1, which is typical of Debian 11 based Raspberry Pi OS, the package requirements are not met, and installation may report unmet dependencies or the client may fail to run. The supported fix is to move to a release such as Debian 12 or newer rather than to replace system libraries by hand, which can break other software. Back up your data and follow the Raspberry Pi guidance for a fresh install or supported upgrade path.
The Pi cannot reach Localtonet
getent hosts localtonet.com
curl -I https://localtonet.com
timedatectl
Failed name resolution points to DNS. A failed HTTPS request with working DNS points to filtering or connectivity. A Pi without a battery-backed clock that has the wrong time can also see secure connections fail, so confirm that timedatectl reports the clock as synchronized. If a managed network blocks outbound traffic, work with the network owner and do not try to bypass organizational controls.
The service is bound to the wrong interface
A service bound only to 127.0.0.1 can be reached by a Localtonet client on the same Pi, but not by a client on another LAN device. Conversely, a service that should stay local should not be rebound to every interface without a clear need and a security review. Often the safer fix is to run the client on the Pi rather than broadening the bind address.
The local target port is wrong
The public port assigned by Localtonet and the Pi's local SSH port are separate values. Enter the local port shown by ss as the tunnel target, and use the assigned public port only in the remote ssh -p command.
Authentication succeeds but commands or files fail
That is an operating-system authorization or filesystem-permission issue rather than a tunnel problem. Check the connected user, groups (id), directory ownership and command privileges, and avoid fixing permission errors by granting unnecessary administrative access.
The Pi itself is unavailable
No tunnel can reach a Pi that lost power, failed to boot, left the network or stopped its target service. For unattended sites, plan for power quality, network recovery and a tested maintenance process, since physical intervention may be required.
Alternatives for files, UDP and private networks
Publish a controlled folder
File Server targets a local folder path instead of an IP address and port. The Default subtype provides a browser-based file manager. SFTP supports compatible clients and automation tools such as FileZilla, WinSCP and rsync. WebDAV mounts the folder as a native network drive through File Explorer or Finder.
The documented sequence is: install the Localtonet application for your operating system, open the File Server Tunnel page, select Process Type (Random Sub Domain, Custom Sub Domain or Custom Domain), select the AuthToken and server, enter the local folder path (for Linux, such as /root or /usr/share), then create and start the tunnel with the Start button. See the Localtonet File Server documentation for the current steps.
Share the narrowest practical directory. Avoid exposing a whole home directory, system folders, SSH keys, credentials or configuration files containing secrets. Our published material does not state whether every Default web manager capability, such as folder encryption, Immich-compatible backup or Smart Collections, also applies to the SFTP and WebDAV subtypes, so verify that before depending on it.
Forward UDP only when the application requires it
UDP is not connection-oriented. Identity, replay handling, ordering and error recovery depend on the application protocol. Confirm the service specification before selecting UDP, and use combined UDP/TCP only when the application genuinely needs both transports.
Use VPN Manager for private network access
If authorized devices need access to several private services or selected resources across local LANs, VPN Manager may fit better than publishing each port separately. It provides a private mesh VPN with granular firewall rules, where you choose a Server Relay Node, add clients using existing tokens, and optionally bridge a client's LAN. Standard service tunnels should not be described as VPN functionality.
Frequently asked questions
Can I access a Raspberry Pi without router port forwarding?
Yes. The Localtonet client establishes an outbound connection from the Raspberry Pi, or from another device that can reach it, to a Localtonet relay. A running tunnel provides a public endpoint without an inbound router port-forwarding rule or a public IP address. The service behind it, such as SSH, still authenticates every user.
Which tunnel should I use for Raspberry Pi SSH?
Use a TCP tunnel because SSH is a raw TCP service. Point it to the address and port reachable from the Localtonet client. If the client runs on the Pi, that is 127.0.0.1 and the verified SSH port, commonly 22.
Which Localtonet package do I install on a Raspberry Pi?
On a 64-bit Raspberry Pi OS (dpkg --print-architecture prints arm64) based on Debian 12 or newer, install the Debian/Ubuntu ARM64 DEB with sudo apt install ./localtonet-linux-arm64.deb. The package needs systemd 247 or newer and OpenSSL 3. On a 32-bit system (armhf), use the Linux (ARM) download from the documentation page instead.
Where does the authentication token go on the Pi?
With the package install, edit /etc/localtonet/auth-token as root, replace DUMMY_AUTH_TOKEN with your token on a single line, run sudo chmod 600 /etc/localtonet/auth-token, and verify with sudo localtonet --headless --authtoken-file /etc/localtonet/auth-token --check-config. Never share the file's contents.
Will the Localtonet client return after a Raspberry Pi reboot?
With the documented package, sudo systemctl enable --now localtonet.service starts the client service now and at every boot. Confirm it with systemctl is-enabled localtonet.service, a controlled reboot, and sudo journalctl -b -u localtonet.service. Then check in the dashboard that the token is connected and the tunnel is running, pressing Start if it is not.
How do I upgrade the client on the Pi?
Install the newer package with sudo apt install ./localtonet-linux-arm64.deb, then run sudo systemctl restart localtonet. Your configuration is preserved. If you use VPN Manager, restart localtonet-vpn.service instead.
Why does 127.0.0.1 fail when the client runs on another computer?
Loopback always refers to the machine running the client. If Localtonet runs on another computer, 127.0.0.1 targets that computer rather than the Pi. Use the Pi's reachable LAN address and verify SSH from the client computer first.
Does this work behind carrier-grade NAT?
The outbound tunnel model does not require the Pi to have a public IP address, so it suits common CGNAT situations. The Pi must still be able to establish and maintain the outbound connection to Localtonet.
Can I use VNC or xrdp through Localtonet?
Yes, both are TCP services. VNC usually listens on 5900 for display :0 (5901, 5902 and so on for others) and xrdp on 3389. Verify the listener with ss, then create a TCP tunnel to the local port. For better protection, forward the desktop port through the SSH tunnel with ssh -L instead of publishing a second endpoint.
Does Localtonet enable SSH for me?
No. SSH must already be enabled, running, listening on the expected port and usable with a valid operating-system account. Localtonet forwards traffic to that service but does not configure SSH users or authentication.
Is a TCP or HTTP tunnel the same as a VPN?
No. TCP, HTTP, UDP, combined UDP/TCP and File Server tunnels publish a particular service or folder. VPN Manager is our private mesh VPN feature and supports granular firewall rules for private network connectivity.
Can I publish files instead of enabling SSH?
Yes. If the goal is controlled file access rather than a terminal session, File Server can publish a selected local folder through its Default, SFTP or WebDAV subtype. Share only the required directory and grant the minimum necessary permissions.
Connect your Raspberry Pi through a controlled outbound tunnel
Verify SSH locally, install the matching Localtonet package as a systemd service, create a TCP tunnel to the exact listening address and port, harden OpenSSH, and test the assigned endpoint from an external network and after a reboot.
Get Started Free โ