Podman Installation on Ubuntu 26.04
Podman installs from Ubuntu's own repository in a single command. Then `systemctl is-active podman.service` reports `inactive`, and a container runs anyway - which is the whole architecture in one line of output.
Foundations Guide 1 of 47 Beginner
- OSUbuntu 26.04 LTS (resolute)
- Podman5.7.0
- Runtimecrun 1.21
- Networknetavark 1.16.1
- TimeAbout 14 min
- Reviewed22 August 2026
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.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| PODMAN01 | 192.168.0.24 | Ubuntu 26.04 LTS | Primary Container Host | 2 Core | 4 GB | 50 GB |
Before you start
- A user with
sudoaccess. Every install command here is prefixed with it; nothing after the install needs it. - Outbound access to
archive.ubuntu.comfor the packages anddocker.iofor the first image. - No other container engine running. If Docker is installed, this guide still works, but its leftover bridge and firewall rules will appear in output later in the path - purge it first if you want the networking track to match what you read.
-
Start from a host with no engine on it
Worth proving rather than assuming, because almost every confusing result later in this path comes from state left behind by something else.
Three answers to collect. The release, so you know which package archive you are pulling from. Then
podman --version, which should fail - and note the exit code,127, meaning the command does not exist rather than the engine being broken. Then the systemd unit files, which should list none.That last one is the one to remember. Come back to it after the install.
bash Example session . /etc/os-release && echo "$PRETTY_NAME"Ubuntu 26.04 LTSpodman --versionbash: line 1: podman: command not found[exit 127]systemctl list-unit-files 'podman*'UNIT FILE STATE PRESET 0 unit files listed.[exit 1]Expected resultUbuntu 26.04 LTS,
command not foundwith exit 127, and0 unit files listed.Success conditionYou have confirmed there is no Podman and no Podman service on the machine.
-
One command, from Ubuntu's own repository
Podman needs no third-party repository. This is a real difference from installing Docker Engine, where the first half of the job is adding a GPG key and an apt source. Here the package is in
universe, which is enabled by default.Check what you are about to get first.
apt-cache policynames the candidate version and the archive it comes from, which is the honest answer to "which Podman will I have" - it is whatever the distribution froze, not the latest release.Then install. Note the size of what arrives: 31 new packages, 291 MB. Podman is a command, not a service, but it is a command with a lot of parts, and step 5 goes through the ones that matter.
bash Example session apt-cache policy podmanpodman: Installed: (none) Candidate: 5.7.0+ds2-3build1 Version table: 5.7.0+ds2-3build1 500 500 http://in.archive.ubuntu.com/ubuntu resolute/universe amd64 Packagessudo -n apt-get updateHit:1 http://security.ubuntu.com/ubuntu resolute-security InReleaseHit:2 http://in.archive.ubuntu.com/ubuntu resolute InReleaseGet:3 http://in.archive.ubuntu.com/ubuntu resolute-updates InRelease [137 kB]Hit:4 http://deb.gierens.de stable InReleaseHit:5 http://in.archive.ubuntu.com/ubuntu resolute-backports InReleaseGet:6 http://in.archive.ubuntu.com/ubuntu resolute-updates/main amd64 Packages [492 kB]Get:7 http://in.archive.ubuntu.com/ubuntu resolute-updates/universe amd64 Packages [244 kB]Fetched 873 kB in 3s (302 kB/s)Reading package lists...sudo -n apt-get install -y podmanReading package lists...Building dependency tree...Reading state information...Solving dependencies...The following additional packages will be installed: aardvark-dns buildah catatonit conmon containers-storage cpp cpp-15 cpp-15-x86-64-linux-gnu cpp-x86-64-linux-gnu criu crun fuse-overlayfs gcc-15-base golang-github-containers-common golang-github-containers-image libcompel1 libcriu2 libisl23 libmpc3 libnet9 libprotobuf-c1 libslirp0 libsubid5 libyajl2 netavark passt python3-protobuf python3-pycriu slirp4netns uidmapSuggested packages: cpp-doc gcc-15-locales cpp-15-doc libwasmedge0 containernetworking-plugins docker-composeThe following NEW packages will be installed: aardvark-dns buildah catatonit conmon containers-storage cpp cpp-15 cpp-15-x86-64-linux-gnu cpp-x86-64-linux-gnu criu crun fuse-overlayfs gcc-15-base golang-github-containers-common golang-github-containers-image libcompel1 libcriu2 libisl23 libmpc3 libnet9 libprotobuf-c1 libslirp0 libsubid5 libyajl2 netavark passt podman python3-protobuf python3-pycriu slirp4netns uidmap0 upgraded, 31 newly installed, 0 to remove and 54 not upgraded.Expected resultCandidate
5.7.0+ds2-3build1fromresolute/universe, then 31 newly installed packages.Success condition
apt-get installcompleted with no held or broken packages. -
Nothing is running, and that is correct
This is the step that matters, and it is why the install guide is worth reading even if you already know
apt.podman --versionanswers.podman inforeports a working runtime, a network backend androotless=true. So the engine is installed and functional. Now ask systemd about it:systemctl is-active podman.servicesays inactive, exit 3systemctl is-enabled podman.socketsays disabled, exit 1
Both of those are the correct, healthy, intended state. There is no daemon to start.
podmanis a command that forks a container and exits; the container's supervisor is a small process calledconmon, one per container, not a long-running service that owns them all.A
podman.socketunit does exist and you will enable it later - it serves the Docker-compatible API that lets realdocker-composetalk to Podman - but it is opt-in, and nothing you do in the next twenty guides needs it.bash Example session podman --versionbash: line 1: podman: command not found[exit 127]podman info --format 'runtime={{.Host.OCIRuntime.Name}} conmon={{.Host.Conmon.Version}}'runtime=crun conmon=conmon version 2.1.13, commit: unknownpodman info --format 'network={{.Host.NetworkBackend}} rootless={{.Host.Security.Rootless}}'network=netavark rootless=truesystemctl is-active podman.serviceinactive[exit 3]systemctl is-enabled podman.socketdisabled[exit 1]Expected result
podman version 5.7.0,runtime=crun,network=netavark,rootless=true, theninactiveanddisabled.Success conditionPodman answers for itself while systemd reports no running Podman service.
-
Run a container, and read what it claims
The image name is written out in full on purpose -
docker.io/library/hello-worldrather thanhello-world. Podman does not assume a registry, and the short form asks you a question it cannot ask without a terminal. That is its own guide later in this track; for now, be explicit.Read the output carefully, because it is wrong:
1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.There was no daemon. You confirmed that one step ago. What actually happened is that
podmanpulled the image itself, wrote it into your home directory, forked a container, and exited. The message comes from inside the image - it is a text file written by Docker, faithfully printed by Podman, describing an architecture that was not used.It is a good reminder for the rest of this path: an OCI image does not know or care which engine runs it, and plenty of container documentation is really Docker documentation.
The storage location is the second thing to notice.
graphrootis under/home/sysadmin/.local/share/containers/storage, not/var/lib/docker. You pulled an image without root and it went somewhere you own.bash Example session podman run --rm docker.io/library/hello-world 2>&1Trying to pull docker.io/library/hello-world:latest...Getting image source signaturesCopying blob sha256:4f55086f7dd096d48b0e49be066971a8ed996521c2e190aa21b2435a847198b4Copying config sha256:e2ac70e7319a02c5a477f5825259bd118b94e8b02c279c67afa63adab6d8685bWriting manifest to image destination Hello from Docker!This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/podman info --format 'graphroot={{.Store.GraphRoot}} driver={{.Store.GraphDriverName}}'graphroot=/home/sysadmin/.local/share/containers/storage driver=overlayExpected resultThe pull, then the greeting, then a graphroot under your own home directory.
Success conditionA container ran as your unprivileged user, with no daemon and no
sudo. -
What else arrived, and which guide uses it
Thirty-one packages is a lot for something that is "just a command". These are the ones worth knowing by name, because each becomes a subject later:
- crun - the OCI runtime that actually creates the container. The thing that calls
clone()and sets up the namespaces. - conmon - the per-container monitor. One of these per running container, holding its streams and reporting its exit. This is what a daemon would otherwise do.
- netavark and aardvark-dns - the network backend and its DNS server. Podman 4 replaced CNI with these.
- passt (providing
pasta) and slirp4netns - userspace networking, which is how an unprivileged process gets a network at all. - uidmap -
newuidmapandnewgidmap, the setuid helpers that let you map a range of subordinate UIDs into a container. - buildah -
podman buildis a front end to this. - criu - checkpoint and restore, which is how a running container gets frozen to disk.
One detail in the last command:
netavarkproduces no path. It is not on yourPATHbecause you never call it - Podman invokes it directly from/usr/lib/podman/. A helper the engine runs is not a command you run.And
/etc/subuidalready contains a range for your user. Ubuntu'suseraddallocates 65,536 subordinate UIDs per account, which is why rootless worked in step 4 with no setup at all. On a distribution that does not do this, that step would have failed.bash Example session dpkg -l | grep -E 'ii +(podman|netavark|aardvark-dns|conmon|crun|containers-common|passt|golang-github-containers-common)' | awk '{print $2, $3}'aardvark-dns 1.16.0-3conmon 2.1.13+ds1-2crun 1.21-1ubuntu3golang-github-containers-common 0.66.0+ds2-3netavark 1.16.1-3.1passt 0.0~git20260120.386b5f5-1podman 5.7.0+ds2-3build1command -v podman conmon crun netavark pasta/usr/bin/podman/usr/bin/conmon/usr/bin/crun/usr/bin/pastacat /etc/subuidsysadmin:100000:65536Expected resultSeven package versions, four of five binaries resolved, and a subuid range for your user.
Success conditionYou can name the runtime, the monitor, the network backend and the UID helper on your own machine.
- crun - the OCI runtime that actually creates the container. The thing that calls
Troubleshooting
podman: command not foundimmediately after a successful install.Why: The shell cached the failed lookup from before the install.
Fix:Run
hash -r, or open a new shell. The binary is at/usr/bin/podman- check withls -l /usr/bin/podmanbefore assuming the install failed.Error: short-name "hello-world" did not resolve to an alias and no unqualified-search registries are defined, or a prompt that cannot be answered.Why: Podman does not assume Docker Hub. A bare image name is ambiguous, and in a non-interactive shell there is nobody to ask.
Fix:Write the registry out -
docker.io/library/hello-world. Configuring short names is a guide of its own later in this track.ERRO[0000] cannot find UID/GID for userorthere might not be enough IDs available in the namespace.Why: No subordinate UID range for your account, so there is nothing to map into a user namespace.
Fix:Check
/etc/subuidand/etc/subgidname your user. If not,sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER, thenpodman system migrate.Error: kernel does not support overlayor storage falls back unexpectedly.Why: Rootless overlay needs either a kernel that permits unprivileged overlayfs or
fuse-overlayfs.Fix:
podman info --format '{{.Store.GraphDriverName}}'names the driver in use.fuse-overlayfsis installed as a dependency here and is the normal fallback.Podman works but a later networking guide shows a
docker0interface orDOCKER-USERfirewall chains.Why: Docker was installed on this host at some point. Purging the packages does not remove its bridge or its netfilter rules until the machine reboots.
Fix:
sudo apt-get purge docker-ce docker-ce-cli containerd.io, thensudo ip link delete docker0and reboot. Confirm withsudo iptables -S | grep -c dockerreturning0.