Podman Version Updater (Source Build, apt-managed)
Build any Podman release from GitHub for Ubuntu 26.04 and install Podman and its dependencies as an apt-tracked packages.
The script builds Podman (and its dependencies) from source, packages each as a .deb, and installs it via apt/dpkg — so it is a standard package install as far as your system is concerned, but the package itself is self-compiled rather than Canonical-shipped. Breakage, data loss, or unexpected system behavior are still possible.
Before running any of these scripts, you should back up:
- All container data, volumes, and images.
- Your entire home directory, or at least
~/.local/share/containers. - Any custom Quadlet unit files.
- Full system snapshots (Timeshift, LVM, or similar) if possible.
The author takes zero responsibility for data loss, broken containers, or unbootable systems. You assume all risk.
This toolchain was written and validated exclusively on:
- Ubuntu 26.04 (Resolute) running Podman 5.7.0 (apt-managed)
This script will only run on Ubuntu 26.04 and 25.10. Other versions and OSes may work, but you would have to remove the $ID and $VERSION-ID check (at your own risk) to get it to run. You may need to tweak dependencies in the script to get it to run on earlier versions of Ubuntu.
The updater script installs build dependencies via apt, but it does not verify whether those packages meet the minimum versions required by the Podman release you are building. Always read the official build instructions for your target tag and ensure your system's libraries are new enough.
- Every install is via
.deb. The script builds each component from source, stages the build into a throwaway directory, packages it withfpm, and installs it withapt-get install ./<component>.deb. Nothing is kept afterward — the staging directory and the built.debare deleted immediately.dpkg's own database is what tracks the install, exactly as if a real apt repo had shipped that version. - Dependencies always build to the newest known-good version, regardless of which Podman version you're installing. Netavark, Aardvark-dns, conmon, fuse-overlayfs, and containers-common are always brought current first.
- Give the script a version string, not a URL. See Usage below.
- The script backs up your running containers, stops services gracefully, verifies the new binary, and restores everything automatically.
- Every component is
apt-mark hold-ed after install, so a futureapt upgradecan't silently clobber your custom build if Ubuntu's repo ever ships a conflicting version. - If anything fails, nothing on the real filesystem has been touched yet — each component only replaces the previous install via
apt-get installafter its.debbuilds successfully. A failed build leaves whatever was previously installed completely intact.
Clone the repository:
git clone https://github.com/upmcplanetracker/podman-version-updater.git
cd podman-version-updater
chmod +x podman-version-updater.sh
Or download just the required file:
wget https://raw.githubusercontent.com/upmcplanetracker/podman-version-updater/main/podman-version-updater.sh
chmod +x podman-version-updater.sh
Run the script with a version string — not a URL. Accepted formats:
| Input | Resolves to |
|---|---|
v6.0.1 |
v6.0.1 |
6.0.1 |
v6.0.1 |
601 (exactly 3 digits) |
v6.0.1 |
latest |
newest non-prerelease tag on GitHub |
v6.0.1-rc1 |
v6.0.1-rc1 (prerelease suffixes pass through) |
6.0.1-beta |
v6.0.1-beta |
Compact digit input only works for exactly 3 digits — anything longer or ambiguous (6010, 61) is rejected; use dotted notation instead. Every version is checked against containers/podman's real GitHub tags before anything is built, so a typo fails fast instead of cloning a branch that doesn't exist.
Examples:
./podman-version-updater.sh 5.8.5
./podman-version-updater.sh 601
./podman-version-updater.sh latest
The script automatically:
- Installs build tools (
cargo,protoc,git,fpm— installed on first run if missing). - Builds Netavark and Aardvark-dns, packages each as a
.deb, and installs via apt. A postinst hook symlinks both into/usr/lib/podman/— Podman hardcodes this path and ignores$PATHwhen looking for network binaries, so this happens automatically on every install, not as a manual follow-up step. - Downloads crun's prebuilt release binary, wraps it in a
.deb, installs via apt. - Builds conmon, packages, installs via apt.
- Builds fuse-overlayfs and installs it without apt. This is a fallback storage driver only — it stays completely inactive if native rootless overlay is in use (the default on modern kernels), and is only invoked if Podman needs it.
- Packages the rootless containers-common configuration files (
storage.conf,containers.conf,registries.conf, seccomp profiles) as a config-only.deband installs it into/etc/containersand/usr/share/containers. - Then builds and installs Podman itself the same way, with a postinst hook that masks the system-level services (see below).
Exact target versions for each dependency are defined as variables at the top of podman-version-updater.sh — edit them there when new upstream releases come out. The script skips rebuilding any dependency already installed at the target version (checked via dpkg-query, not by probing $PATH).
The entire process is handled in one run, ensuring the network stack and Podman are always synchronised.
If you have a custom storage graphroot defined in /etc/containers/storage.conf, the script automatically creates a backup at /tmp/podman-config-backup-<TIMESTAMP> before the containers-common package overwrites it.
- Run the upgrade script with your target version.
- After the upgrade, verify your current graphroot:
podman info | grep graphRoot. - If it has reverted to the default, stop all containers, copy your original
storage.conffrom the backup directory back to/etc/containers/storage.conf, and restart the Podman services.
This keeps the script clean and ensures that users with advanced, non-standard storage setups don't lose their data during an upgrade.
When Podman is installed, its package deploys several systemd system-level unit files:
podman.service/podman.socketpodman-auto-update.service/podman-auto-update.timerpodman-clean-transient.servicepodman-restart.service
These units run as root and are designed for system-wide (rootful) Podman deployments. If you run rootless Podman with Quadlet, they are not only unnecessary — they are dangerous. Specifically, podman-clean-transient.service can reset ownership of container storage paths to root:root on boot, causing all rootless Quadlet services to fail to start.
systemctl disable removes the symlinks that cause a unit to start automatically, but systemd preset processing (triggered by package installs or daemon-reload in some configurations) can re-enable units whose preset state is enabled. You can verify this with:
systemctl list-unit-files | grep podman
# Look for units showing: disabled enabled
# The second column is the preset — "enabled" means it CAN be re-enabled.
systemctl mask replaces the unit file with a symlink to /dev/null, making it impossible for any mechanism to start the unit until you explicitly unmask it.
The podman package's own postinst script automatically masks all system-level Podman units on every install — this now lives inside the .deb itself (not a manual step run by the shell script), so it fires correctly even on a bare dpkg -i reinstall. A matching prerm unmasks them if the package is ever removed, so a later apt install podman from the stock repo doesn't inherit masked services.
If you ever hit container ownership errors after a reboot, verify unit state:
systemctl list-unit-files | grep podman
# All entries should show: masked enabled
If any show disabled instead of masked, re-run the mask manually:
sudo systemctl mask \
podman.service podman.socket \
podman-auto-update.service podman-auto-update.timer \
podman-clean-transient.service podman-restart.service
If you intentionally run system-level Podman containers alongside rootless ones, unmask only the specific units you need and ensure they do not touch your rootless user's storage paths.
There are two distinct rollback modes now, because there's no kept .deb file to fall back to — nothing is retained on disk by design.
./podman-version-updater.sh --rollback 5.8.5
This re-runs the normal build → package → apt-install pipeline targeting the older version you specify. apt install ./podman-<version>.deb overwrites the currently installed package — dpkg treats this as a plain reinstall/downgrade of the same package name, no different from installing any other version. This is slower than restoring a cached file would be, but there's nothing to store or clean up.
./podman-version-updater.sh --rollback-to-stock
This releases the apt-mark hold on every custom-built component (podman, conmon, crun, netavark, aardvark-dns, fuse-overlayfs, containers-common) and reinstalls whatever version Ubuntu's own repo currently provides via apt install --reinstall. This is the fast path with no rebuild and no waiting. It's the equivalent of the old --rollback behavior plus the manual cleanup commands that used to be a separate step.
Because both modes go through normal apt/dpkg operations, dpkg -l | grep -E 'podman|conmon|crun|netavark|aardvark|fuse-overlayfs|containers-common' always reflects exactly what's installed and there's no more checking /usr/local/bin versus /usr/bin to figure out which version is actually active.
podman --version
podman ps -a
systemctl --user status podman.socket
There's no hash -r step needed anymore because Podman installs to the standard /usr/bin/podman, the same path it was always at, so your shell's command hash never goes stale.
The script restarts all containers that were previously running, but you should always confirm:
podman ps -a
If any containers are stopped, start them manually:
podman start --all
For Quadlet users, check that the systemd units are active:
systemctl --user status $(basename -s .container ~/.config/containers/systemd/*.container)
# or for rootful Quadlets
sudo systemctl status $(basename -s .container /etc/containers/systemd/*.container)
Unlike the old /usr/local-shadow approach, there is only ever one installed Podman at a time — the custom-built package is what apt/dpkg considers installed, at the standard /usr/bin/podman path. There's no fallback binary sitting alongside it, and no $PATH precedence to think about.
-
Check what's installed and held:
dpkg -l | grep -E 'podman|conmon|crun|netavark|aardvark|fuse-overlayfs|containers-common' apt-mark showhold -
Go back to an older custom build:
--rollback <version>(rebuilds and reinstalls). -
Go back to Ubuntu's stock packages entirely:
--rollback-to-stock(drops the holds, reinstalls from the repo — instant, no rebuild).
Because holds are applied automatically after every install, a plain sudo apt upgrade will never silently overwrite a custom build — you always have to explicitly unhold or run one of the two rollback modes above.
There's much less to clean up now, since builds and packages are deleted automatically as part of every install:
~/podman-state-backup.txt– container state snapshot, removed automatically on a successful run./tmp/podman-build.*,/tmp/<component>-pkg-*– build/staging directories, removed automatically on success (and cleared on reboot regardless)./tmp/container-libs-common– temporary clone of the containers-common config repo, removed automatically after staging./tmp/podman-config-backup-<TIMESTAMP>– backup of your original/etc/containersconfig, created before every upgrade. Review before deleting if you have custom storage paths as this one is not auto-removed.
Remember: you are still replacing core system packages with self-compiled versions. Proceed with caution, full backups, and a thorough understanding of your own environment — the apt/dpkg integration makes it easier to track and roll back, but it doesn't make the underlying operation any less consequential.