Rootless Podman Networking with pasta

Tested on Red Hat Enterprise Linux 10.2 (Coughlan)
Package podman-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_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:

text
Rootless container
network namespace
pasta
host networking

When you attach a rootless container to a user-defined bridge:

text
Rootless container
Netavark bridge namespace
rootless networking layer
host

Rootless 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:

bash
podman info --format 'RootlessNetworkCmd={{.Host.RootlessNetworkCmd}}'

On the lab host:

output
RootlessNetworkCmd=pasta

Also verify the installed packages on the host:

bash
rpm -q podman passt netavark aardvark-dns
output
podman-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_64

passt 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.

text
older Podman
    → slirp4netns commonly default

Podman 5.0+
    → pasta default

Podman 6+
    → pasta is the only supported rootless user-mode stack

slirp4netns 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:

bash
podman run --rm docker.io/library/alpine:3.20 ip -4 addr show enp0s3
output
2: 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 forever

Check routes in the same namespace:

bash
podman run --rm docker.io/library/alpine:3.20 ip route
output
default via 10.0.2.2 dev enp0s3  metric 116 
10.0.2.0/24 dev enp0s3 scope link  metric 116

Compare with the host main interface:

bash
ip -4 addr show enp0s3
output
2: 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 29550sec

pasta 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:

bash
podman run --rm docker.io/library/alpine:3.20 wget -qO- --timeout=10 https://example.com

The lab receives the example.com HTML page:

output
<!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:

bash
podman run --rm docker.io/library/alpine:3.20 wget -qO- --timeout=3 http://10.0.2.15:8080/
output
wget: can't connect to remote host (10.0.2.15): Connection refused

That 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:

bash
podman run --rm docker.io/library/alpine:3.20 getent hosts host.containers.internal
output
169.254.1.2       host.containers.internal  host.containers.internal

With the host server listening on 0.0.0.0:8080, the same container can fetch it:

bash
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:

bash
podman run --rm --network pasta:--map-gw docker.io/library/alpine:3.20 ip route
output
default via 10.0.2.2 dev enp0s3  metric 116 
10.0.2.0/24 dev enp0s3 scope link  metric 116

Use --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:

bash
podman run --rm --network pasta:--mtu,1500 docker.io/library/alpine:3.20 ip link show enp0s3
output
2: 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:ff

The 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:

text
--network pasta:OPTION,VALUE,OPTION

Teach 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:

toml
[network]
pasta_options = [
  "--mtu",
  "1500"
]

Precedence is roughly:

  • distribution and user containers.conf defaults
  • 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:

toml
[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.

IMPORTANT
Podman 6 removes slirp4netns support. Do not publish 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:

text
10.0.2.0/24

or 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:

bash
podman network create rootless-bridge
output
rootless-bridge

Inspect DNS and addressing:

bash
podman network inspect --format 'DNSEnabled={{.DNSEnabled}} Subnet={{(index .Subnets 0).Subnet}} Gateway={{(index .Subnets 0).Gateway}}' rootless-bridge
output
DNSEnabled=true Subnet=10.89.0.0/24 Gateway=10.89.0.1

Attach a container and read its eth0 address:

bash
podman run --rm --network rootless-bridge docker.io/library/alpine:3.20 ip -4 -o addr show eth0
output
2: eth0    inet 10.89.0.3/24 brd 10.89.0.255 scope global eth0\       valid_lft forever preferred_lft forever

Unlike 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:

bash
podman run -d --name web1 --network rootless-bridge docker.io/library/alpine:3.20 sleep 300

From another container on rootless-bridge, look up the name:

bash
podman run --rm --network rootless-bridge docker.io/library/alpine:3.20 getent hosts web1
output
10.89.0.2         web1

Aardvark 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:

bash
podman rm -f web1

Inspect the rootless network namespace

Rootless bridge operations use an extra networking namespace. Enter it with:

bash
podman unshare --rootless-netns ip -4 addr show
output
1: 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 podman1

podman1 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:

bash
podman run -d --name bridge-web --network rootless-bridge -p 18091:80 docker.io/library/nginx:alpine

Request the published port from the host:

bash
curl -s --max-time 3 http://127.0.0.1:18091/ >/dev/null

Read the nginx access log:

bash
podman logs bridge-web 2>&1 | tail -1
output
10.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:

bash
podman rm -f bridge-web

Preserve source IP on rootless bridge networks

IMPORTANT
The setting below applies to Podman 6+ with a sufficiently new 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:

toml
[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:

bash
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:

bash
ss -lntp | grep 18090
output
LISTEN 0      128                                     *:18090            *:*    users:(("pasta.avx2",pid=197398,fd=6))

Test the mapping:

bash
curl -s --max-time 3 http://127.0.0.1:18090/
output
ok

Full -p / -P syntax, pod publishing rules, and failure modes belong in Podman port mapping.

Remove the test container:

bash
podman rm -f pubtest

Rootless 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.conf content 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


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.


Frequently Asked Questions

1. What is the default rootless network stack in Podman 5?

pasta is the default rootless user-mode network stack from Podman 5.0 onward. podman info reports RootlessNetworkCmd=pasta on modern installs. slirp4netns was the older default and remains selectable on some Podman 5.x builds but is removed in Podman 6.

2. Why does my rootless container have the same IP as the host?

pasta copies the host main interface address and routes into the container network namespace. That gives useful outbound source-IP behavior but means the host main IP is local inside the container. Use host.containers.internal for container-to-host access instead of the shared interface address.

3. Can rootless Podman use bridge networks?

Yes. Rootless users can create user-defined Netavark bridge networks with podman network create and attach containers with --network. Container-to-container DNS works on DNS-enabled bridges through Aardvark. The default rootless run path still uses pasta unless you select a bridge network explicitly.

4. Does rootless port publishing preserve the client source IP?

Default rootless pasta forwarding preserves the original client source IP. Rootless containers on a user-defined bridge use rootlessport on Podman 5.x, which typically does not preserve the remote client IP in application logs. Podman 6 may offer an experimental pasta Pesto forwarder for bridges.

5. Can I still use slirp4netns on Podman 6?

No. Podman 6 removes slirp4netns as a supported rootless network stack. Migrate slirp-specific options and address assumptions to pasta or rootless Netavark bridge networks before upgrading.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)