| Tested on | Red Hat Enterprise Linux 10.2 (Coughlan) |
|---|---|
| Package | podman-5.8.2-5.el10_2.x86_64passt-0^20251210.gd04c480-5.el10_2.x86_64netavark-1.17.2-1.el10.x86_64aardvark-dns-1.17.1-1.el10_2.x86_64 |
| Applies to | Any Linux host with Podman installed |
| Privilege | Rootless examples as unprivileged user testuser |
| Scope | Rootless networking architecture, pasta defaults and options, slirp4netns migration, rootless Netavark bridges, podman unshare --rootless-netns, port-forward behavior, and Podman 5 vs 6 differences. Does not cover privileged ports below 1024, full -p syntax, general Netavark bridge administration, or container DNS depth. |
Rootless Podman does not mean containers run without networking. It means a normal user cannot create arbitrary host bridges, change host firewall rules, or attach directly to physical interfaces the way root can. Podman works around that with user-mode stacks — pasta by default on Podman 5 — and with rootless Netavark bridge networks when you need a shared subnet and container-name DNS.
This guide explains the architecture, shows what default pasta looks like on the lab host, and covers what broke when slirp4netns stopped being the default path.
The lab image is docker.io/library/alpine:3.20. Rootless commands run as testuser unless noted.
How rootless Podman networking works
A rootful container on the default bridge rides a host-managed Linux bridge (podman0) configured by Netavark. A rootless container on the default path never touches that bridge directly.
Default rootless path:
Rootless container
│
▼
network namespace
│
▼
pasta
│
▼
host networkingWhen you attach a rootless container to a user-defined bridge:
Rootless container
│
▼
Netavark bridge namespace
│
▼
rootless networking layer
│
▼
hostRootless does not mean isolated loopback only. It means Podman uses a stack that can provide outbound connectivity and port forwarding without root privileges on the host.
Check the rootless network backend
Confirm which rootless forwarder Podman selected before you tune options or compare tutorials written for slirp4netns.
As the rootless user:
podman info --format 'RootlessNetworkCmd={{.Host.RootlessNetworkCmd}}'On the lab host:
RootlessNetworkCmd=pastaAlso verify the installed packages on the host:
rpm -q podman passt netavark aardvark-dnspodman-5.8.2-5.el10_2.x86_64
passt-0^20251210.gd04c480-5.el10_2.x86_64
netavark-1.17.2-1.el10.x86_64
aardvark-dns-1.17.1-1.el10_2.x86_64passt supplies the pasta binary Podman invokes for rootless networking. Netavark still manages user-defined bridge networks even when the default container path uses pasta.
pasta became the default in Podman 5.0
Version history matters because many rootless tutorials still describe slirp4netns behavior.
older Podman
→ slirp4netns commonly default
Podman 5.0+
→ pasta default
Podman 6+
→ pasta is the only supported rootless user-mode stackslirp4netns usually placed containers on a private range such as 10.0.2.0/24 with its own port handler and slirp-specific options. pasta copies host addressing and routes instead. Do not paste slirp configuration or address assumptions into a Podman 5/6 pasta environment without retesting.
Run a rootless container with default pasta networking
Start with what a normal podman run sees. List addresses inside the container:
podman run --rm docker.io/library/alpine:3.20 ip -4 addr show enp0s32: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65520 qdisc fq_codel state UNKNOWN qlen 1000
inet 10.0.2.15/24 brd 10.0.2.255 scope global noprefixroute enp0s3
valid_lft forever preferred_lft foreverCheck routes in the same namespace:
podman run --rm docker.io/library/alpine:3.20 ip routedefault via 10.0.2.2 dev enp0s3 metric 116
10.0.2.0/24 dev enp0s3 scope link metric 116Compare with the host main interface:
ip -4 addr show enp0s32: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
altname enx0800272dcd83
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic noprefixroute enp0s3
valid_lft 29550sec preferred_lft 29550secpasta copied the host enp0s3 address and default route into the container namespace. The interface name matches the host path, which is why rootless containers can look like they share the host LAN address.
How pasta provides outbound Internet access
Test a normal HTTPS request from a default rootless container:
podman run --rm docker.io/library/alpine:3.20 wget -qO- --timeout=10 https://example.comThe lab receives the example.com HTML page:
<!doctype html><html lang="en"><head><title>Example Domain</title><link rel="icon" href="data:,">...At a high level:
- the application opens a connection inside the container namespace
- pasta proxies traffic without root privileges on the host
- outbound connections can use addressing behavior closer to the host main path than classic slirp NAT
- no user-defined bridge is required for basic outbound access
This article does not dive into pasta packet internals. For architecture context across rootful and rootless modes, see Podman networking modes.
Why a pasta container cannot reach the host by the main host IP
Because pasta copied 10.0.2.15 into the container, traffic to that address can target the container namespace instead of a service on the host. This is expected pasta behavior on a simple single-interface host, not a random routing bug.
With a host HTTP server on 0.0.0.0:8080, try the shared main IP from inside the container:
podman run --rm docker.io/library/alpine:3.20 wget -qO- --timeout=3 http://10.0.2.15:8080/wget: can't connect to remote host (10.0.2.15): Connection refusedThat does not mean a rootless container can never reach the host. Modern Podman adds dedicated host-access mappings. Use host.containers.internal instead of the copied interface address — see Access the host from a Podman container for bind-address checks, host-gateway, and pasta-specific troubleshooting.
Access the host with host.containers.internal
Podman 5.3+ improved pasta host-access integration so containers can reach host services through a stable internal hostname.
Resolve the managed name:
podman run --rm docker.io/library/alpine:3.20 getent hosts host.containers.internal169.254.1.2 host.containers.internal host.containers.internalWith the host server listening on 0.0.0.0:8080, the same container can fetch it:
podman run --rm docker.io/library/alpine:3.20 wget -qO- http://host.containers.internal:8080/The HTML directory listing returns successfully. Do not hard-code 169.254.1.2 in application configuration — always prefer host.containers.internal. Full host-access behavior belongs in the dedicated host-access guide linked above.
pasta and gateway mapping
By default Podman invokes pasta with gateway-to-host mapping disabled, equivalent to passing --no-map-gw. To allow the container default gateway address to reach the host intentionally:
podman run --rm --network pasta:--map-gw docker.io/library/alpine:3.20 ip routedefault via 10.0.2.2 dev enp0s3 metric 116
10.0.2.0/24 dev enp0s3 scope link metric 116Use --map-gw only when gateway-based host access is a deliberate requirement. It changes an isolation default; prefer host.containers.internal for ordinary host service access.
Configure pasta options on podman run
Podman passes pasta CLI options after pasta: in the --network value. Comma separates option tokens.
Change MTU
Some environments need an explicit MTU instead of pasta defaults:
podman run --rm --network pasta:--mtu,1500 docker.io/library/alpine:3.20 ip link show enp0s32: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN qlen 1000
link/ether 4a:a0:96:cd:f0:04 brd ff:ff:ff:ff:ff:ffThe lab default pasta container reported mtu 65520 on enp0s3; --mtu,1500 overrides that for the interface inside the namespace.
Syntax reminder
The general form is:
--network pasta:OPTION,VALUE,OPTIONTeach the Podman syntax here, not every pasta flag from pasta(1). Add only options you have tested on your installed passt build.
Configure default pasta options in containers.conf
To apply pasta flags to every rootless container, set defaults in containers.conf:
[network]
pasta_options = [
"--mtu",
"1500"
]Precedence is roughly:
- distribution and user
containers.confdefaults - per-run
--network pasta:...overrides for many options
Compare your file against the containers.conf(5) man page for the exact array format on your containers/common version. Full configuration precedence is outside this article.
default_rootless_network_cmd
On Podman 5.x you can select the rootless user-mode stack explicitly:
[network]
default_rootless_network_cmd = "pasta"Valid Podman 5-era values include pasta and slirp4netns where slirp remains installed. Do not change away from pasta without a specific compatibility reason — slirp assumptions rarely transfer cleanly.
default_rootless_network_cmd = "slirp4netns" as a Podman 6 solution. Plan a pasta or rootless-bridge migration before upgrading — see Podman 5 to 6 migration.
pasta vs slirp4netns
| pasta | slirp4netns |
|---|---|
| default since Podman 5.0 | older rootless network stack |
| copies host addressing and routes | usually assigns a separate private range |
| good outbound source-IP behavior on default path | behavior depends on slirp port handler |
| modern Podman path | legacy on Podman 5.x only |
| supported on Podman 6 | removed on Podman 6 |
One major migration pain is address assumptions. slirp-based workflows often expected:
10.0.2.0/24or slirp-only options such as allow_host_loopback, cidr=10.0.2.0/24, and port_handler=.... Those settings do not apply to pasta. Retest host access, published ports, and container-to-container paths after switching defaults.
What broke when Podman switched from slirp4netns to pasta?
| Area | slirp-era assumption | pasta reality |
|---|---|---|
| Host main IP access | sometimes reached host via slirp gateway tricks | container may hold the same main IP locally |
| Container-to-container via host-published port | depended on slirp forwarding layout | pasta addressing can change that path |
slirp-only containers.conf options |
tuned slirp port handler and CIDR | ignored or invalid under pasta |
| Podman 4 networking docs | describe slirp defaults | default rootless path is pasta on Podman 5+ |
Not every workload broke. Outbound Internet access, published ports on default pasta, and user-defined rootless bridges still work — but scripts that hard-coded slirp addresses or host-loopback behavior need retesting.
Rootless user-defined bridge networks
Rootless does not mean pasta only. Create a bridge when multiple containers need a shared subnet and Aardvark DNS:
podman network create rootless-bridgerootless-bridgeInspect DNS and addressing:
podman network inspect --format 'DNSEnabled={{.DNSEnabled}} Subnet={{(index .Subnets 0).Subnet}} Gateway={{(index .Subnets 0).Gateway}}' rootless-bridgeDNSEnabled=true Subnet=10.89.0.0/24 Gateway=10.89.0.1Attach a container and read its eth0 address:
podman run --rm --network rootless-bridge docker.io/library/alpine:3.20 ip -4 -o addr show eth02: eth0 inet 10.89.0.3/24 brd 10.89.0.255 scope global eth0\ valid_lft forever preferred_lft foreverUnlike rootful Podman, rootless containers do not use the podman bridge by default. The normal rootless path uses pasta; create or explicitly select a bridge network when you need bridge subnet and Aardvark DNS semantics. Netavark manages the bridge; rootless networking adds an extra user network namespace behind it.
Start a named container and resolve it from a sibling on the same network:
podman run -d --name web1 --network rootless-bridge docker.io/library/alpine:3.20 sleep 300From another container on rootless-bridge, look up the name:
podman run --rm --network rootless-bridge docker.io/library/alpine:3.20 getent hosts web110.89.0.2 web1Aardvark DNS on the bridge answers the container's short hostname and assigned IP. Netavark 1.15+ no longer adds the legacy dns.podman search domain — do not expect web1.dns.podman in modern output. Detailed DNS behavior lives in Podman container DNS. Bridge creation options belong in Create and manage Podman networks.
Clean up the lab container when finished:
podman rm -f web1Inspect the rootless network namespace
Rootless bridge operations use an extra networking namespace. Enter it with:
podman unshare --rootless-netns ip -4 addr show1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
inet 127.0.0.1/8 scope host lo
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65520 qdisc fq_codel state UNKNOWN group default qlen 1000
inet 10.0.2.15/24 brd 10.0.2.255 scope global noprefixroute enp0s3
3: podman1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65520 qdisc noqueue state UP group default qlen 1000
inet 10.89.0.1/24 brd 10.89.0.255 scope global podman1podman1 is the rootless bridge interface at 10.89.0.1. This command is for inspection and troubleshooting — not a replacement for normal podman run networking.
Rootless bridge port forwarding and rootlessport
On Podman 5.8.x, rootless containers attached to user-defined bridge networks use rootlessport for -p forwarding. Connectivity works, but application logs may not show the original remote client address.
Publish nginx on a rootless bridge:
podman run -d --name bridge-web --network rootless-bridge -p 18091:80 docker.io/library/nginx:alpineRequest the published port from the host:
curl -s --max-time 3 http://127.0.0.1:18091/ >/dev/nullRead the nginx access log:
podman logs bridge-web 2>&1 | tail -110.89.0.5 - - [22/Aug/2026:19:00:18 +0000] "GET / HTTP/1.1" 200 896 "-" "curl/8.12.1" "-"The log shows 10.89.0.5, a forwarder address on the rootless bridge, not 127.0.0.1 where curl ran. That is the rootlessport behavior on Podman 5.x bridge networks.
Default rootless pasta publishing does not use rootlessport the same way. A pasta-published port on the lab host is handled by pasta.avx2 and preserves client source IP — see Podman port mapping for syntax and troubleshooting.
Remove the lab container:
podman rm -f bridge-webPreserve source IP on rootless bridge networks
passt build that provides Pesto forwarding. The lab runs Podman 5.8.2, where bridge forwarding uses rootlessport and this option is not the tested procedure.
Podman 6 introduces an experimental bridge forwarder:
[network]
rootless_port_forwarder = "pasta"That selects pasta kernel-level forwarding through Pesto instead of rootlessport, with the goal of preserving the original client source IP. Requirements and behavior may change between releases. On Podman 5.x, use default pasta networking when client IP preservation matters and you do not need a custom bridge.
Rootless port publishing
Published ports work rootless on both default pasta and bridge networks. The syntax is the same as rootful Podman:
podman run --rm -d -p 18090:80 --name pubtest docker.io/library/alpine:3.20 sh -c 'while true; do echo -e "HTTP/1.0 200 OK\r\n\r\nok" | nc -l -p 80; done'Confirm the host listener:
ss -lntp | grep 18090LISTEN 0 128 *:18090 *:* users:(("pasta.avx2",pid=197398,fd=6))Test the mapping:
curl -s --max-time 3 http://127.0.0.1:18090/okFull -p / -P syntax, pod publishing rules, and failure modes belong in Podman port mapping.
Remove the test container:
podman rm -f pubtestRootless ports below 1024
If podman run -p 80:80 ... fails because an unprivileged user cannot bind a privileged host port, see Rootless Podman privileged ports for sysctl, redirect, and proxy approaches. This article does not duplicate those remediations.
Rootless DNS
DNS behavior depends on the network mode:
- Default pasta: Podman integrates pasta with DNS forwarding and generates appropriate
/etc/resolv.confcontent for the container. - Rootless bridge: Aardvark DNS resolves container names and aliases on DNS-enabled custom networks, as shown in the bridge section above.
Detailed inspection and troubleshooting belong in Podman container DNS.
Rootless Macvlan and IPvlan
Do not use macvlan or ipvlan as solutions for normal rootless Podman networking. Although these drivers exist in Netavark, rootless networking runs inside a separate network namespace and cannot access the host interfaces required for normal macvlan/ipvlan attachment.
Use rootful Podman when you need direct macvlan or ipvlan attachment to a physical host interface. See Create and manage Podman networks for those drivers.
Podman 5 vs Podman 6 rootless networking
| Podman 5.x | Podman 6+ |
|---|---|
| pasta default since 5.0 | pasta only supported rootless user-mode stack |
| slirp4netns still available on many builds | slirp4netns removed |
default_rootless_network_cmd can select slirp or pasta |
do not rely on slirp configuration |
rootless bridge uses rootlessport for -p |
rootlessport remains default bridge forwarder initially |
source IP often lost on bridge -p |
experimental Pesto/pasta bridge forwarder can preserve it |
| legacy CNI setups may still exist | CNI removed |
Do not teach Podman 6-only behavior to readers still on distributions shipping Podman 5.x. Version-gate slirp removal and Pesto forwarding explicitly.
Which rootless network should you use?
| Requirement | Recommended starting point |
|---|---|
| normal rootless container with outbound networking | default pasta |
| multiple rootless containers needing shared network/DNS | user-defined bridge |
| container name resolution on a custom subnet | rootless bridge with Aardvark DNS |
| reach host services from the container | host.containers.internal — see host-access guide |
| direct host network namespace | --network=host |
| no external networking | --network=none |
| legacy slirp-specific app on Podman 5 only | slirp4netns only if intentionally retained |
| Podman 6 greenfield | pasta or rootless Netavark bridge |
References
- Podman rootless tutorial — networking
- podman-run man page — network modes
- containers.conf man page
- passt / pasta documentation
Summary
Rootless Podman networking works because pasta (or, on Podman 5.x only, slirp4netns) provides outbound connectivity and port forwarding without root on the host. From Podman 5.0 onward, pasta is the default: it copies the host main interface address and routes into the container namespace, which gives useful outbound behavior but breaks the naive pattern of curling the host by its main IP.
For host service access, use host.containers.internal and read the dedicated host-access guide rather than guessing bridge gateways or copied interface addresses. Tune pasta through --network pasta:... and pasta_options in containers.conf when you need MTU or other tested flags.
Rootless is not limited to pasta. User-defined Netavark bridge networks give you a separate eth0 subnet, Aardvark DNS, and rootlessport forwarding on Podman 5.x — at the cost of losing the original client source IP in many bridge publish setups. Default pasta publishing preserves client IPs; bridge publishing on Podman 5.8 typically does not. Podman 6 may change that trade-off with experimental Pesto forwarding.
Before upgrading to Podman 6, retire slirp-specific configuration and retest anything that depended on 10.0.2.x addressing or slirp-only options. For port syntax and privileged-port failures, continue with the port-mapping and privileged-port guides rather than re-deriving -p behavior here.

