Install Podman from the software repository on openEuler

To run containers with a regular user account, you can install Podman from the openEuler repository. This guide first covers rootless installation and running a web service with persistent volumes. When startup at boot is required, the service will then be managed by user-level systemd.

This tutorial uses openEuler 22.03 LTS SP4 and 24.03 LTS SP3 as examples.

Use a regular user account with sudo privileges. Use sudo when installing software or modifying system configurations, but never use sudo when running Podman: podman and sudo podman manage two separate sets of containers, images, and data by default. You cannot resolve permission issues for the same environment by temporarily adding sudo.


1. Check Current State and Install

First, check whether Podman is already installed and confirm the current user:

id -un
podman --version
rpm -q podman podman-plugins slirp4netns fuse-overlayfs systemd-pam

It’s normal if some commands or packages are not found. If Podman is already installed, use podman ps -a to check existing containers and preserve current configurations and data. If only the root account exists, set up a regular user with sudo access before proceeding with the rootless approach described here.

Before installing, ensure that the OS, everything, EPOL main, and both types of update repositories are enabled. Use dnf repolist to verify. The slirp4netns and fuse-overlayfs packages come from EPOL. If the independent EPOL Update repository is missing, refer to the section at the end titled “Missing packages or missing EPOL Update” to complete the setup.

Now install the required packages—no need to upgrade the entire system first:

sudo dnf --refresh install podman podman-plugins slirp4netns fuse-overlayfs systemd-pam curl
  • slirp4netns provides network access for regular users.
  • fuse-overlayfs enables layered storage.
  • podman-plugins adds CNI network name resolution support for these releases.
  • systemd-pam sets up the user’s runtime directory and systemd session.
    Dependencies are resolved automatically by DNF.

2. Verify Regular User Environment

After first installing systemd-pam, log out of your SSH or terminal session and log back in as this user. Then check:

echo "$XDG_RUNTIME_DIR"
grep "^${USER}:" /etc/subuid /etc/subgid

The first command should return a path like /run/user/1000. The /etc/subuid and /etc/subgid files should each contain an entry for your username, such as username:100000:65536. If any of these are missing, refer to the section on user sessions and UID/GID mapping below—do not proceed using root privileges.

Next, check the actual runtime environment:

podman info

All subsequent commands should be run under this regular user account. This guide does not require starting the system-level Podman API service. Local podman run commands can directly manage containers.


3. Run a Container with Persistent Volume

First, pull the image:

podman pull docker.io/library/nginx:stable

Use the full image name to avoid ambiguous short-name resolution. If the download is slow or fails, refer to the image or proxy configuration section below and retry.

Now create a named volume to store web content, write a test file, and start the service. Use oe-podman-html and oe-podman-web as example names—change them if you already have resources with the same names to avoid conflicts:

podman volume create oe-podman-html

podman run --rm \
  -v oe-podman-html:/data \
  docker.io/library/nginx:stable \
  sh -c 'printf "Hello from openEuler Podman\n" > /data/index.html'

podman run -d --name oe-podman-web \
  -p 127.0.0.1:8080:80 \
  -v oe-podman-html:/usr/share/nginx/html:ro \
  docker.io/library/nginx:stable

podman ps
curl -f http://127.0.0.1:8080/

If you see Hello from openEuler Podman, the container, port mapping, and volume are working. Temporary writes are deleted when the container exits, but the web content remains in the named volume.

Port 8080 listens only on the localhost of the machine running Podman. The curl command is also executed locally. To expose the service externally, configure the listening address, firewall, or reverse proxy as needed. For this single-container example, the default rootless network is sufficient—no custom network is required.

You can view logs with podman logs oe-podman-web, and stop/start the container using podman stop oe-podman-web and podman start oe-podman-web. A status of “Up” means the process is running, but if the web page is temporarily unavailable, check whether the application has started properly.


4. Enable Auto-Startup at Boot

If you only need temporary containers, you can skip this section. For long-running services, transfer the example container to user-level systemd management.

Before generating the service file, ensure no existing service file with the same name exists. This method works for both Podman 3.4 and 4.9 tested in this guide:

mkdir -p "$HOME/.config/systemd/user"

podman generate systemd --name --new oe-podman-web \
  > "$HOME/.config/systemd/user/container-oe-podman-web.service"

podman stop oe-podman-web
podman rm oe-podman-web

sudo loginctl enable-linger "$USER"
systemctl --user daemon-reload
systemctl --user enable --now container-oe-podman-web.service

