如果你用 Docker Desktop 跑过容器,多半遇到过这几件事:开机后进程里常驻一个 com.docker.backend,风扇转个不停;公司发了通知说 Docker Desktop 商业授权要收费;或者只是想让容器以普通用户身份跑,却发现默认还得靠一个 root 权限的守护进程。
Podman 就是为解这些痛点而生的。 它和 Docker 的命令几乎一样,但架构完全不同:没有常驻的守护进程(daemonless),默认以普通用户身份运行容器(rootless)。换句话说,你的 docker run 换个词就能跑,但底层更干净、更安全。
这篇是写给新手的从零教程。不讲太多原理八卦,重点是把"装好 → 跑起来 → 会日常用 → 能排错 → 敢从 Docker 迁过来"这条路径走通。
说明:本文命令基于 Podman v5 及以上版本(当前最新为 v6.1.3,2026-09-29 发布),主要实验环境为 Windows 11 + WSL2 Ubuntu 与原生 Linux。macOS 与 Linux 的差异会单独标注。
目录
- Podman 到底是什么
- 它和 Docker 的本质区别
- 安装:三条路线选一条
- 第一个容器:从这里开始动手
- 日常命令速查:和 Docker 逐条对照
- 镜像:构建、推送与导入导出
- 进阶重点:Pod、compose 与 systemd
- rootless 权限模型:新手最容易卡的地方
- 常见问题与排错清单
- 从 Docker 迁移:一份可直接执行的清单
- 该不该换:适用与不适用场景

