E.g. for PBS (on PVERouter)
TUN for Tailscale:
nano /etc/pve/nodes/pverouter/lxc/101.conf
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
pct start 101
Then login via proxmox console:
apk update
apk add openssh
rc-update add sshd nano
rc-service sshd start
#!/bin/sh
#
# Port Forwarding VM Setup Script
# Safe to rerun - just edit FORWARDS and run again
#
# ============ CONFIGURATION ============
# Format: local_port:target_host or local_port:target_host:remote_port
#
# Examples:
# 8080:192.168.1.100 -> forward :8080 to 192.168.1.100:8080
# 443:192.168.1.100:8443 -> forward :443 to 192.168.1.100:8443
# 3000:100.64.0.5 -> forward :3000 to 100.64.0.5:3000 (tailscale IP)
#
FORWARDS="
8007:10.10.10.10
8080:192.168.1.100
443:192.168.1.100:8443
3000:100.64.0.5
"
# =======================================
set -e
log() {
echo "[$(date '+%H:%M:%S')] $1"
}
# Install packages if missing
install_packages() {
# Enable community repo if not already (needed for tailscale)
if ! grep -q "^[^#].*/community$" /etc/apk/repositories; then
log "Enabling community repository"
sed -i 's|#\(.*community\)$|\1|' /etc/apk/repositories
apk update
fi
local needed=""
for pkg in iptables tailscale; do
if ! apk info -e "$pkg" >/dev/null 2>&1; then
needed="$needed $pkg"
fi
done
if [ -n "$needed" ]; then
log "Installing:$needed"
apk add --no-cache $needed iptables-openrc
fi
}
# Enable IP forwarding (idempotent)
setup_forwarding() {
if ! grep -q "^net.ipv4.ip_forward=1" /etc/sysctl.conf 2>/dev/null; then
log "Enabling IP forwarding"
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
fi
sysctl -w net.ipv4.ip_forward=1 >/dev/null
}
# Setup tailscale service
setup_tailscale() {
if ! rc-status | grep -q tailscale; then
log "Enabling tailscale service"
rc-update add tailscale default 2>/dev/null || true
fi
if ! service tailscale status >/dev/null 2>&1; then
log "Starting tailscale"
service tailscale start
fi
if ! tailscale status >/dev/null 2>&1; then
log "Run 'tailscale up' to authenticate"
fi
}
# Parse a forward rule: local_port:host or local_port:host:remote_port
parse_rule() {
local rule="$1"
local_port=$(echo "$rule" | cut -d: -f1)
target_host=$(echo "$rule" | cut -d: -f2)
remote_port=$(echo "$rule" | cut -d: -f3)
# If no remote port specified, use local port
if [ -z "$remote_port" ]; then
remote_port="$local_port"
fi
}
# Rebuild iptables rules (clean slate each run)
setup_iptables() {
log "Configuring iptables rules"
# Flush existing NAT rules
iptables -t nat -F PREROUTING 2>/dev/null || true
iptables -t nat -F POSTROUTING 2>/dev/null || true
# Add MASQUERADE for return traffic
iptables -t nat -A POSTROUTING -j MASQUERADE
# Process each forward rule
echo "$FORWARDS" | while read -r line; do
line=$(echo "$line" | xargs) # trim whitespace
[ -z "$line" ] && continue
parse_rule "$line"
log " :$local_port -> $target_host:$remote_port"
iptables -t nat -A PREROUTING -p tcp --dport "$local_port" -j DNAT --to-destination "$target_host:$remote_port"
iptables -t nat -A PREROUTING -p udp --dport "$local_port" -j DNAT --to-destination "$target_host:$remote_port"
done
# Persist rules
/etc/init.d/iptables save >/dev/null 2>&1
if ! rc-status | grep -q iptables; then
rc-update add iptables default 2>/dev/null || true
fi
}
# Show current status
show_status() {
echo ""
echo "=== Current Forwarding Rules ==="
iptables -t nat -L PREROUTING -n 2>/dev/null | grep DNAT | \
awk '{printf " %-4s :%s -> %s\n", $2, $7, $8}' | sed 's/dpt://' | sed 's/to://' \
|| echo " (none)"
echo ""
echo "=== Tailscale Status ==="
if tailscale status >/dev/null 2>&1; then
tailscale ip -4 2>/dev/null | xargs echo " IP:"
else
echo " Not connected - run 'tailscale up'"
fi
echo ""
}
# Main
log "Port forwarding setup starting"
install_packages
setup_forwarding
setup_tailscale
setup_iptables
show_status
log "Done. Edit FORWARDS in this script and rerun to update."