How to reach namespace from inside the LAN?

My home server is running an application I only access from my LAN. I do NOT need to reach it from outside. The administration panel of that app is accessible from any device on my LAN via http://192.168.50.5:9641. That app pulls data from the internet from time to time and I need these requests to go through a VPN.

My goal is to use a namespace to force the app through the VPN while the rest of the system keeps using the normal connection (can’t use the VPN everywhere). I found and modified this script to achieve this goal, but I need help on the last step (networking is def. not my strongest skill)

My problem is, once the app is up and running within the namespace, I cannot (rather I don’t know how to) reach it from within my LAN, and access the admin dashboard. Any idea how I can make it work ? Below is what I have so far.

This is the modified script (link to the original above). It creates and configures the namespace and the virtual interfaces (including one supposed to allow communication with the rest of the LAN. That doesn’t work). It is called by openVPN when openVPN starts up or goes down (see openVPN conf further down)

root@home-server:~# cat /etc/netnamespace/vpn/netns.sh
#!/bin/sh

set -eu

NETNS=vpn

case "${script_type:-}" in

up)
    echo "vpn netns script called - up"
    echo "dev=$1 mtu=$2 ip=$4 mask=$5"

    echo "reset namespace"
    ip netns del "$NETNS" 2>/dev/null || true
    ip netns add "$NETNS"

    echo "enable loopback"
    ip netns exec "$NETNS" ip link set lo up

    echo "move tun interface into namespace"
    ip link set "$1" netns "$NETNS"

    echo "configure tun interface"
    ip netns exec "$NETNS" ip link set "$1" mtu "$2"
    ip netns exec "$NETNS" ip addr add "$4/\" dev "$1"
    ip netns exec "$NETNS" ip link set "$1" up

    echo "configure default route"
    ip netns exec "$NETNS" ip route replace default dev "$1"

    echo "create macvlan interface"
    ip link delete macvlan0 2>/dev/null || true
    ip link add macvlan0 link eth0 type macvlan mode bridge
    
    echo "move macvlan into namespace"
    ip link set macvlan0 netns "$NETNS"
    
    echo "enable macvlan interface"
    ip netns exec "$NETNS" ip link set macvlan0 up
    
    echo "assign LAN IP address"
    ip netns exec "$NETNS" ip addr add 192.168.50.0/24 dev macvlan0

    echo "vpn namespace ready"
    ;;

route-up)
    echo "route-up called - nothing to do"
    ;;

down)
    echo "vpn netns script called - down"

    ip netns exec "$NETNS" ip link delete macvlan0 2>/dev/null || true

    ip netns del "$NETNS" 2>/dev/null || true
    ;;

*)
    echo "netns.sh: ignoring script_type='${script_type:-}'"
    ;;

esac

I created the resolv files manually, which is a departure from the original script

root@home-server:~# cat /etc/netns/vpn/resolv.conf 
nameserver 8.8.8.8
nameserver 1.1.1.1

This is how I configured the VPN

root@home-server:~# cat /etc/openvpn/protonvpn.conf 
client
dev tun
proto udp
remote 95.XXX.XXX.XXX 80
remote 95.XXX.XXX.XXX 51820
remote 95.XXX.XXX.XXX 5060
remote 95.XXX.XXX.XXX 1194
remote 95.XXX.XXX.XXX 4569
remote-random
resolv-retry infinite
nobind
cipher AES-256-GCM
setenv CLIENT_CERT 0
tun-mtu 1500
mssfix 0
persist-key
persist-tun
reneg-sec 0
remote-cert-tls server
auth-user-pass /etc/protonvpn/auth.txt
auth-nocache
route-noexec
route-nopull
ifconfig-noexec
script-security 2
up /etc/netnamespace/vpn/netns.sh
route-up /etc/netnamespace/vpn/netns.sh
down /etc/netnamespace/vpn/netns.sh
<ca>
-----BEGIN CERTIFICATE-----
<REDACTED>
-----END CERTIFICATE-----
</ca>
<tls-crypt>
-----BEGIN OpenVPN Static key V1-----
<REDACTED>
-----END OpenVPN Static key V1-----
</tls-crypt>

This is the VPN service configuration

