最近把开发环境里的 Docker Desktop 换成了 Podman,在 Windows 上折腾了大概一周,过程中踩了无数个坑,也把podman machine、WSL2、Compose 的配合方式全部摸了一遍。这篇就当个人使用记录整理出来,给同样想在 Windows 上跑 Podman 的朋友一个参考。Podman 对 Docker CLI 的兼容性很好,绝大多数docker命令直接换成podman就能用,真正的难点反而不是命令本身,而是 Windows 下面的 WSL2、端口转发、文件挂载这些环境问题。
如果你和我一样,日常主要用 Docker Compose 做本地开发,又不想继续被 Docker Desktop 的后台进程和资源占用困扰,这篇文章会很有价值。下面我先从为什么换、怎么装、怎么用,到最后踩坑实录,一条线完整讲清楚。
1. 为什么我在 Windows 上换掉 Docker Desktop
1.1 导火索:许可证、资源和“常驻进程”
Docker Desktop 对大型企业商用是收费的,个人和小团队虽然可以免费使用,但不管免费还是付费,它本质上是一个带 GUI 的桌面应用,启动之后常驻后台,还要维护自己的虚拟机和网络栈。我的笔记本只有 16GB 内存,平时开浏览器、IDE、聊天工具再加上 Docker Desktop,内存动不动就见底。
换到 Podman 之后最直观的感受是:没有一个需要常驻的 daemon 进程了。Podman 采用无守护进程架构,CLI 直接采用类似fork/exec的方式去创建容器,不再需要先跟一个后台服务打招呼。在 Linux 上这个优势很明显,在 Windows 上虽然受 WSL2 或虚拟机托管限制,资源占用仍然比 Docker Desktop 轻不少。
1.2 Podman 的架构和我理解的取舍
很多人以为 Podman 只是“换了个命令前缀”,其实架构差别不小。Docker CLI 默认连接的是docker daemon,所有容器操作都要经过这个守护进程;Podman 则是 CLI 直接通过 OCI runtime 启动容器,用户态的管理逻辑更接近普通命令。
但在 Windows 上有一层绕不开的限制:容器技术依赖 Linux 内核特性,Windows 本身不是直接跑 Linux 容器的地方。所以 Windows 版 Podman 仍然需要一个 Linux 环境,通常就是 WSL2。你可以用podman machine自动创建并管理一个 Linux 虚拟机,也可以直接在 WSL2 发行版里安装 Linux 版 Podman。
理解了这一点,后面很多所谓“坑”就顺理成章了:不是 Podman 本身难用,而是多了一层 Windows 与 Linux 之间的桥接,端口、文件系统、权限都要跨边界流动。
1.3 这篇记录适合谁
如果你属于下面任意一种情况,这篇记录的参考价值会比较大:
- 平时用 Docker Compose 做本地开发,容器数量不多,不想再跑 Docker Desktop 这种重客户端。
- 已经在使用 WSL2 做开发,想直接在 WSL 里跑容器,减少桌面应用对系统资源的占用。
- 已经跑过一两个 Podman 命令,但对
podman machine和 WSL2 的关系还没完全搞清楚。 - 在迁移过程中碰到了端口占用、文件挂载权限、Compose 不兼容等问题。
如果你的容器环境还需要 Kubernetes 集群完整集成、复杂网络策略管理,Podman 在 Windows 上的体验暂时还比不上专门的容器管理平台,这篇文章对你帮助有限。
2. Windows 上 Podman 的两种部署方式
2.1 方式A:podman machine 自动管理虚拟机
安装 Windows 版 Podman 后,最标准的用法就是使用podman machine。它会自动创建一个虚拟机(当前版本默认基于 WSL2),专门用来跑容器。看到“machine”别紧张,这就是一个替你管理 Linux 后端的命令。
初始化:
podman --version podman machine init --cpus 4 --memory 4096 --disk-size 40 podman machine start podman machine list这几个参数解释一下:
--cpus 4是给虚拟机 4 个 CPU,按自己电脑核数调整,别超过物理核数。--memory 4096是给 4GB 内存,本地开发一般够用。--disk-size 40是虚拟机磁盘上限 40GB,注意不是说立刻占满,而是允许增长到 40GB。
启动后执行podman info能看到当前连接信息。如果一切正常,后面用podman pull、podman run就是常规操作了。
我个人对podman machine的感受是:方便是真方便,问题也真不少。默认的跨文件系统挂载性能一般,而且如果之前装过 WSL2,它还会生成一个单独的 WSL 发行版用于支持 machine,有时候跟现有发行版抢资源。
2.2 方式B:直接在 WSL2 发行版里装 Podman
如果你已经在 WSL2 里配置好了开发环境,我更推荐这种方式:绕开podman machine,直接在 WSL 的 Linux 发行版里安装 Podman。
以 Ubuntu 为例:
sudo apt update sudo apt install -y podman podman --version这个方式下,Podman 是 Linux 原生跑在 WSL2 里的,所有命令和 Linux 服务器上的 Podman 完全一致。文件系统在首发版里,而不是通过 Windows 和 WSL 之间的 9P 协议转发,性能损失小很多。端口转发也由 WSL2 自动实现,Windows 主机访问localhost:8080通常直接就能通。
这个方案的缺点也很明显:你得习惯先进 WSL 再操作容器;如果你同时装了多个发行版,就要注意容器环境在哪个发行版里跑,别搞混了。
2.3 两者对比和选型建议
我用一张表把两种方式的关键差异列出来,方便按场景选:
| 对比项 | podman machine | WSL2 内安装 Podman |
|---|---|---|
| 安装复杂度 | 安装 Windows 包后 init/start 即可 | 需先装 WSL2 + Ubuntu 再 apt 安装 |
| 入口位置 | 直接在 Windows 命令行操作 | 需要进入 WSL 终端操作 |
| 资源占用 | 有独立虚拟机开销 | 复用 WSL 发行版,更轻 |
| 文件挂载 | 默认支持用户目录,但性能一般 | 在 Linux 文件系统内性能最好 |
| 端口转发 | 由 machine 处理,偶发 127.0.0.1 不通 | WSL2 自动转发,基本无感 |
| 与 Docker Compose 兼容 | 保持一致 | 保持一致 |
| 适合场景 | 想快速体验、不想深究 WSL | 已有 WSL 开发环境、长期使用 |
我最后选的是“WSL2 + Ubuntu 内装 Podman”。原因很简单:我平时开发已经离不开 WSL,所有项目代码都在 Ubuntu 的 home 目录里,直接装原生 Podman 是最贴近 Linux 生产环境的方案。遇到问题查文档,也基本是 Linux 生态的答案,能少绕弯路。
3. 从拉取镜像到跑起容器:核心命令实操
3.1 Docker 命令映射表
迁移到 Podman 最大的红利就是命令几乎不用改。下面这张表是我常用的对照,够覆盖绝大多数日常操作:
| Docker 命令 | Podman 命令 | 作用 |
|---|---|---|
| docker pull nginx | podman pull nginx | 拉取镜像 |
| docker run -d -p 8080:80 nginx | podman run -d -p 8080:80 nginx | 运行容器 |
| docker ps | podman ps | 查看运行中容器 |
| docker images | podman images | 查看镜像列表 |
| docker exec -it 容器名 bash | podman exec -it 容器名 bash | 进入容器终端 |
| docker logs -f 容器名 | podman logs -f 容器名 | 跟踪容器日志 |
| docker stop 容器名 | podman stop 容器名 | 停止容器 |
| docker system prune | podman system prune | 清理无用资源 |
唯一要适应的是:很多 Docker 文档里出现的是docker-compose,对应到 Podman 则是podman compose,后面第 4 节专门讲。
3.2 拉取镜像并跑一个 Nginx
拿最经典的 Nginx 练手。先拉取镜像:
podman pull docker.io/library/nginx:alpine因为 Podman 支持 Docker Hub registry,所以镜像名跟 Docker 完全一样。拉完后查看:
podman images然后运行容器:
podman run -d --name web -p 8080:80 nginx:alpine podman ps-d表示后台运行,--name web给容器命名,-p 8080:80表示把容器的 80 端口映射到本机 8080 端口。如果一切正常,在浏览器访问http://localhost:8080就能看到 Nginx 欢迎页。
如果想看日志或者进入容器内部排查,用:
podman logs -f web podman exec -it web shNginx 的 alpine 镜像没有 bash,只有 sh,所以这里用sh。这是新手常踩的坑,换成 CentOS 这种镜像才有bash。
3.3 端口占用排查:Windows/WSL 两侧
如果你端口映射时报错bind: address already in use,说明有别的进程占用了 8080 端口。这个坑在 Windows 上特别常见,因为 Windows 和 WSL2 共用 localhost,两边都可能占用同一端口。
在 Windows PowerShell 里查占用:
netstat -ano | findstr :8080输出结果里会有一个 PID,再查看这个 PID 对应的进程:
tasklist | findstr "12345"确认是没用的进程后,再结束它:
taskkill /PID 12345 /F如果在 WSL 里发现端口占用,用:
ss -tlnp | grep 8080能直接看到进程名和 PID,再用kill -9 PID处理。
这里提醒一句:动手杀进程之前,一定要确认它不是系统服务或别的正在用的程序。我遇到过几次把 Redis 的端口进程误杀,导致本地缓存全断的尴尬情况。
3.4 目录挂载与数据卷
跑容器时经常需要把本机目录挂进容器,比如把项目目录挂到 Nginx 的静态目录:
podman run -d --name web -v ~/web:/usr/share/nginx/html -p 8080:80 nginx:alpine在 Windows 下如果直接用绝对路径挂载,要注意路径里的冒号。PowerShell 里C:\Users\me\web会被解析出问题,建议给整个-v参数加引号:
podman run -d --name web -v "C:\Users\me\web:/usr/share/nginx/html" -p 8080:80 nginx:alpine我实测过,挂 Windows 目录进容器可用,但性能比较一般。频繁读写文件时会明显慢,尤其是前端构建或者大量日志输出。更好的做法是:代码放到 WSL2 的 Linux 文件系统里,再挂载 Linux 目录进容器。
如果想隔离数据,用数据卷比 bind mount 更省心:
podman volume create html_vol podman run -d --name web -v html_vol:/usr/share/nginx/html -p 8080:80 nginx:alpine数据卷由 Podman 管理生命周期,以后删容器不会直接丢数据,适合数据库、缓存这类持久化场景。
4. Compose 迁移:把 Docker Compose 项目搬到 Podman
4.1 先装一个 compose 提供者
Podman 里执行podman compose需要一个外部的 compose provider,常见的有两个选择:
podman-compose:Python 实现,安装简单,兼容性日常够用。docker-compose:原版 Compose,Podman 也能调用它做 provider。
如果你在 WSL 的 Ubuntu 里,推荐直接装podman-compose:
sudo apt install -y podman-compose如果更习惯原版 Compose,也可以装 docker-compose v2,原理上都是 Podman 调用外部命令解析 Compose 文件。装好之后验证一下:
podman compose version看到版本输出就说明 provider 已经就绪。
4.2 一个最小可用的 compose 示例
我在迁移本地项目时,最常用的 Compose 文件长这样:
services: web: image: docker.io/library/nginx:alpine ports: - "8080:80" volumes: - ./html:/usr/share/nginx/html restart: unless-stopped redis: image: docker.io/library/redis:7-alpine ports: - "6379:6379"在项目目录下执行:
podman compose up -d它会按照 Compose 文件里的定义拉取镜像、创建网络、启动服务。用podman ps看状态:
NAME IMAGE PORTS STATUS backend_web_1 nginx:alpine 0.0.0.0:8080->80/tcp Up backend_redis_1 redis:7-alpine 0.0.0.0:6379->6379/tcp Uppodman compose down可以停止并删除这些容器,但自定义的 volume 默认保留,避免了误删数据。
4.3 迁移时要注意的几个差异
我踩过几个 Compose 迁移的坑,写出来供参考:
- Compose 文件里的
version字段现在可以不写,写了反而可能触发解析警告,建议新文件直接省略。 network_mode: host在 macOS 和 Windows 上的表现本来就跟 Linux 不一样,Podman 也一样,本地开发尽量用端口映射。depends_on的解析顺序,Podman compose 基本支持,但如果你用了condition: service_healthy,需要确认健康检查命令在镜像里存在。environment和env_file都能正常用,但注意环境变量里的特殊字符是否被 shell 转义,尤其是密码里的$。
整体体验下来,正常前后端项目从 Docker Compose 迁移到 Podman compose,改动量很小。大部分时候只要把docker-compose up -d换成podman compose up -d就能跑起来。
5. Windows 下的坑与排查实录
5.1 WSL2 初始化和虚拟化问题
执行podman machine init时偶尔会遇到类似“WSL2 is not installed”或者提示升级 WSL 内核的错误。这种情况多半不是 Podman 的问题,而是 WSL2 环境没就绪。
先检查:
wsl --status wsl -l -v如果没有发行版,执行:
wsl --install wsl --set-default-version 2也可以wsl --update强制更新 WSL 内核。
虚拟化没开也会导致 WSL2 无法启动。在 Windows 上按Win + R输入msinfo32,看“固件中已启用虚拟化”这一项是不是是。如果是否,需要进 BIOS 开启 Intel VT-x 或 AMD SVM。
另外,WSL2 内存占用的问题很常见。Windows 默认会拿一半物理内存给 WSL,容易卡顿。在用户目录下创建一个.wslconfig:
[wsl2] memory=4GB processors=4 swap=2GB再执行wsl --shutdown后重新进入,内存控制会明显好很多。
5.2 Rootless 权限和文件共享的坑
Podman 默认以 rootless 模式运行,好处是更安全,但也带来了权限边界问题。在 WSL2 里直接用普通用户跑 Podman,偶尔会遇到 fuse-overlayfs 创建失败,或者容器内读写权限不对。
如果遇到类似Error: statfs或者permission denied,可以先检查:
cat /proc/sys/user/max_user_namespaces如果结果是 0,说明用户命名空间被封了,旧版 WSL 内核比较常见。临时处理:
sudo sysctl -w kernel.unprivileged_userns_clone=1更省事的方案是直接给 WSL 发行版加上 systemd 支持,让 Podman 的用户态管理更稳定。新版 WSL 已经在/etc/wsl.conf里支持 systemd:
[boot] systemd=true改完后执行wsl --shutdown再重新打开。
文件共享方面,千万别在/mnt/c这种 Windows 挂载目录下创建容器项目。NTFS 挂载到 WSL 之后权限和 inode 表现很怪,容易触发容器内权限错乱。把项目放在 WSL 的 home 目录下,比如/home/你的用户名/project,一切都正常。
5.3 和 Docker Desktop 共存的注意事项
如果你之前装过 Docker Desktop,后来又想试试 Podman,两个环境其实可以并存,但要注意别同时运行。因为 Docker Desktop 和podman machine都可能占用 WSL2 的资源,同时开着会抢端口、抢内存。
我的习惯是:平时只跑 Podman,需要用回 Docker Desktop 时,先执行:
podman machine stop或者直接退出 Podman Desktop(如果你也用 GUI 的话),再启动 Docker Desktop。通过DOCKER_HOST环境变量切换连接目标的方式理论上可以,但日常开发没必要给自己增加复杂度。
如果要彻底卸载 Docker Desktop,除了删应用,还要记得检查 WSL 里残留的docker-desktop发行版。可以用wsl --unregister docker-desktop清理,避免占着磁盘空间。
5.4 常见问题速查表
我把自己遇到过的典型问题整理成表,方便你快速定位:
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
podman machine init报 WSL2 缺失 | WSL 未安装或版本太低 | wsl --update,确认默认版本为 2 |
容器启动后localhost:8080无响应 | 端口映射失败或端口占用 | 用netstat -ano查主机端口,用ss -tlnp查 WSL 端口 |
| 挂载 Windows 目录后写入很慢 | 跨文件系统访问,9P 协议性能瓶颈 | 项目移到 WSL 内部,或改用数据卷 |
进入容器提示executable file not found | 镜像中没有这个 shell | 换/bin/sh或安装完整 bash 版本的镜像 |
podman compose up提示找不到命令 | 缺少 compose provider | 安装podman-compose或 docker-compose v2 |
| WSL 内存长时间占满 | WSL2 默认内存限制过大 | 配置.wslconfig并执行wsl --shutdown |
6. 最后聊聊我的日常习惯
6.1 Windows Terminal + WSL 的工作流
我现在的工作流已经固定成:Windows Terminal 开两个标签页,一个标签页进 WSL Ubuntu,所有podman命令都在这里执行;另一个标签页留在 PowerShell,处理文件复制、进程查看这类 Windows 本机操作。
这样做的好处是:容器永远待在同一个环境里,不会因为进错终端导致命令连不上。而且 WSL2 的 localhost 端口是自动转发到 Windows 的,所以 Windows 的浏览器、调试工具可以直接访问容器端口,没有任何额外配置。
如果你更习惯 GUI 操作,可以装 Podman Desktop。它扮演类似 Docker Desktop 的角色,但底层还是调用podmanCLI,跑的应用界面更轻。不过我个人还是偏向命令行,因为很多运维脚本和 CI 场景最终还是靠命令。
6.2 几个让我少踩坑的小技巧
针对 Windows 上跑 Podman,还有几个小经验值得分享:
- 如果你要在
docker run和podman run之间来回切换,不要直接设alias docker='podman',部分脚本会检测 Docker 环境变量,建议在项目里统一写好 Makefile 或者 npm scripts,内部调用podman。 - 定期执行
podman system prune -af --volumes清理不再使用的镜像和容器卷,Windows 的硬盘空间很宝贵。 - 使用
podman stats查看容器资源占用,比 Windows 任务管理器里翻 VM 进程直观得多。 - 容器尽量不要用 root 用户运行。Podman 的 rootless 模式就是天然优势,别为了省事而在容器里默认使用
--user root,安全隐患不值得。
我在实际使用中还发现,Podman 对普通开发场景非常友好,但别指望它是 Docker Desktop 的完全替代品,如果你的项目高度依赖 Docker Desktop 的 Kubernetes 单机集群、GUI 管理面板、自动升级这些功能,还得权衡一下。不过对我这种主要靠命令和 Compose 干活的人来说,Podman 已经足够,而且资源占用确实小了很多。
后面如果遇到 Podman 在 Windows 上的新坑,我再继续更新记录。也欢迎有类似迁移经验的朋友一起交流补充。