Networking & port forwarding
The Alpine guest has full outbound internet access from first boot. Inbound access - SSH, VNC, container services - works through port forwarding, with a few rules injected automatically and the rest configured in Settings.
Guest network: SLIRP user-mode
On the QEMU backend the guest network is provided by SLIRP, a userspace TCP/IP stack embedded inside QEMU. No root, no kernel TUN/TAP device, and no host-side network interface are involved. From inside the guest the network looks like a small NAT:
| Address | Role |
|---|---|
10.0.2.15/24 | Guest IP (eth0) |
10.0.2.2 | Default gateway (SLIRP host) |
10.0.2.3 | Built-in DNS resolver |
8.8.8.8, 1.1.1.1 | Fallback DNS (in /etc/resolv.conf) |
IPv6 is disabled on the QEMU netdev (ipv6=off). Outbound TCP and UDP both work. The podroid-network OpenRC service brings up eth0 with these addresses at boot.
On the AVF backend the guest receives its IP from Android's built-in tethering via DHCP (udhcpc), rather than a static SLIRP address. The default route and DNS are assigned by the Android network stack. Inbound connectivity still works through port forwarding as described below, but the transport is vsock rather than SLIRP hostfwd.
Implicit port forwards
Three port forwards are injected automatically every time the VM starts. They never appear in the port-forward UI and cannot be removed from there:
| Service | Host port | Guest port | Protocol | When active |
|---|---|---|---|---|
| SSH (dropbear) | 9922 |
22 |
TCP | When SSH is enabled in Settings |
| X11 / VNC (Xvnc) | 5900 |
5900 |
TCP | Always (backs the in-app screen viewer) |
| PulseAudio | 4713 |
4713 |
TCP | Always (backs the in-app audio stream) |
The SSH forward is conditional: if you turn SSH off in Settings, the 9922→22 rule is not added and dropbear does not start inside the guest. The VNC and PulseAudio forwards are unconditional because the in-app X11 viewer depends on them.
Adding your own rules
Open Settings → Port forwards → Add. Enter a host port (on the Android device), a VM port (inside the guest), and pick TCP, UDP, or Both. On the QEMU backend rules apply live via the QMP protocol - no VM restart is needed. Rules are persisted across reboots.
A typical setup for a container service listening on guest port 8080:
# Inside the guest: start your service on port 8080
podman run -d -p 8080:80 nginx
# In Settings -> Port forwards -> Add:
# Host port: 8080
# VM port: 8080
# Protocol: TCP
#
# Then from your laptop on the same Wi-Fi:
curl http://<phone-ip>:8080
LAN reachability
All port forward listeners bind 0.0.0.0, not 127.0.0.1. This means any device on the same Wi-Fi network can reach the phone by its local IP address. The phone's current IP is shown in Settings.
# SSH from a laptop on the same Wi-Fi
$ ssh root@<phone-ip> -p 9922
# password: podroid
# VNC from any viewer
$ vncviewer <phone-ip>:5900
# HTTP service forwarded to host port 8080
$ curl http://<phone-ip>:8080
The privileged-port limit
Android apps run as unprivileged Linux users and do not hold CAP_NET_BIND_SERVICE. The Linux kernel refuses to bind a TCP or UDP socket to any port below 1024 for a process without that capability. This is a kernel-level rule, not a Podroid restriction, and there is no in-app workaround without root.
Consequences:
- SSH is exposed on host port 9922, not 22. Port 22 is below 1024; 9922 is not.
- You cannot listen on host ports
:80or:443directly. - The Add-rule dialog accepts any value from 1 to 65535, but a host port below 1024 will fail to bind at VM start time with a bind error in the logs. The rule itself is accepted - the failure happens when QEMU or the vsock forwarder tries to open the socket.
Forward to a high host port instead of the standard one: map host 8080 to guest 80, and host 8443 to guest 443. Services inside the guest can still listen on port 80 or 443 as usual - only the host-side port is restricted.
# Forward a web server on guest :80 to a high host port
# Settings -> Port forwards -> Add: 8080 -> 80 TCP
# Then reach it from outside:
$ curl http://<phone-ip>:8080
Port forwarding on AVF
On the AVF backend, port forwarding is implemented over vsock rather than SLIRP hostfwd. Each rule creates a TCP listener on 0.0.0.0:hostPort on the Android side, which is bridged to a vsock port on the guest via the podroid-vsock-agent service. The privileged-port limit applies here too, for the same reason.
UDP rules are ignored on the AVF backend. The vsock transport is TCP-only. If you need UDP forwarding, use the QEMU backend.
Podroid is free software (GPL). Docs for v1.2.1. Found something inaccurate? Open an issue.