This removes the previous demo container but preserves the volume. The --new flag ensures the service creates the container on start and removes it on stop—so always store business data in volumes or bind mounts, not in the container’s writable layer. enable-linger allows the user’s services to start even when the user is not logged in and continue running after logout. See Podman generate systemd documentation

Afterward, manage the container via systemd:

systemctl --user status container-oe-podman-web.service
curl -f http://127.0.0.1:8080/

# Stop or start as needed
systemctl --user stop container-oe-podman-web.service
systemctl --user start container-oe-podman-web.service

Newer Podman versions recommend Quadlet, so you may see a DEPRECATED warning when generating the service. This is a deprecation notice, not an error. Podman 4.9 in openEuler 24.03 LTS SP3 includes Quadlet, but it requires cgroup v2. The environments tested here use cgroup v1, so this guide uses the compatible method without requiring changes to the host’s boot configuration. Environments using cgroup v2 can refer to the Quadlet documentation.

You don’t need to reboot the entire machine immediately after installation. Later, during maintenance windows, you can verify recovery using systemctl --user is-active container-oe-podman-web.service and the curl command above.


Common Requirements and Issues

Slow Image Pull: Where to Configure Podman Image Registry

Podman does not read Docker’s daemon.json. Regular users can add Docker Hub mirror rules in their own config directory. If a file already exists or contains a docker.io rule, check and merge configurations:

mkdir -p "$HOME/.config/containers/registries.conf.d"
cat >"$HOME/.config/containers/registries.conf.d/20-docker-hub.conf" <<'EOF'
[[registry]]
prefix = "docker.io"
location = "docker.io"

[[registry.mirror]]
location = "docker.m.daocloud.io"
EOF

podman pull docker.io/library/nginx:stable

This example uses DaoCloud, but you can replace it with any trusted mirror, such as docker.1ms.run. Use only the hostname in location, without https://. HTTPS is used by default. Podman tries mirrors first, then falls back to the original registry. This rule only applies to docker.io and does not accelerate other registries or internal container access. Configuration syntax

The configuration takes effect on the next local Podman command—no daemon restart needed, unlike Docker.


Using an Existing HTTP Proxy with Podman

In this local rootless setup, image pulls are handled directly by the current Podman process. Set proxy variables in your current terminal session:

export http_proxy=http://127.0.0.1:7890
export https_proxy=http://127.0.0.1:7890
export no_proxy=localhost,127.0.0.1,::1

podman pull docker.io/library/nginx:stable

Replace the proxy address with one reachable from your machine. When using SSH, 127.0.0.1 refers to the remote host itself. This is different from the proxy settings for the background dockerd.

Podman may also pass these proxy variables into new containers by default. If you want the proxy to apply only to the host-side pull and not to containers, add --http-proxy=false to podman run. Inside containers, 127.0.0.1 refers to the container itself, not the host proxy. After transferring the service to systemd, the service won’t inherit proxy variables set in the terminal. You can pull the image manually first, then configure the service environment only if the service needs to pull images.

Podman run proxy options


Missing Packages or Missing EPOL Update

Use dnf repolist --all to check enabled repositories and inspect files in /etc/yum.repos.d/. The standard update path is update/$basearch/, which cannot replace EPOL/update/main/$basearch/. Repository IDs may differ—verify based on actual URLs.

For openEuler 22.03 LTS SP4 and 24.03 LTS SP3, if the independent EPOL Update repository is missing, add the following configuration. Adjust existing entries instead of duplicating:

OE_RELEASE="openEuler-$(rpm -q --qf '%{VERSION}' openEuler-release | sed 's/LTS/-LTS/; s/_/-/g')"
printf '%s\n' "$OE_RELEASE"

sudo tee /etc/yum.repos.d/openEuler-EPOL-Update.repo > /dev/null <<EOF
[EPOL-UPDATE]
name=openEuler EPOL Update
baseurl=https://repo.openeuler.openatom.cn/$OE_RELEASE/EPOL/update/main/$basearch/
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-openEuler
EOF

sudo dnf --refresh makecache --repo EPOL-UPDATE

Ensure the printed path matches your system, e.g., openEuler-24.03-LTS-SP3. After completing, re-run the installation command. If the issue occurs in another repository, fix the specific source reported—do not disable RPM signature checks or mix repositories from different distributions.


XDG_RUNTIME_DIR Empty or systemctl --user Unavailable

Check rpm -q systemd-pam, install if missing, then re-login as the user. Use normal SSH or local login—do not use su from root and manually set /run/user/1000, as this does not replace a full user session.

