CertGrid CertGrid
Concepts·Podman

Podman Pasta Networking

A rootless container on the default network has `eth0` set to 192.168.0.21 - the host's own LAN address - and an empty IPAddress field. It never touches the 10.88.0.0/16 bridge, because rootless has no authority to create one.

Networking Guide 29 of 47 Advanced

Written against the versions above. Podman follows the distribution here rather than a vendor repository, so the version you get is the one Ubuntu shipped. The commands are stable across 5.x.

Every command on this page ran on podman01.
Server NameIP AddressOSRolesCPURAMHDD
PODMAN01192.168.0.21Ubuntu 26.04 LTSPrimary Container Host2 Core4 GB50 GB

Before you start

  1. Three containers, three different addressing schemes

    Same host, same engine, same image, and three completely different answers to "what is my IP":

    | network | eth0 | |---|---| | default (podman) | 192.168.0.21/24 - the host's own address | | user-defined (appnet) | 10.89.0.2/24 - a real bridge address | | --network slirp4netns | 10.0.2.100/24 on a tap0 interface |

    The first one is the surprising one, and it is the default. A rootless container with no --network flag does not get a bridge address - it sees the host's interface configuration, which is why NetworkSettings.IPAddress is empty. There is no Podman-managed address to report.

    The 10.88.0.0/16 subnet from guide 50 is defined and, rootless, simply unused.

    bash Example session
    podman run --rm --network host docker.io/library/alpine ip -o addr show eth02: eth0    inet 192.168.0.21/24 brd 192.168.0.255 scope global eth0\       valid_lft forever preferred_lft forever2: eth0    inet6 fe80::215:5dff:fe01:115a/64 scope link \       valid_lft forever preferred_lft foreverpodman run --rm --network slirp4netns docker.io/library/alpine ip -o addr show tap02: tap0    inet 10.0.2.100/24 brd 10.0.2.255 scope global tap0\       valid_lft forever preferred_lft forever2: tap0    inet6 fd00::b0e5:baff:fe9a:6933/64 scope global dynamic flags 100 \       valid_lft 86400sec preferred_lft 14400sec2: tap0    inet6 fe80::b0e5:baff:fe9a:6933/64 scope link \       valid_lft forever preferred_lft forever

    Expected resultThe host's address from --network host, and 10.0.2.100 from slirp4netns.

    Success conditionYou have seen two of the three schemes side by side.

  2. The process doing it

    pgrep -a pasta shows what is actually implementing the default rootless network:

    /usr/bin/pasta --config-net
      --pid /run/user/1000/containers/networks/rootless-netns/rootless-netns-conn.pid
      --dns-forward 169.254.1.1 -t none -u none -T none -U none --no-map-gw --quiet
      --netns /run/user/1000/containers/networks/rootless-netns/rootless-netns
      --map-guest-addr 169.254.1.2

    pasta - "pack a subtle tap abstraction" - is a userspace network device. It arrived as part of the passt package in guide 1.

    The reason it exists is the whole rootless story. Creating a bridge, attaching a veth pair to it and writing NAT rules all require privilege over the host's network stack, and an unprivileged user has none. So instead pasta runs as you, copies packets between the container's namespace and a normal socket it opens as your user, and configures the container to look like the host.

    Two flags are worth marking. --netns .../rootless-netns is a shared namespace - one per user, not one per container, which is why the teardown error in guide 23 talks about killing *the* network process. And --map-guest-addr 169.254.1.2 is the address that will turn up in guide 55.

    bash Example session
    pgrep -a pasta8421 /usr/bin/pasta --config-net --pid /run/user/1000/containers/networks/rootless-netns/rootless-netns-conn.pid --dns-forward 169.254.1.1 -t none -u none -T none -U none --no-map-gw --quiet --netns /run/user/1000/containers/networks/rootless-netns/rootless-netns --map-guest-addr 169.254.1.2podman inspect alpha --format 'ip={{.NetworkSettings.Networks.appnet.IPAddress}}'ip=10.89.0.2

    Expected resultOne pasta process holding a shared netns, and a real bridge IP for the container that has one.

    Success conditionYou can name the process that gives a rootless container its network.

  3. pasta, slirp4netns, and when to override

    slirp4netns is the older implementation of the same idea and still available with --network slirp4netns. It gives the container a tap0 at 10.0.2.100/24 - a fixed, private address unrelated to your host.

    Podman 5 defaults to pasta because it is faster and, importantly, preserves the source address of outbound connections. slirp4netns rewrites everything to look like it came from 10.0.2.x, which breaks anything the far end does with the client address.

    What to reach for, and why:

    • Default (pasta) - almost always right. Outbound works, performance is good.
    • A user-defined network - whenever containers must reach *each other* by name or by stable address. This is the answer to most "how do I connect two containers" questions.
    • --network host - no isolation at all; the container uses your namespace directly. Note it looked identical to the pasta case in step 1, which is a fair summary of how close the default is to no isolation, network-wise.
    • --network none - no interfaces beyond loopback. Good for a build or a batch job that must not reach anything.
    • --network slirp4netns - only for compatibility with something that expects 10.0.2.x.

    The mental model that makes rootless networking predictable: you get no bridge unless you ask for one. Ask, by creating a network, and everything behaves the way the documentation assumes.

    bash Example session
    podman run --rm --network none docker.io/library/alpine ip -o addr1: lo    inet 127.0.0.1/8 scope host lo\       valid_lft forever preferred_lft forever1: lo    inet6 ::1/128 scope host \       valid_lft forever preferred_lft foreverpodman network create --driver bridge --subnet 10.90.0.0/24 --gateway 10.90.0.1 fixedfixedpodman network inspect fixed --format 'subnet={{range .Subnets}}{{.Subnet}} gw={{.Gateway}}{{end}}'subnet=10.90.0.0/24 gw=10.90.0.1podman run --rm --network fixed docker.io/library/alpine ip -o addr show eth02: eth0    inet 10.90.0.2/24 brd 10.90.0.255 scope global eth0\       valid_lft forever preferred_lft forever2: eth0    inet6 fe80::bc24:94ff:fea8:55cb/64 scope link tentative \       valid_lft forever preferred_lft forevertime="2026-08-22T12:03:25Z" level=error msg="Removing container 3d5a52b3359f8991421ae73898d382d4db809d426d2cbce61beb1f4e2fb12b0e: cleaning up container 3d5a52b3359f8991421ae73898d382d4db809d426d2cbce61beb1f4e2fb12b0e: removing container 3d5a52b3359f8991421ae73898d382d4db809d426d2cbce61beb1f4e2fb12b0e network: 1 error occurred:\n\t* rootless netns: kill network process: permission denied\n\n"

    Expected resultLoopback only from --network none, and a container on a network with a subnet you chose.

    Success conditionYou can pick a network mode and say what address the container will get.

Troubleshooting

Official sources