一、Podman 到底是什么
一句话定义:Podman(Pod Manager)是一个管理 OCI 容器与镜像的命令行工具,能跑容器、管镜像、管卷、管 Pod,且不需要守护进程。
它的几个身份标签:
| 属性 | 说明 |
|---|---|
| 开源协议 | Apache-2.0 |
| 开发方 | Red Hat 主导,containers 组织维护 |
| 仓库热度 | GitHub 约 33,000 star、3,400 fork(podman-container-tools/podman) |
| 官网 | https://podman.io |
| 当前版本 | v6.1.3(2026-09-29);v5.x 是当前应用最广的稳定系列 |
| 图形界面 | Podman Desktop v1.29.3(CNCF Sandbox 项目) |
这里要特别点出一个容易混淆的事:Podman 是一个 CLI 工具,Podman Desktop 是它的图形前端。两者可以分开装——你可以只用命令行 Podman,也可以只用 Desktop。新手如果想少打命令,建议两个都装(见第三节)。
它由哪几个工具组成
真正动手时你会发现,Podman 的生态里其实是一组分工明确的工具:
- podman:主角,管容器的生老病死(run/stop/rm/ps/inspect…)
- buildah:专职构建镜像(
podman build底层就调它) - skopeo:不落地地搬运、检查、复制镜像,比如把镜像从 Docker Hub 直接同步到私有仓库
- crun / runc:真正的运行时,负责创建内核 namespace 和 cgroup。Podman 默认用 crun(Red Hat 自研,C 语言写的,启动更快、内存占用更低)
给新手的建议:一开始只需要 podman 一个命令就够了。buildah 和 skopeo 等你需要写 CI 脚本或做镜像搬运时再深入。
二、它和 Docker 的本质区别
这是全篇最值得花时间理解的一节。命令的相似是"表面功夫",架构的不同才是"为什么用它"的答案。
2.1 核心架构对比
Docker 的模式是"客户端 - 服务端":你敲 docker run,本质上只是给后台常驻的 dockerd 发了个请求,真正建容器的是那个 root 权限的守护进程。所有容器都挂在这一个进程底下,它是单点,也是权限放大的来源。
Podman 的模式是"直接 fork":你敲 podman run,Podman 进程自己就去创建容器了,做完就退出。没有中间商。容器直接是 Podman 的子进程,用普通用户的身份创建,天然不需要 root。
2.2 六个关键差异
| 维度 | Docker | Podman |
|---|---|---|
| 守护进程 | 必须常驻 dockerd(root) | 无守护进程,用完即退 |
| 默认权限 | 容器由 root 守护进程创建 | 默认 rootless,以当前用户身份运行 |
| 安全边界 | 攻破 dockerd ≈ 拿到 root | 攻破容器 ≈ 只拿到普通用户权限 |
| Pod 概念 | 需要 Kubernetes 才有 | 原生支持 podman pod |
| systemd 集成 | 需额外配置 | 原生 podman generate systemd / Quadlet |
| 开机自启 | 由 Docker 服务统一管理 | 由 systemd 用户服务管理,可精确到单个容器 |
| 命令兼容 | — | 高度兼容 Docker CLI,多数命令直接换名可用 |
| 授权成本 | Docker Desktop 对部分企业收费 | 完全免费开源 |
2.3 一句话记法
Docker 像"总公司 + 派工单":所有活都由总部那个大管家接单再分派。 Podman 像"个体户自己上手":你叫它干活,它自己就干完了,不用惊动任何人。
2.4 那 Docker 还有什么优势
不吹不黑,交换条件在这里:
- 生态成熟度:Docker Compose、Docker Desktop 的图形化体验、插件市场、文档量,仍然是第一梯队。Podman 的 compose 靠
podman-compose或podman compose提供,偶有兼容边角问题。 - 单机开发体验:Docker Desktop 在 macOS/Windows 上的"开箱即用"做得更顺(Podman 需要先
podman machine init)。 - Kubernetes 本地开发:kind、minikube 默认以 Docker 为主。
结论:学习/个人/安全敏感场景,Podman 很香;企业重度依赖 Docker Compose 复杂编排时,迁移要评估成本。 好消息是二者可以共存,后面第十节会给共存方案。
三、安装:三条路线选一条
Podman 在 Linux 上是"原生居民",在 Windows / macOS 上则需要一个 Linux 虚拟机(Podman Machine)。请先确认你在哪个平台,再走对应的路。
3.1 路线 A:Linux(最推荐,体验最完整)
以 Ubuntu / Debian 为例:
sudo apt update
sudo apt install -y podman
podman --version
Fedora / RHEL / CentOS Stream(原生支持最好,版本最新):
sudo dnf install -y podman
⚠️ 踩坑预警:Ubuntu 22.04 官方源里的 Podman 版本偏旧(3.4.x 级别),可能导致 Podman Desktop 连不上。若要新版本,改用官方仓库:
. /etc/os-release echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/ /" \ | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list curl -L "https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/Release.key" \ | sudo apt-key add - sudo apt update && sudo apt install -y podman buildah skopeo
Linux 上装完直接用,不需要初始化虚拟机,也没有 podman machine 这一步。
3.2 路线 B:Windows(主流做法是走 WSL2)
Windows 上跑 Podman 有两种姿势,按你的需求选:
姿势 1:装 Podman Desktop(图形界面,新手友好)
- 到 https://podman-desktop.io 下载 Windows 安装包
- 建议下载官网的
.exe安装包,而不是 Microsoft Store 的 MSIX 版本(MSIX 版本偶有权限与路径问题) - 安装时勾选 “Add to PATH”,方便后续命令行调用
- 首次启动会引导你创建一个 Podman Machine(本质是 WSL2 里的一台轻量虚拟机)
姿势 2:直接在 WSL2 的 Ubuntu 里装(推荐给开发人员)
如果你本来就有 WSL2,这是最舒服的方式——Podman 直接装在一个真正的 Linux 里,行为与服务器完全一致:
# 在 WSL2 Ubuntu 中执行
sudo apt update && sudo apt install -y podman
podman --version
💡 重要差异:WSL2 里不用
podman machine。因为你已经在 Linux 里了,Podman 直接跑在 WSL2 的内核上。podman machine是给"在 Windows 上模拟 Linux"准备的,WSL2 路线不需要。
如果你的网络环境访问 download.opensuse.org 较慢,也可以先在 WSL2 里用系统源装,够用即可。
关于 WSL2 的补充:Podman 与 Docker 在 WSL2 里可以并存。如果你之前用 Docker Desktop 的 WSL 集成,注意两者对 docker 命令的接管关系(见第十节)。
3.3 路线 C:macOS
- 通过 Homebrew 安装 CLI:
brew install podman
- 初始化虚拟机(macOS 上没有原生 Linux 内核,必须有这一步):
podman machine init --cpus 4 --memory 8192 --disk-size 50
podman machine start
- 想用图形界面就再装 Podman Desktop(https://podman-desktop.io)。
⚠️ macOS 上的资源要提前给够:默认虚拟机的 CPU / 内存偏小,跑稍重的镜像会卡。建议至少
--cpus 4 --memory 8192(8 GB),磁盘 50 GB。如果虚拟机已建好才发现不够,需要先podman machine rm再重新init,或在 Desktop 里调整机器配置。
3.4 安装后的第一步自检
不管哪条路线,装完先跑这三条命令确认环境健康:
podman --version # 版本号
podman info # 环境详情:runtime、rootless 与否、存储驱动
podman run --rm hello-world
podman info 里重点看两处:
host:
security:
rootless: true # ← Windows/macOS 下通常是 false(在虚拟机里是 root)
# Linux 下应为 true,说明 rootless 生效
如果最后一条 hello-world 能打印出提示语,说明拉镜像、建容器、跑进程、销毁容器这条完整链路已经通了。
四、第一个容器:从这里开始动手
这一节的所有命令,在 Linux / WSL2 里可以直接跑;Windows、macOS 用户请先确认 podman machine 已在运行(podman machine list)。
4.1 跑一个一次性容器
podman run --rm hello-world
拆解这四个部分,这是 Podman(也是 Docker)命令的通用结构:
| 片段 | 含义 |
|---|---|
podman | 主命令 |
run | 子命令:创建并启动一个容器 |
--rm | 选项:容器退出后自动删除 |
hello-world | 镜像名(不含 --,说明它是位置参数) |
口诀:
podman [全局选项] 子命令 [子命令选项] 镜像 [容器内执行的命令]
4.2 跑一个能交互的容器
podman run -it --rm ubuntu:24.04 bash
-i= 保持标准输入打开-t= 分配一个伪终端(pseudo-TTY)--rm= 退出后清理ubuntu:24.04= 镜像名 + 标签bash= 容器启动后要执行的命令
进去之后,你会发现自己在一个全新的 Ubuntu 里。敲 exit 退出,容器随即消失。
💡 初学者最容易犯的错:不加
-it就想进交互式 shell,结果容器瞬间退出,podman ps什么都看不到。记住:没有前台进程,容器就会退出。
4.3 后台运行 + 端口映射
podman run -d --name web -p 8080:80 nginx:alpine
| 片段 | 含义 |
|---|---|
-d | 后台运行(detached) |
--name web | 给容器起名 web,方便后续引用 |
-p 8080:80 | 宿主机 8080 端口 映射到 容器 80 端口 |
然后验证:
podman ps # 看到 web 正在运行
curl http://localhost:8080 # 应返回 nginx 欢迎页 HTML
podman logs web # 看日志
podman exec -it web sh # 进入这个正在运行的容器
⚠️
-p的冒号方向别记反:-p 宿主机端口:容器端口。-p 8080:80是"外面 8080,里面 80"。写反了会连不上。
4.4 挂载数据卷
容器一删,里面的数据就没了。要持久化,得挂卷:
# 方式一:命名卷(Podman 管理存储位置)
podman run -d --name db -v pgdata:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=secret postgres:16-alpine
# 方式二:绑定挂载(挂宿主机目录,开发时常用)
podman run -d --name web2 -v $(pwd)/html:/usr/share/nginx/html:ro -p 8081:80 nginx:alpine
-v pgdata:/path中pgdata不以/开头 → 命名卷-v $(pwd)/html:/path中左边是绝对路径 → 绑定挂载- 结尾
:ro表示只读挂载(容器改不了宿主机文件,更安全)
💡 rootless 下的挂载权限坑:绑定挂载宿主机目录时,容器里的用户 UID 与宿主机不一致,常出现"容器写不进去"的情况。这是 rootless 的正常现象,解法见第八节。
4.5 清理:容器跑多了怎么收场
podman ps -a # 看全部容器(含已停止)
podman stop web # 停
podman rm web # 删容器
podman rm -f web # 强制删(运行中也删)
podman images # 看本地镜像
podman rmi nginx:alpine # 删镜像
# 一键清理(危险,但很省事)
podman system prune -a # 删除所有未使用的容器、镜像、网络、卷
podman system prune -af --volumes # 连未使用的卷一起删(数据会丢!)
⚠️
podman system prune -a会删掉所有没在跑的镜像,下次用要重新拉。别在赶时间的时候手滑。带--volumes更要小心,卷里可能是数据库数据。
五、日常命令速查:和 Docker 逐条对照
这是本文最实用的一张表。Podman 对 Docker CLI 的兼容度非常高,绝大多数场景就是把 docker 换成 podman。
| 用途 | Docker | Podman | 是否直接换名可用 |
|---|---|---|---|
| 查看版本 | docker version | podman version | ✅ |
| 环境信息 | docker info | podman info | ✅ |
| 拉镜像 | docker pull nginx | podman pull nginx | ✅ |
| 看镜像 | docker images | podman images | ✅ |
| 跑容器 | docker run -d -p 80:80 nginx | podman run -d -p 80:80 nginx | ✅ |
| 看运行中容器 | docker ps | podman ps | ✅ |
| 看全部容器 | docker ps -a | podman ps -a | ✅ |
| 进容器 | docker exec -it web sh | podman exec -it web sh | ✅ |
| 看日志 | docker logs -f web | podman logs -f web | ✅ |
| 停 / 启 / 重启 | docker stop/start/restart | podman stop/start/restart | ✅ |
| 删容器 | docker rm -f web | podman rm -f web | ✅ |
| 删镜像 | docker rmi nginx | podman rmi nginx | ✅ |
| 构建镜像 | docker build -t x . | podman build -t x . | ✅ |
| 打标签 | docker tag | podman tag | ✅ |
| 推/拉仓库 | docker push/pull | podman push/pull | ✅ |
| 镜像导入导出 | docker save/load | podman save/load | ✅ |
| 看卷 | docker volume ls | podman volume ls | ✅ |
| 看网络 | docker network ls | podman network ls | ✅ |
| 看详情 | docker inspect | podman inspect | ✅ |
| 编排 | docker compose up | podman compose up(依赖外部) | ⚠️ 见第七节 |
| Pod 概念 | 无(靠 K8s) | podman pod create/ps | ❌ 独有功能 |
| systemd 集成 | 手工配置 | podman generate systemd / Quadlet | ❌ 独有功能 |
| 虚拟机管理 | Docker Desktop 内部 | podman machine init/start/stop | ❌ 独有功能 |
5.1 更省事的做法:直接用 docker 命令
Podman 提供了一个兼容包 podman-docker,装好后会创建一个指向 podman 的 docker 别名:
# Fedora / RHEL
sudo dnf install -y podman-docker
# Ubuntu / Debian
sudo apt install -y podman-docker
装上之后,你现有的 docker ... 脚本不用改一个字符:
docker run -d --name web -p 8080:80 nginx # 实际由 podman 执行
⚠️ 重要澄清:
podman-docker只是符号链接/包装脚本,它不会启动 Docker 守护进程。有人以为装了它就"有了 Docker",是误解。另外,如果你同时装了 Docker Desktop,注意两者对/usr/bin/docker的争夺——先装的赢。
5.2 高频组合命令备忘
# 停止并删除所有容器
podman stop $(podman ps -q) && podman rm $(podman ps -aq)
# 删除所有 <none> 悬空镜像
podman image prune -f
# 看容器资源占用(类似 top)
podman stats
# 看容器端口映射
podman port web
# 复制文件进出容器
podman cp ./config.yml web:/etc/app/config.yml
podman cp web:/var/log/app.log ./
# 导出容器为新镜像(把改动固化下来)
podman commit web my-web:v1
给新手的建议:先把 run / ps / logs / exec / stop / rm / images / rmi 这 8 个命令练熟,日常 90% 的操作就够了。
六、镜像:构建、推送与导入导出
6.1 写第一个 Containerfile
Podman 用 Containerfile(和 Dockerfile 完全同格式,只是名字更中性):
# Containerfile
FROM docker.io/library/python:3.13-slim
WORKDIR /app
# 先拷依赖清单,利用层缓存
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 再拷代码
COPY . .
EXPOSE 8000
CMD ["python", "-m", "http.server", "8000"]
构建:
podman build -t myapp:v1 .
💡 几点新手要点:
.containerignore与.dockerignore作用一致,用来排除不需要进镜像的文件(如.git、node_modules),能显著减小体积、加快构建- 层缓存:把变化少的指令(装依赖)放前面,变化多的(拷代码)放后面
- 镜像名规范:
podman build时不写仓库前缀,默认是本地镜像;要推送必须带注册表前缀
6.2 完整镜像名长什么样
| 片段 | 名称 | 说明 |
|---|---|---|
docker.io | 注册表(registry) | 不写默认 Docker Hub |
/library | 命名空间 | 官方镜像在 library 下,不写默认 |
nginx | 仓库名(repository) | 镜像名 |
:alpine | 标签(tag) | 不写默认 latest |
所以 nginx:alpine 的完整形式是 docker.io/library/nginx:alpine。 理解这一点,后面配私有仓库就不会懵。
⚠️ 生产环境别用
latest:它会随上游更新而变化,今天跑通的版本明天可能挂。永远锁具体标签(如nginx:1.27-alpine),或者直接用 digest(nginx@sha256:...)。
6.3 推送到 Docker Hub 或私有仓库
# 登录
podman login docker.io
# 或私有仓库
podman login registry.example.com -u myuser
# 打标签(必须带注册表前缀)
podman tag myapp:v1 docker.io/myusername/myapp:v1
# 推送
podman push docker.io/myusername/myapp:v1
💡 登录凭据存在哪:默认在
${XDG_RUNTIME_DIR}/containers/auth.json,比 Docker 的明文~/.docker/config.json更安全。可以配podman login --get-login查看当前登录用户。
6.4 镜像搬运三件套(Podman 的独门优势)
场景:把镜像从 A 仓库同步到 B 仓库,中间不想落地、不想跑容器。
# 用 skopeo:直接从源仓库复制到目标仓库
skopeo copy docker://docker.io/library/nginx:alpine \
docker://registry.example.com/mirror/nginx:alpine
# 只看远程镜像信息,不下载镜像本体
skopeo inspect docker://docker.io/library/nginx:alpine
# 列出远程仓库所有标签
skopeo list-tags docker://docker.io/library/nginx
场景:离线环境传输。
# 有网机器:导出成 tar
podman save -o nginx.tar docker.io/library/nginx:alpine
# 离线机器:导入
podman load -i nginx.tar
# 也可以用 skopeo 直接导出为归档
skopeo copy docker://docker.io/library/nginx:alpine \
docker-archive:nginx.tar:nginx:alpine
这就是 buildah + skopeo 的价值:它们把"构建"和"搬运"从"运行"里拆了出来,做 CI 时特别顺手。
七、进阶重点:Pod、compose 与 systemd
这一节是 Podman 真正区别于 Docker 的地方。如果只学一个进阶功能,我建议学 Pod。
7.1 Pod:Podman 的特色功能
Pod 是一组共享网络与命名空间的容器集合。 这个概念来自 Kubernetes——同一个 Pod 里的容器像"住在同一间房":共享 IP、共享端口空间、可以用 localhost 互相访问。
# 1. 创建一个 Pod,把 8080 端口暴露在 Pod 层面
podman pod create --name mypod -p 8080:80
# 2. 往 Pod 里塞两个容器(注意:端口已由 pod 占用,容器内不再写 -p)
podman run -d --pod mypod --name web nginx:alpine
podman run -d --pod mypod --name logger alpine sh -c 'while true; do echo hi; sleep 5; done'
# 3. 看 Pod 及其内部的容器
podman pod ps
podman ps --pod
# 4. 在 Pod 内一个容器里访问另一个
podman exec web curl -s http://localhost:80 | head -3
为什么有用:这正是 Kubernetes 里 Pod 的模型。你在本地用 podman pod 练会了,上 K8s 时概念是通的。而且 podman generate kube 能直接把 Pod 导出成 K8s YAML:
podman generate kube mypod > mypod.yaml # 导出为 Kubernetes 清单
podman play kube mypod.yaml # 从 K8s 清单直接拉起
这是 Podman 最漂亮的用法之一:本地开发 → 一键生成 K8s 清单 → 直接上集群。
7.2 compose:怎么用 docker-compose.yml
Podman 本身不内置 compose,但有两条路:
方式一:podman-compose(轻量,独立实现)
pip install podman-compose
# 或
sudo apt install -y podman-compose
podman-compose up -d
podman-compose down
podman-compose ps
它读取同样的 docker-compose.yml / compose.yaml,把每个 service 翻译成 podman run。
方式二:podman compose(推荐,作为 docker-compose 的前端)
新版本 Podman 提供了 podman compose 子命令,它会调用系统上已安装的 compose provider(docker-compose 或 podman-compose),兼容性更好:
podman compose up -d
podman compose down
Podman Desktop 里还支持一键把 compose 项目转成 Pod,图形化点几下就行。
⚠️ 兼容性提醒:
podman-compose对复杂 compose 特性(depends_on的健康检查条件、某些deploy段、profiles)支持不如 Docker Compose 完整。如果你的 compose 文件很复杂,建议直接用podman compose走 docker-compose provider,或干脆评估是否留用 Docker。
7.3 systemd 集成:让容器开机自启
这是 Podman 比 Docker 更优雅的地方——把容器变成真正的 systemd 服务。
方式一:Quadlet(Podman 4.4+ 起推荐,Podman 5+ 是主流做法)
在 ~/.config/containers/systemd/web.container 写声明式配置:
[Unit]
Description=Nginx Web Container
[Container]
Image=docker.io/library/nginx:alpine
PublishPort=8080:80
Volume=%h/html:/usr/share/nginx/html:ro
[Service]
Restart=always
[Install]
WantedBy=default.target
然后:
systemctl --user daemon-reload
systemctl --user start web.service
systemctl --user enable web.service # 开机自启
systemctl --user status web.service
💡 命名规则很关键:
.container文件生成的服务名 = 文件名 +.service(web.container→web.service),不要自己去enable一个不存在的同名服务。💡 还想让用户退出登录后容器继续跑(服务器场景):
sudo loginctl enable-linger $USER
方式二:podman generate systemd(旧版方式,仍可用)
podman generate systemd --new --name web --files
# 会把 web.service 生成到当前目录,拷到 ~/.config/systemd/user/ 后 daemon-reload 即可
⚠️
podman generate systemd在新版本中已被标记为 deprecated,新项目请优先用 Quadlet。
方式三:Linux 系统级服务(root 运行)
如果确实需要 root 容器(如监听 80 端口),可以 sudo podman ... + 放 /etc/systemd/system/。但优先考虑 rootless + 端口转发,安全性更好。
八、rootless 权限模型:新手最容易卡的地方
这一节专门给被"权限不够"卡住的人。
8.1 rootless 是怎么做到的
Linux 内核从 3.8 起提供 user namespace(用户命名空间)。Podman 利用它做了一层 UID 映射:
你在容器里是 root(UID 0),但在宿主机上实际只是你的普通用户(UID 1000)。 就算容器里把 root 玩坏了,攻击者拿到的也只是你这份普通权限,碰不到系统。
这个映射区间记录在 /etc/subuid 和 /etc/subgid 里:
grep $USER /etc/subuid /etc/subgid
# 应输出类似:youruser:100000:65536
如果这里是空的,rootless 会出问题。 修复:
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
podman system migrate # 重建存储
8.2 三个高频权限坑
坑 1:绑定挂载后容器写不进去
现象:
podman run -v $(pwd)/data:/data alpine touch /data/test.txt
# touch: /data/test.txt: Permission denied
原因:宿主机目录属于 UID 1000,但容器里你是 UID 0,映射后实际身份对不上宿主机文件属主。
三种解法(按推荐度排):
# 解法一(推荐):让容器以宿主机 UID 运行
podman run --userns=keep-id -v $(pwd)/data:/data alpine touch /data/test.txt
# 解法二:指定容器内用户与你的 UID 一致
podman run --user $(id -u):$(id -g) -v $(pwd)/data:/data alpine touch /data/test.txt
# 解法三:只读挂载(不需要写就用这个)
podman run -v $(pwd)/data:/data:ro alpine ls /data
💡
--userns=keep-id是 rootless 场景下的万能解:它让容器里的 UID 直接等于你在宿主机的 UID,文件权限就对上了。
坑 2:想监听 1024 以下端口(如 80、443)
rootless 默认不允许绑定特权端口。
# 方案一:宿主端口用高位(最常用)
podman run -d -p 8080:80 nginx:alpine # 宿主 8080 转发到容器 80
# 方案二:放行低位端口(一次性配置)
sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80
# 永久生效:写入 /etc/sysctl.d/
# 方案三:用 systemd socket 激活或反向代理(生产更推荐)
坑 3:Podman Desktop 连不上 Podman
在 Windows 上,Podman Desktop 通过 socket 与 Podman 通信,依赖 dbus;dbus 不可用时会连接失败。
排查顺序:
# 1. 确认 machine 在跑
podman machine list
# 2. 看 socket 是否存在
podman info --format '{{.Host.RemoteSocket.Path}}'
# 3. 重启 machine 与 Desktop
podman machine stop && podman machine start
如果仍然连不上,优先检查是不是走 WSL2 路线——WSL2 里的 Podman 与外层 Desktop 的对接最容易出问题。最简单的绕法是:直接在 WSL2 里用命令行 Podman,不依赖 Desktop 的 GUI。
九、常见问题与排错清单
下面这张表是新手最可能撞上的问题,建议自查时直接对照。
| 现象 | 可能原因 | 解法 |
|---|---|---|
podman ps 什么都没有 | 容器无前台进程已退出 | podman ps -a 看状态;交互式要加 -it,长驻进程要加 -d |
Cannot connect to Podman socket | machine 未启动 / socket 路径错 | podman machine start;podman info 核对 socket |
Error: image not known | 镜像名或标签拼错 / 未 pull | 先 podman pull,或用完整名 docker.io/library/nginx:alpine |
Permission denied 挂载目录 | rootless UID 不匹配 | 加 --userns=keep-id 或 --user $(id -u):$(id -g) |
| 端口映射不生效 | -p 写反 / 防火墙 / 容器内未监听 | 确认 -p 宿主:容器;podman port <容器> 核对 |
| 拉镜像超时 / TLS 错误 | 网络 / 代理 / 证书吊销检查 | 配 ~/.config/containers/registries.conf 镜像加速;必要时设 SSL_CERT_FILE |
| 磁盘被撑满 | 悬空镜像 + 停止的容器堆积 | podman system df 看占用;podman system prune -a 清理 |
subuid/subgid 相关报错 | /etc/subuid 未配置 | usermod --add-subuids/--add-subgids + podman system migrate |
| compose 启动失败 | podman-compose 兼容性不足 | 改用 podman compose(走 docker-compose provider) |
| 容器时间/时区不对 | 未挂载时区 | -v /etc/localtime:/etc/localtime:ro -e TZ=Asia/Shanghai |
| 中文乱码 | 容器内缺 locale | -e LANG=C.UTF-8 |
9.1 三个必备诊断命令
# 1. 看环境全貌(runtime、rootless、存储驱动、socket)
podman info
# 2. 看某容器为什么起不来(重点看 State 与 LogPath)
podman inspect mycontainer | head -50
podman logs mycontainer
# 3. 看磁盘占用明细
podman system df
9.2 调试容器的正确姿势
# 不进容器、只覆盖入口命令,看看环境长什么样
podman run --rm -it --entrypoint sh nginx:alpine
# 注意:基础镜像如果没有 sh(如 scratch),这条会失败
# 保留退出后的容器,方便事后 inspect
podman run --name debug1 nginx:alpine # 不加 --rm
# 看容器退出码与原因
podman inspect debug1 --format '{{.State.ExitCode}} / {{.State.Error}}'
# 实时资源占用
podman stats --no-stream
💡 一条通用排错思路:先把
-d去掉、把--rm去掉、把入口换成sh,让容器"停在那里"让你看。这比盯着报错猜快得多。
十、从 Docker 迁移:一份可直接执行的清单
如果你决定从 Docker 转 Podman,不用一次全换,按这个顺序走最稳。
10.1 迁移前的心理准备
| 项目 | 迁移难度 | 说明 |
|---|---|---|
日常 docker run/ps/logs/exec | ⭐ 极低 | 换名即用 |
| Dockerfile / Containerfile | ⭐ 极低 | 同格式 |
docker-compose.yml | ⭐⭐⭐ 中 | 简单文件没问题,复杂编排要测 |
| 数据卷与镜像 | ⭐⭐ 低 | 可 save/load 或重新拉取 |
| CI/CD 脚本 | ⭐⭐ 低 | 换命令名即可,或装 podman-docker |
| Kubernetes 本地开发 | ⭐⭐⭐⭐ 较高 | 依赖 kind/minikube 的链路要单独评估 |
10.2 五步迁移法
第 1 步:共存,不拆 Docker
先把 Podman 装上,让两者并存。 不要一上来就卸 Docker,否则出问题没法回退。
# Linux 上装 Podman(前面第三节的命令)
sudo apt install -y podman
podman run --rm hello-world # 确认独立可用
第 2 步:把现有镜像与卷搬过来
# 导出 Docker 镜像列表并逐个搬运
docker images --format '{{.Repository}}:{{.Tag}}' | grep -v '<none>' > images.txt
while read img; do
podman pull "docker.io/$img" || echo "FAILED: $img"
done < images.txt
卷的数据建议用应用层方式迁移(如 pg_dump)而不是直接拷目录,更可靠。
第 3 步:批量验证日常命令
把常用脚本里的 docker 换成 podman,跑一遍。新版本 Podman 提供了自动检测命令:
podman-compose --help # 若用 compose
podman system check # 检查存储健康
第 4 步:决定 compose 路线
# 试跑现有 compose 文件
podman compose up -d
podman compose ps
# 若报错,逐项比对不支持的特性
第 5 步:迁移 systemd 服务与清理
# 把需要开机自启的容器改成 Quadlet
mkdir -p ~/.config/containers/systemd
# 写 web.container(见 7.3)
systemctl --user daemon-reload
systemctl --user enable --now web.service
sudo loginctl enable-linger $USER
# 确认新服务稳定运行一周后,再考虑卸载 Docker Desktop
10.3 兼容层:让 docker 命令指向 Podman
# Linux
sudo apt install -y podman-docker # 或 dnf install podman-docker
docker --version # 会显示 podman 的版本
⚠️ 注意:装
podman-docker前先卸掉 Docker 的 docker CLI,否则会造成命令冲突(谁后装谁被覆盖,或直接报错)。这一步放在迁移的最后,且确认不再需要 Docker 之后再做。
10.4 别名兜底:最温和的过渡
不想动系统配置,可以在 ~/.bashrc / ~/.zshrc 里加别名:
alias docker=podman
alias docker-compose='podman compose'
这样你的所有习惯都能保留,行为却已经是 Podman。 成本最低,随时 unalias 回退。
十一、该不该换:适用与不适用场景
最后给一个决策参考,避免为了换而换。
11.1 强烈推荐用 Podman 的场景
具体来说,这些场景特别合适:
- 安全敏感的环境:CI/CD 流水线、多租户开发机、给实习生开放的环境
- 企业授权受限:不想为 Docker Desktop 商业授权付费
- 个人开发与学习:完全免费,且能顺便理解 K8s 的 Pod 模型
- 服务器上的常驻服务:systemd 集成比 Docker 更原生
- 本地 K8s 开发:
podman generate kube这条捷径非常省事 - 高性能计算 / 共享集群:rootless 无需管理员权限
11.2 建议暂时留用 Docker 的场景
- 重度依赖 Docker Compose 全部特性(复杂
depends_on健康检查、profiles、deploy段) - 本地 K8s 工具链深度绑定 Docker(kind、minikube、部分 IDE 插件)
- 团队协作规范已围绕 Docker 建立,迁移沟通成本高于收益
- 依赖 Docker Desktop 独有功能(如某些扩展、内置 K8s 一键集群)
11.3 一个务实的结论
不用二选一。 Podman 和 Docker 可以在同一台机器上共存——新项目用 Podman,老项目继续用 Docker,慢慢迁。
对新手来说,最佳路径是:先装 Podman(或已在用的 WSL2 里装一个),用
hello-world、nginx、postgres三个镜像把run / ps / logs / exec / stop / rm练熟。当你发现"我敲的命令和 Docker 一模一样,但少了一个常驻进程、还不用 root"的时候,这次迁移就已经值了。
附:速查卡
把下面这段存成 ~/.bashrc 的备忘,或抄在便签上:
# ===== Podman 新手速查 =====
podman run --rm hello-world # 冒烟测试
podman run -it --rm ubuntu:24.04 bash # 交互式
podman run -d --name web -p 8080:80 nginx:alpine # 后台 + 端口
podman run -d --name db -v pgdata:/var/lib/postgresql/data -e POSTGRES_PASSWORD=x postgres:16
podman run --userns=keep-id -v $(pwd)/data:/data alpine # 解决挂载权限
podman ps -a # 全部容器
podman logs -f web # 跟踪日志
podman exec -it web sh # 进容器
podman stats # 资源占用
podman port web # 端口映射
podman stop web && podman rm web # 停 + 删
podman images # 镜像列表
podman rmi nginx:alpine # 删镜像
podman system df # 磁盘占用
podman system prune -a # 大扫除(谨慎)
podman pod create --name mypod -p 8080:80 # 创建 Pod
podman generate kube mypod > mypod.yaml # 导出 K8s 清单
podman machine list / start / stop # Windows/macOS 虚拟机
podman info # 环境诊断
官方资源
| 资源 | 地址 |
|---|---|
| 项目官网 | https://podman.io |
| GitHub 仓库 | https://github.com/containers/podman |
| 官方文档 | https://docs.podman.io |
| Podman Desktop | https://podman-desktop.io |
| 镜像仓库 | https://hub.docker.com(Podman 兼容 Docker Hub) |
版本提示:本文示例基于 Podman v5/v6 语法。
podman generate systemd已在 v5 起标记为 deprecated,新项目请用 Quadlet;podman compose需要系统上装有docker-compose或podman-compose作为 provider。不同发行版打包版本差异较大(Ubuntu 官方源常落后),涉及 Desktop 联调时请优先安装较新版本。