
最近把开发环境里的 Docker Desktop 换成了 Podman在 Windows 上折腾了大概一周过程中踩了无数个坑也把podman machine、WSL2、Compose 的配合方式全部摸了一遍。这篇就当个人使用记录整理出来给同样想在 Windows 上跑 Podman 的朋友一个参考。Podman 对 Docker CLI 的兼容性很好绝大多数docker命令直接换成podman就能用真正的难点反而不是命令本身而是 Windows 下面的 WSL2、端口转发、文件挂载这些环境问题。如果你和我一样日常主要用 Docker Compose 做本地开发又不想继续被 Docker Desktop 的后台进程和资源占用困扰这篇文章会很有价值。下面我先从为什么换、怎么装、怎么用到最后踩坑实录一条线完整讲清楚。1. 为什么我在 Windows 上换掉 Docker Desktop1.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 方式Apodman 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 machineWSL2 内安装 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 nginxpodman pull nginx拉取镜像docker run -d -p 8080:80 nginxpodman run -d -p 8080:80 nginx运行容器docker pspodman ps查看运行中容器docker imagespodman images查看镜像列表docker exec -it 容器名 bashpodman exec -it 容器名 bash进入容器终端docker logs -f 容器名podman logs -f 容器名跟踪容器日志docker stop 容器名podman stop 容器名停止容器docker system prunepodman 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 项目搬到 Podman4.1 先装一个 compose 提供者Podman 里执行podman compose需要一个外部的 compose provider常见的有两个选择podman-composePython 实现安装简单兼容性日常够用。docker-compose原版 ComposePodman 也能调用它做 provider。如果你在 WSL 的 Ubuntu 里推荐直接装podman-composesudo 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] memory4GB processors4 swap2GB再执行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_clone1更省事的方案是直接给 WSL 发行版加上 systemd 支持让 Podman 的用户态管理更稳定。新版 WSL 已经在/etc/wsl.conf里支持 systemd[boot] systemdtrue改完后执行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 v2WSL 内存长时间占满WSL2 默认内存限制过大配置.wslconfig并执行wsl --shutdown6. 最后聊聊我的日常习惯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 dockerpodman部分脚本会检测 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 上的新坑我再继续更新记录。也欢迎有类似迁移经验的朋友一起交流补充。