Missing X server or $DISPLAY:无头环境跑浏览器的正确姿势

在服务器 / 容器里想跑 Chromium、Electron、Playwright,报错:

[...] ozone_platform_x11.cc:259] Missing X server or $DISPLAY

意思很直白——程序想弹 GUI,但当前环境没 X11 显示服务。三个典型场景。

场景一:SSH 到 Linux 服务器

SSH 登进去的 shell 默认没有桌面:

ssh root@server
google-chrome     # 直接报 Missing X server

两种解法:

Headless 模式(首选)

google-chrome --headless=new --disable-gpu --no-sandbox https://example.com

Playwright:

browser = playwright.chromium.launch(headless=True)

Puppeteer:

const browser = await puppeteer.launch({ headless: true });

非 headless 场景用 X11 转发

ssh -X root@server

或用 -Y(信任模式,速度更快)。前提是本机有 X server(Mac 上是 XQuartz,Windows 上是 VcXsrv/X410)。

场景二:Docker 容器里

容器默认没 X server:

$ echo $DISPLAY
(空)

首选还是 headless。要真的跑 GUI 就靠 Xvfb(虚拟帧缓冲):

apt update && apt install -y xvfb
xvfb-run -a chromium https://example.com

xvfb-run 会在后台起一个虚拟 X server 让程序连。Playwright 官方镜像就自带这套。

如果显示环境变量已经写了 DISPLAY=:1,但仍然报错,那是因为 :1 对应的 X server 根本没起:

ls /tmp/.X11-unix/

看不到 X0 / X1 就是没启动。容器里手动 export DISPLAY=:1 是没用的——DISPLAY 只是”连哪个 X server”,不会创建 X server。

场景三:WSL

WSL2 现在原生支持 WSLg,一般不会碰到这错。老 WSL1 或者关了 WSLg 的话:

export DISPLAY=:0
sudo apt install x11-apps
xclock         # 测试有没有 GUI

再不行就装个 X server(Windows 上 VcXsrv / X410)。

快速判断清单

先看几个变量:

echo $DISPLAY               # 应该是 :0 或 :1
ls /tmp/.X11-unix/         # 应该有 X0 之类的 socket
ps aux | grep -E 'Xorg|Xvfb'  # 有 X server 进程在跑
hostname                    # 类似 6e6340e2e0d2 基本是容器
cat /proc/1/cgroup          # 看到 docker 关键字 = 容器

对号入座:

现象判断
DISPLAY 空没有桌面环境,用 headless 或 Xvfb
DISPLAY 有但 X11 socket 目录空声明了但 X server 没起
hostname 是短哈希Docker 容器
WSL 里 xclock 不动WSL 侧 X server 没通

一句话总结

服务器 / 容器跑浏览器,能 headless 就 headless,非要 GUI 上 Xvfb。 手动改 $DISPLAY 只是告诉客户端连哪儿,从来不会凭空生出一个 X server。