When a user service fails to start, check status first:

systemctl --user status container-oe-podman-web.service

If the current user lacks log access, administrators can check:

sudo journalctl -b _UID="$(id -u)" \
  _SYSTEMD_USER_UNIT=container-oe-podman-web.service --no-pager -n 100

Missing subuid/subgid or Mount Permission Issues

Rootless Podman requires additional UID/GID ranges for users. The system administrator should check /etc/subuid, /etc/subgid, and the actual user account to assign a range that doesn’t conflict with others. For example, after confirming 100000–165535 is available:

sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 "$USER"

This is just an example range—do not copy it verbatim for every user. If containers are already running, modifying the mapping requires stopping the user’s containers, running podman system migrate, and checking ownership of existing data. Never change this during active business operations. Rootless configuration guide

By default, the container’s root maps to the regular user who started Podman. Other container UIDs map to subordinate ranges. When binding host directories, set permissions based on the application’s UID/GID. SELinux labels are another layer: use :Z for private data directories and :z for shared directories between containers. These modify labels and should not be applied to entire home or system directories. Use :U with caution—it recursively changes ownership of the host directory, so verify the impact first. Volume mount options


Custom Network Fails: “nat table does not exist” or Container Name Not Resolvable

The default network used in the single-container example does not require this step. When setting up custom multi-container networks, the CNI implementation in these releases may fail with can't initialize iptables table 'nat' on minimal systems: regular users cannot load kernel modules in their namespace.

After confirming the error matches this issue, the administrator must load the required modules:

sudo modprobe iptable_nat
sudo modprobe ip6table_nat
```If automatic loading at boot is truly required, create `/etc/modules-load.d/podman-rootless.conf` and write the two module names into it. If the file already exists, merge the content and retry the original container operation.

Container name resolution requires the corresponding network plugin. First verify that `podman-plugins` is installed, then use `podman network inspect network-name` to check DNS status. Existing networks created before installing the plugin may need to be recreated after stopping related containers—do not assume that installing the package automatically fixes network configuration.

### What Cannot Be Directly Transferred When Switching from Docker

- Images can usually be re-pulled or transferred via `docker save` and `podman load` on the target user account. Image archives do not include data volumes or data written during container runtime—business data must be backed up and migrated separately.
- Docker and Rootless Podman differ in directory permissions, storage, and runtime identity. Do not let Podman take over `/var/lib/docker`. Test the new instance with different host ports first, and switch business traffic only after confirmation.
- Ordinary users cannot bind low-numbered ports such as 80 or 443 due to host policy restrictions; use an existing reverse proxy to forward to higher ports instead. Resource limits for Rootless Podman under cgroup v1 also have boundaries.
- `alias docker=podman` does not complete the migration. `podman compose` uses an external Compose provider in supported versions; Docker-compatible APIs are another layer entirely and must be verified per project—do not directly reuse Docker Compose success records.

This guide uses native Podman commands without installing additional Compose providers. For API and Compose scenarios, refer to [Podman API](https://docs.podman.io/en/latest/markdown/podman-system-service.1.html) and [podman compose](https://docs.podman.io/en/latest/markdown/podman-compose.1.html).

### Update and Removal Examples

Update packages via DNF; manage images and application data separately:

```bash
sudo dnf upgrade podman podman-plugins slirp4netns fuse-overlayfs
podman pull docker.io/library/nginx:stable

For services previously created with --new, run systemctl --user restart container-oe-podman-web.service during maintenance windows to rebuild containers with the new image using the same parameters. Ordinary podman start will not replace existing containers with a new image—actual applications should follow their upgrade instructions for backing up and handling data.

When no longer needed, if the service was managed by systemd, first remove it:

systemctl --user disable --now container-oe-podman-web.service
rm "$HOME/.config/systemd/user/container-oe-podman-web.service"
systemctl --user daemon-reload

If not configured with systemd, run podman stop oe-podman-web followed by podman rm oe-podman-web. Both methods preserve the web volume. Only remove the volume podman volume rm oe-podman-html after confirming the content is no longer needed. Only consider disabling linger when this account no longer requires any offline user services.

Verification notes: Installation method previously validated on openEuler 22.03 LTS SP4 and 24.03 LTS SP3 (x86_64/aarch64) under Rootless, SELinux Enforcing, networking, persistent volumes, and reboot scenarios. This version was re-verified on 24.03 LTS SP3 x86_64 for revised installation, default network, and user service workflows. Revision date: 2026-09-20