root@home-server:~# systemctl cat openvpn@protonvpn.service 
# /usr/lib/systemd/system/openvpn@.service
[Unit]
Description=OpenVPN connection to %i
PartOf=openvpn.service
Before=systemd-user-sessions.service
After=network-online.target
Wants=network-online.target
Documentation=man:openvpn(8)
Documentation=https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
Documentation=https://community.openvpn.net/openvpn/wiki/HOWTO
[Service]
Type=notify
PrivateTmp=true
WorkingDirectory=/etc/openvpn
ExecStart=/usr/sbin/openvpn --daemon ovpn-%i --status /run/openvpn/%i.status 10 --cd /etc/openvpn --config /etc/openvpn/%i.conf --writepid /run/openvpn/%i.pid
PIDFile=/run/openvpn/%i.pid
KillMode=process
CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SETPCAP CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_AUDIT_WRITE
TasksMax=10
DeviceAllow=/dev/null rw
DeviceAllow=/dev/net/tun rw
ProtectSystem=true
ProtectHome=true
RestartSec=5s
Restart=on-failure
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/openvpn@protonvpn.service.d/override.conf
#[Service]
#ExecStartPost=-/bin/bash -c "/usr/local/sbin/commitVPNExecChanges.sh Start"
#ExecStopPost=-/bin/bash -c "/usr/local/sbin/commitVPNExecChanges.sh Stop"
#TasksMax=100
#
[Service]
CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SETPCAP CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_SYS_ADMIN
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_ADMIN
PrivateMounts=no
PrivateTmp=false
ProtectSystem=false
ProtectHome=false

The service starts without issue (log below is reversed !!!)

root@home-server:~# journalctl -r -t openvpn
Apr 23 19:54:01 home-server openvpn[2252347]: route-up called - nothing to do
Apr 23 19:54:01 home-server openvpn[2252315]: vpn namespace ready
Apr 23 19:54:01 home-server openvpn[2252315]: assign LAN IP address
Apr 23 19:54:01 home-server openvpn[2252315]: enable macvlan interface
Apr 23 19:54:01 home-server openvpn[2252315]: move macvlan into namespace
Apr 23 19:54:01 home-server openvpn[2252315]: create macvlan interface
Apr 23 19:54:01 home-server openvpn[2252315]: configure default route
Apr 23 19:54:01 home-server openvpn[2252315]: configure tun interface
Apr 23 19:54:01 home-server openvpn[2252315]: move tun interface into namespace
Apr 23 19:54:01 home-server openvpn[2252315]: enable loopback
Apr 23 19:54:01 home-server openvpn[2252315]: reset namespace
Apr 23 19:54:01 home-server openvpn[2252315]: dev=tun0 mtu=1500 ip=10.96.0.37 mask=255.255.0.0
Apr 23 19:54:01 home-server openvpn[2252315]: vpn netns script called - up

Once the service is started, I can use the namespace to reach the WAN

root@home-server:~# ip netns list
vpn (id: 0)

root@home-server:~# ip netns exec vpn ping www.bbc.co.uk
PING bbc.map.fastly.net (151.101.128.81) 56(84) bytes of data.
64 bytes from 151.101.128.81: icmp_seq=1 ttl=59 time=46.7 ms
64 bytes from 151.101.128.81: icmp_seq=2 ttl=59 time=48.9 ms
64 bytes from 151.101.128.81: icmp_seq=3 ttl=59 time=51.2 ms
^C64 bytes from 151.101.128.81: icmp_seq=4 ttl=59 time=46.5 ms
64 bytes from 151.101.128.81: icmp_seq=5 ttl=59 time=46.5 ms
64 bytes from 151.101.128.81: icmp_seq=6 ttl=59 time=49.1 ms
^C
--- bbc.map.fastly.net ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 5008ms
rtt min/avg/max/mdev = 46.514/48.140/51.154/1.726 ms

My app uses the namespace without issue: It can reach the WAN since it can verify it has the latest version

root@home-server:/etc# ip netns exec vpn sudo -u morian /home/morian/bin/rantanplan --port 9641
INFO[2026-04-23 19:25:26] rantanplan version: v0.51.3 (9be5331o) - Build - 2026-01-22 13:02:30 
INFO[2026-04-23 19:25:26] rantanplan is listening on 0.0.0.0:9641           
INFO[2026-04-23 19:25:26] rantanplan is running at http://localhost:9641/   
INFO[2026-04-23 19:25:27] Version v0.51.3 (9be5331o) is already the latest released

But from my laptop, I cannot see the port open on the home-server. And the dashboard is not reachable()

efrem@leviatan:~$ nc -zv 192.168.50.5 9641
nc: connect to 192.168.50.5 port 9641 (tcp) failed: Connection refused

Note that if the app run without the VPN, it responds

efrem@leviatan:~$ nc -zv 192.168.50.5 9641
Connection to 192.168.50.5 9641 port [tcp/*] succeeded!

These are the home-server interfaces

root@home-server:~# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute 
       valid_lft forever preferred_lft forever
2: enp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether f0:2f:74:cf:60:34 brd ff:ff:ff:ff:ff:ff
    altname enxf02f74cf6034
    inet 192.168.50.5/24 brd 192.168.50.255 scope global dynamic enp3s0
       valid_lft 65883sec preferred_lft 65883sec
    inet6 fe80::f22f:74ff:fecf:6034/64 scope link proto kernel_ll 
       valid_lft forever preferred_lft forever