在 WSL2 + Arch Linux 下成功运行 rootless Podman(含 podman compose)的完整踩坑与修复指南

8 阅读3分钟

背景

最近在 WSL2 的 Arch Linux 环境中搭建开发环境,想用 Podman 完全替代 Docker,实现 rootless 容器管理(更安全、更轻量),并通过 podman compose 运行现有的 docker-compose.yml 文件。

一开始以为只是简单 pacman -S podman 就能用,结果连续踩了几个经典坑:

  • newuidmap / newgidmap 缺少 setuid 或 capabilities,导致 namespace 建立失败
  • netavark nftables 在 WSL2 内核下报错
  • podman compose 优先调用了 docker-compose 插件,却连不上 socket
  • 用户级 systemd(systemctl --user)连接 bus 失败,$XDG_RUNTIME_DIR / $DBUS_SESSION_BUS_ADDRESS 看似设置了却无效
  • 用 sudo systemctl --user 导致环境变量不继承

整个过程花了小半天时间,但最终成功实现了零 sudo、rootless、代码路径原地使用、podman compose 正常运行的开发体验。下面把所有问题、思路、命令和最终方案整理成一篇完整指南,希望帮到同样在 WSL2 + Arch 上折腾 Podman 的朋友。

环境信息

  • WSL2(Windows 11)
  • 发行版:Arch Linux
  • Podman 版本:(建议用最新版,过程中用的是 5.x 系列)
  • 内核:Linux x.x.x-microsoft-standard-WSL2

目标:rootless 模式下运行 podman compose up -d,代码和 volume 直接挂载当前目录,无需 Podman Desktop 的独立 machine。

问题 1:newuidmap 权限缺失

现象

newuidmap: Could not set caps
Error: cannot set up namespace using "/usr/bin/newuidmap": should have setuid or have filecaps setuid

原因:Arch 的 shadow 包安装的 newuidmap / newgidmap 默认没有 setuid 位,也没有 file capabilities,而 WSL2 内核对 setuid 支持不完美。

解决

# 检查当前权限
getcap /usr/bin/newuidmap
ls -l /usr/bin/newuidmap   # 看到 -rwxr-xr-x,没有 s 位

# 赋予 capabilities(推荐,WSL2 兼容性更好)
sudo setcap cap_setuid+ep /usr/bin/newuidmap
sudo setcap cap_setgid+ep /usr/bin/newgidmap

# 验证
getcap /usr/bin/newuidmap   # 应显示 cap_setuid+ep

如果 setcap 失败,可尝试重装 shadow 并重启 WSL:

sudo pacman -S --overwrite '*' shadow
# Windows PowerShell: wsl --shutdown

额外检查:确保 subuid/subgid 已配置

grep '^$(whoami):' /etc/subuid /etc/subgid
# 没有的话添加:
echo "$(whoami):100000:65536" | sudo tee -a /etc/subuid /etc/subgid

问题 2:netavark nftables 失败

现象

nftables error: "nft" did not return successfully while applying ruleset

原因:WSL2 内核对 nftables 支持不完整,Podman 5.x 默认用 nftables 后端。

解决: 编辑 ~/.config/containers/containers.conf(没有就创建):

[network]
firewall_driver = "iptables"

或者临时环境变量:

export NETAVARK_FW=iptables

问题 3:podman compose 连不上 socket

现象

Cannot connect to the Docker daemon at unix:///run/user/1000/podman/podman.sock

原因:podman compose(包装器)检测到 docker-compose 插件,优先执行它,但 rootless Podman 的 socket 没启动。

解决

  1. 启用用户级 podman.socket(注意:不要加 sudo

    systemctl --user enable --now podman.socket
    
  2. 设置环境变量(让 docker-compose 插件兼容 Podman)

    export DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock
    # 建议加到 ~/.bashrc
    
  3. 推荐替代方案:安装 podman-compose(Python 实现,不依赖 socket)

    sudo pacman -S python-podman-compose
    # 然后用 podman-compose up -d 代替 podman compose
    

问题 4:systemctl --user 连接 bus 失败

现象

Failed to connect to user scope bus via local transport: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined

原因:用了 sudo systemctl --user,sudo 切换到 root,用户变量丢失;或者 WSL2 下用户 systemd 实例没正确启动。

解决

  1. 永远不要用 sudo systemctl --user,直接用普通用户运行:

    systemctl --user enable --now podman.socket
    
  2. 确保环境变量(WSL2 经常丢失):

    export XDG_RUNTIME_DIR=/run/user/$(id -u)
    export DBUS_SESSION_BUS_ADDRESS=unix:path=$XDG_RUNTIME_DIR/bus
    

    永久加到 ~/.bashrc

    if [[ -z "$XDG_RUNTIME_DIR" ]]; then
        export XDG_RUNTIME_DIR=/run/user/$(id -u)
        mkdir -p "$XDG_RUNTIME_DIR"
        chmod 0700 "$XDG_RUNTIME_DIR"
    fi
    
    if [[ -z "$DBUS_SESSION_BUS_ADDRESS" ]]; then
        export DBUS_SESSION_BUS_ADDRESS=unix:path=$XDG_RUNTIME_DIR/bus
    fi
    
  3. 启用 linger(让用户 systemd 持久运行):

    loginctl enable-linger $(whoami)
    # 然后 wsl --shutdown 重启 WSL
    

最终验证

# 检查 socket
ls /run/user/1000/podman/podman.sock

# 测试简单容器
podman run --rm hello-world

# 启动 compose(根据你选择的方式)
podman compose --env-file .env -f docker/docker-compose.yml up -d
# 或
podman-compose --env-file .env -f docker/docker-compose.yml up -d

小结与建议

  • 优先 rootless:安全性高,WSL2 开发路径原地使用最方便
  • 优先 podman-compose:比 podman compose + docker-compose 插件更稳定
  • WSL2 特性:经常需要手动设置 XDG/DBUS、enable-linger、重启 WSL
  • 官方推荐 vs 实际:Podman 官方推 Podman Desktop + machine,但对于“在已有 WSL 发行版里开发”的场景,rootless 直装是更灵活的选择

整个过程虽然坑多,但调通后体验非常好:代码无需同步、CLI 零延迟、Podman 命令原生使用。

欢迎大家在评论区分享你的 WSL2 + Podman 经验,也欢迎指出文章中的不足~

(完)