Making the Valetuduo UI available from outside
shimun December 30, 2025 #nixos #valetudo #nginx #wstunnelPreface
Valetudo is an great replacement for the OEMs cloud native firmware. The cloud however, still has some advantages the biggest being imo, to be able to start cleaning when everyone has left the home. Starting the cleaning job while still in wifi range can be challening when in a hurry.
Possible solutions
In order to make Valetudo accessible from outside my wifi I've explored a couple options:
- Routing the robots IP via my routers wireguard network
- Running userspace wireguard on the robot itself
- Using WsTunnel
Wireguard
Wireguard seemed obvious since I already operate an network with all my personal device in it so adding the robot seemed natural. While the kernel on the robot does not come with wireguard support, the lack thereof can easily be compensated for by compiling either boring-tun or wireguard-go statically for aarch64. An issue arises however when trying to configure the interface due to an lack of the required depenencies for wg-quick. Surely those issues can be worked out but would take some effort and result in a rather fragile solution.
WsTunnel
WsTunnel makes things a lot simpler on the robots side since WsTunnel is available as an statically compiled binary for aarch64 with no other depenencies in form of setup scripts, so lets start there:
Download WsTunnel for aarch64 from Github releases extract the tgz and then copy the binary to the robot
|
Confirm the binary is indeed runable on our robot:
# /data/wstunnel -h
Use Websocket or HTTP2 protocol to tunnel {TCP,UDP} traffic
wsTunnelClient <---> wsTunnelServer <---> RemoteHost
Usage: wstunnel [OPTIONS] <COMMAND>
Commands:
client
server
help Print this message or the help of the given subcommand(s)
Options:
--no-color <NO_COLOR> Disable color output in logs [env: NO_COLOR=]
--nb-worker-threads <INT> *WARNING* The flag does nothing, you need to set the env variable *WARNING*
Control the number of threads that will be used.
By default, it is equal the number of cpus [env: TOKIO_WORKER_THREADS=]
--log-lvl <LOG_LEVEL> Control the log verbosity. i.e: TRACE, DEBUG, INFO, WARN, ERROR, OFF
for more details: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#example-syntax [env: RUST_LOG=] [default: INFO]
-h, --help Print help
-V, --version Print version
Success
Now that we know that we won't run in any trouble on the robots side we can move on to setting up the server. In order to allow reverse tunneling we have to provide WsTunnel with an config since the CLI cannot express reverse tunneling options.
restrict.yml:
restrictions:
- name: "Reverse"
match:
-
allow:
-
protocol:
- Tcp
- Udp
port:
- 7732
With this config in place the tunnel can be started:
wstunnel \
server \
'--restrict-config restrict.yml' \
ws://127.0.0.1:7733
The process needs to be daemonized somehow, I've opted to use the services.wstunnel NixOS module to generate an systemd-unit but for other distros that unit can be easily written by hand.
WsTunnel will now accept connections on 127.0.0.1:7733 which is quite useless since the robot cannot reach localhost on the server, thats why I've setup nginx such that it proxies requests from an virtual host to the local WsTunnel socket.
I've also configured another virtual host to serve the tunneled Valetudo UI to clients in my wireguard network.
Now the only thing left to do is to terminate the tunnel on the robot:
Once started the UI should be available via nginx.
The final touch
The tunnel will terminate as soon as out ssh session ends which makes the whole exerise quite pointless, therefore the process needs to be daemonized. Which is easier said than done since the rootfs on my robot is a readonly sqashfs which means there is no way to add to /etc/init.d. ButI've figured there is a script which starts Valetudo and said script resides on /data which is writeable so I've added:
if [[ -f /data/tunnel.sh ]]; then
/data/tunnel.sh > /dev/null 2>&1 &
fi
to /data/_root_postboot.sh with tunnel.sh containing the WsTunnel command.
Time to reboot and test