Ubuntu 16.04 配置 VNC 远程桌面:TigerVNC + systemd 完整指南
2026/9/23 3:49:52 网站建设 项目流程

搞运维的人大多都碰过这种场景:机房或内网里摆着一台 Ubuntu 16.04 的机器,没有接显示器,但业务方或者研发临时要看个图形界面、跑个 GUI 工具,这时候远程桌面就成了刚需。Ubuntu 16.04 虽然已经退出常规维护期,但企业内部存量服务器和工作站还大量跑着它,VNC 仍然是这类环境里最常见也最直接的远程桌面方案。

这篇文章不吹不黑,只讲我在 16.04 上反复配置 VNC 的完整流程,从服务端选型、安装、配置、systemd 托管,到客户端连接、安全加固和问题排查都过一遍。重点不是“能连上就行”,而是让你知道每一步为什么要这么干、踩了坑怎么定位。

1. 设计思路拆解:这个项目到底要解决什么问题

1.1 为什么需要 VNC,Ubuntu 16.04 上远程桌面不是有现成的吗

很多人会问:Ubuntu 桌面版不是自带“桌面共享”功能吗,为什么还要单独装 VNC?

Ubuntu 16.04 默认桌面环境是 Unity,系统里确实带了一个叫 vino 的 VNC 服务端,也就是“桌面共享”的后台实现。但 vino 干的事情和运维场景里需要的 VNC 完全是两码事:它是把当前已经在物理显示器上登录的桌面画面共享出去,换句话说,必须有人先在机器本地登录了图形会话,远程才能连上。服务器放在机房,没有接显示器,没有人登录,vino 就彻底歇菜了。

运维场景真正需要的是“无头远程桌面”——机器不接屏幕、没有人登录,我通过网络连上去之后,系统自动帮我拉起一个全新的图形会话。这个会话和物理显示器没关系,它跑在虚拟的显示设备上,我用客户端看到的是一套独立桌面。实现这个逻辑就需要一个真正的 VNC Server,比如 TigerVNC、x11vnc、x0vncserver 这类工具。

另一个关键点是版本差异。Ubuntu 18.04、20.04 上配置 VNC 的很多教程用的是 GNOME 的 Wayland 会话,和 16.04 的 Xorg + Unity 配置路径完全不同,网上刷到的教程经常直接照搬过来就翻车。所以我写这篇的时候默认系统是Ubuntu 16.04 + Unity 桌面,这也是存量机器里最主流的组合。

1.2 主流 VNC 服务端怎么选:TigerVNC 还是 x11vnc

选型是整个配置过程中第一个坑,选错了后面全白搭。我先把我实际用过的几个方案摆出来对比下:

服务端核心逻辑适合场景我在 16.04 上的评价
vino共享当前已登录桌面临时给本地已登录用户看个屏幕不适合无人值守服务
x11vnc复用已有的 X 显示(:0)需要远程控制物理显示器上的桌面对无头机器没意义,它默认也要有个 X 在跑
TigerVNC standalone独立启动虚拟 X 桌面,多会话隔离无头服务器、多用户远程桌面首选,稳定、权限清晰、systemd 好管
x0vncserver抓取现有 X 输出做 VNC 转发和 x11vnc 类似不是独立会话,不推荐

我一般无脑选TigerVNC。原因有几个:第一,它提供独立的vncserver命令,可以创建一个全新的虚拟桌面;第二,它支持按用户隔离配置,每个人有自己的~/.vnc目录和密码,权限不会互相打架;第三,和 systemd 配合很顺,能实现开机自启,这在机房无人值守场景是硬需求。

如果你只是想让运维同事能看到物理屏幕上正在跑的东西,那 x11vnc 也可以,但那种场景一般发生在“机器就在旁边,只是懒得走过去”的情况,和本文讲的无头服务不是一个路子。

2. 环境准备与基础安装

2.1 先确认系统状态:别在没桌面的系统上白忙活

我得先泼一盆冷水:VNC 服务端再厉害,它也只是把图形会话转发给你看,它自己不会生成桌面。如果你的 Ubuntu 16.04 是最小化安装,压根没装桌面环境,那即使 VNC 连上了,看到的也只是一片灰屏或者一个只剩下鼠标指针的空壳。

所以在安装 VNC 之前,先跑这几个命令确认环境:

# 查看系统版本,确认是 16.04 lsb_release -a # 查看当前是否有图形会话 echo $XDG_CURRENT_DESKTOP # 查看已安装的桌面环境相关包 dpkg -l | grep -E "ubuntu-desktop|unity|xfce4|gnome"

如果$XDG_CURRENT_DESKTOP是空的,dpkg也查不到ubuntu-desktop或类似的桌面包,说明这就是个纯命令行系统。这种情况下你有两个选择:

  • 安装完整的 Ubuntu 桌面:sudo apt install ubuntu-desktop,包很大,几百 MB 到 1GB,装完还要重启,但最省心。
  • 装轻量级桌面 XFCE:sudo apt install xfce4 xfce4-goodies,体积小,在远程桌面上跑起来也比 Unity 流畅不少。

从运维角度,我强烈建议对 16.04 这种老系统做远程桌面时用 XFCE。原因后面配置部分会细说,这里先记住一个结论:Unity 在虚拟 VNC 屏幕上渲染效率不高,远程操作用起来有明显的卡顿感,XFCE 会顺畅很多。

2.2 安装 TigerVNC Server:一条命令的事

确认系统版本、桌面环境都就绪后,安装 TigerVNC 非常简单:

# 先更新软件源索引 sudo apt update # 安装 TigerVNC 服务端和相关工具 sudo apt install tigervnc-standalone-server tigervnc-common

这里多说一句:Ubuntu 16.04 官方源里的 TigerVNC 版本比较老,大概是 1.7 左右。对绝大多数场景完全够用,不需要折腾第三方 PPA。如果你非要最新版,可以加 PPA 编译安装,但生产环境我一般遵循“能不动源就不动源”的原则,老版本反而经过了更多环境验证。

安装完成后,可以用vncserver --version确认一下是否装好:

vncserver --version

能弹出版本信息就算安装成功。这时候先别急着启动,离真正能连上还差几个配置文件。

3. 核心配置详解:密码和 xstartup 是重中之重

3.1 设置 VNC 访问密码:vncpasswd 的隐藏坑

VNC 密码和系统登录密码是两套体系,必须单独设置。首次启动服务前,先执行:

vncpasswd

这个命令会要你输入两次密码,密码会保存在~/.vnc/passwd文件里。操作本身很简单,但有几个细节很容易坑到人:

第一,传统 VNC 协议对密码长度有上限,TigerVNC 在 16.04 上实际只取前 8 位。也就是说你设置了一个 16 位复杂密码,后面 8 位会被静默丢弃,客户端连的时候也只能用前 8 位验证。这个限制在 TigerVNC 1.8 之后才逐步放宽,16.04 源里的老版本是实打实的 8 位限制。

第二,vncpasswd默认生成的文件权限是 600,这个不要手贱去改。如果变成 644,VNC Server 会因为“密码文件权限过于宽松”而拒绝启动,这是安全机制。

第三,每个 Linux 用户有自己独立的 VNC 密码。比如你用rootvncpasswd,那密码就存在/root/.vnc/,普通用户登录不了你的 VNC 会话。多用户服务器上要给每个人单独设密码,互不干扰。

3.2 编辑 xstartup:决定你连接后看到什么

xstartup 是 VNC 会话启动时自动执行的脚本,位置在~/.vnc/xstartup。它干的事情简单说就是:“VNC 服务拉起一个虚拟 X 窗口后,在这个窗口里启动什么桌面”。

第一次启动 VNC 之前,这个文件还不存在,vncserver 首次运行会生成一个默认的模板。默认模板通常指向系统默认桌面,但 16.04 上经常失灵,因为 Unity 桌面启动依赖很多环境变量,默认模板里没带。所以我们要自己写一个。

这是我用在 Ubuntu 16.04 上的标准写法:

#!/bin/sh # 清除会话管理器和 DBus 环境变量,避免冲突 unset SESSION_MANAGER unset DBUS_SESSION_BUS_ADDRESS # 启动 XFCE 桌面 startxfce4 & # 如果系统是 Unity 桌面,则用下面这行替代上面那行 # gnome-session --session=ubuntu-2d & # 避免会话窗口被锁屏卡住 xsetroot -solid grey

写完记得给执行权限:

chmod 755 ~/.vnc/xstartup

这里有几个点需要展开讲:

为什么unset SESSION_MANAGERunset DBUS_SESSION_BUS_ADDRESS?因为 VNC 启动的是全新会话,如果系统里还残留着物理桌面或者之前会话留下的环境变量,会导致新会话启动到一半就崩,表现出来就是连接后灰屏或者桌面起不来。清理环境变量是一个保险动作,成本低收益高。

为什么推荐用 XFCE 而不是 Unity?在虚拟屏幕上跑 Unity,光是启动就要好几十秒,而且打开菜单、拖动窗口都会明显卡顿。XFCE 轻量,启动快,远程操作体验好得多。如果公司内部合规要求必须用 Unity 桌面,那就用注释里那行gnome-session --session=ubuntu-2d

还要注意:16.04 的 Unity 是 2D 渲染模式,配置远程桌面时务必要加--session=ubuntu-2d,而不是直接写unity或者gnome-session。这个参数在新版本系统上已经不存在了,很多从 18.04 教程抄过来的写法在 16.04 上根本起不来。

3.3 首次启动会生成配置文件:先手动跑一次

写完 xstartup 后,先手动启动一次 VNC Server,把配置文件和日志生成出来:

# 第一个显示编号 :1,端口对应 5901 vncserver :1

如果一切正常,会看到类似New Xtigervnc server ... is :1的输出。这时候在本地测试一下端口监听:

ss -tlnp | grep 5901

能看到0.0.0.0:5901127.0.0.1:5901的监听记录,说明服务已经起来了。

手动启动还有一个作用:测试你的 xstartup 脚本能不能正常拉起桌面。如果启动后过两秒连接进去是灰屏,多半是脚本写得有问题,这时候去看日志:

cat ~/.vnc/*.log

TigerVNC 会把启动过程中的报错写进~/.vnc/主机名:1.log,排错就靠它了。

4. 用 systemd 托管 VNC 服务:实现开机自启

4.1 手动启动的问题在哪

手动跑vncserver :1能用,但不够“运维”。问题很明显:机器重启后 VNC 服务不会自动拉起,如果人不在现场就彻底断了;进程如果意外退出,也没有守护机制帮你拉回来。

正确做法是写一个 systemd service 文件,把 VNC Server 托管起来。这也是 16.04 相比老教程最大的进步点,早期的 Ubuntu 用的是init.d脚本,维护起来很痛苦。

我习惯把配置文件放在/etc/systemd/system/vncserver@.service,用模板方式支持多用户多会话。一个典型的配置模板:

[Unit] Description=VNC Server for %i After=syslog.target network.target [Service] Type=forking User=ubuntu Group=ubuntu WorkingDirectory=/home/ubuntu PIDFile=/home/ubuntu/.vnc/%H:%i.pid ExecStart=/usr/bin/vncserver :%i -geometry 1280x800 -depth 24 -localhost ExecStop=/usr/bin/vncserver -kill :%i [Install] WantedBy=multi-user.target

这里有几个地方要解释清楚:

  • %i是模板实例,当你执行systemctl start vncserver@1时,%i就是 1,对应的 VNC display 是:1,端口是 5901。这样一套模板可以管理多个会话,比如@2对应 5902。
  • User=Group=决定了 VNC 进程以哪个身份运行,必须和创建~/.vnc目录、跑vncpasswd的那个用户一致,否则会读不到密码文件。
  • ExecStart里的-geometry参数控制虚拟屏幕分辨率,这个看业务需求,一般 1280x800 是最稳妥的。-depth 24是颜色深度,如果网络环境差可以降到 16,但颜色会偏色,我一般保持 24。
  • -localhost参数表示 VNC 只监听本机回环地址,这个不是必选项,但如果你准备走 SSH 隧道访问 VNC,强烈建议加上,能挡掉一大批端口扫描攻击。这个策略我在下一节还会细讲。

4.2 启动、开机自启与服务状态检查

配置文件写好后,依次执行:

# 重新加载 systemd 配置 sudo systemctl daemon-reload # 启动 :1 会话对应的 VNC 服务 sudo systemctl start vncserver@1 # 设置开机自启 sudo systemctl enable vncserver@1 # 查看服务状态 sudo systemctl status vncserver@1

如果状态显示active (running),说明托管成功。这时候再用ss -tlnp看端口,监听情况会比手动启动时更规范,因为 systemd 会统一管理进程生命周期,而不只是后台挂一个进程。

有一个注意点:如果之前手动启动过vncserver :1,再执行 systemd start 可能会报端口被占用。先跑vncserver -kill :1或者pkill -f Xtigervnc清掉旧进程再启动 systemd 服务,否则会有个“地址已在使用”的经典报错。

另外,如果你改了任意配置文件(包括 xstartup、systemd 模板),记得重启对应的服务实例:

sudo systemctl restart vncserver@1

很多人改了 xstartup 后发现桌面没变化,就是因为没有重启 VNC 进程。

5. 防火墙和安全加固:别把 VNC 裸奔到公网

5.1 放行 VNC 端口前先搞清楚规则

Ubuntu 16.04 默认可能没开防火墙,如果你装完系统后手动启用了 UFW,需要放行 VNC 端口。VNC 的端口规则很简单:display:1对应 5901,:2对应 5902,依次类推。

# 查看 UFW 状态 sudo ufw status # 放行 5901 端口(按你的实际 display 号调整) sudo ufw allow 5901/tcp

如果你用了-localhost参数,VNC 只监听 127.0.0.1,这时候 UFW 放不放行 5901 其实都不影响,因为外部流量根本到不了这个端口。这点要提前想清楚,别配置文件里写了-localhost又开着防火墙放行端口,等于白折腾一遍。

5.2 从安全角度考虑:VNC 不适合裸奔公网

说到底,VNC 协议本身的设计侧重点是“远程显示”,不是“加密传输”。虽然 TigerVNC 近几个版本在认证环节有改进,但 16.04 源里的老版本整体安全强度有限,密码也只有 8 位,爆破成本极低。机房环境里有内网隔离还好,一旦暴露到公网,基本等于给扫描器送人头。

我在实际项目里的推荐做法是:

第一,VNC 只监听本机,强制走 SSH 隧道访问。客户端机器上执行:

ssh -L 5901:localhost:5901 ubuntu@服务器IP

这个命令的意思是把本机的 5901 端口流量通过 SSH 加密通道转发到服务器的 5901 端口,然后 VNC 客户端连接localhost:5901即可。这样数据在传输过程中是加密的,VNC 也不直接暴露在网络上。

第二,如果团队里有人确实需要非加密直连,那就把防火墙限制到固定的内网 IP 段,不要懒省事直接ufw allow 5901/tcp。运维工作里安全策略不是成本,是刚需。

第三,不要把 VNC 的 8 位密码设置成和系统密码一样,VNC 密码泄露的风险和系统密码泄露的风险要隔离开,这个习惯能救你一次。

6. 客户端连接与环境验证

6.1 Windows / Linux / macOS 客户端连接方式

服务端配好,剩下就是客户端连接。我自己最常用的组合是 Windows 下的 VNC Viewer。

打开 VNC Viewer,在地址栏输入:

服务器IP:5901

如果是走 SSH 隧道,地址就写:

localhost:5901

回车后客户端会提示未加密连接的风险,确认后输入 VNC 密码。这里有个体验细节:VNC Viewer 会缓存访问过的服务器列表和密码,公用的 Windows 机器上记得点掉“记住密码”选项,防止密码泄露。

Linux 桌面客户端一般直接装tigervnc-viewer

sudo apt install tigervnc-viewer vncviewer 192.168.1.100:5901

macOS 用户更简单,自带“屏幕共享”功能,在 Finder 里按Cmd + K,输入:

vnc://192.168.1.100:5901

macOS 的 VNC 客户端兼容性不错,但连接非 Retina 分辨率的老 VNC Server 时画质会有点发虚,不影响操作。

6.2 连接后怎么确认环境真的可用

很多场景下连上 VNC 不算完,你得确认这个远程桌面“真的能干正事”。比如研发说要用 VNC 远程跑一下 RVIZ 或者其他 GUI 工具,那就必须验证两件事:桌面环境是否完整、OpenGL 渲染是否可用。

在 VNC 会话里打开终端,跑这两个命令:

# 确认当前显示环境变量 echo $DISPLAY # 检查图形渲染信息 glxinfo | grep "OpenGL renderer"

echo $DISPLAY应该输出:1localhost:10.0之类的内容,这是 X11 应用定位显示位置的关键参数。如果应用启动时报“cannot open display”,基本都是这个环境变量没传对。

glxinfo需要额外安装mesa-utils

sudo apt install mesa-utils

如果能看到软件渲染器(比如 llvmpipe),说明 OpenGL 是通的,大多数 GUI 程序都能跑。如果 VNC 会话里跑 RVIZ 这类重型三维可视化工具出现画面撕裂、无法显示模型,通常不是 VNC 的问题,而是这台机器没有 GPU 或者 GPU 驱动没装,远程桌面只能走软件渲染,性能上不去。这属于硬件层限制,再调 VNC 参数也解决不了,只能换机器或者接受降级体验。

6.3 调整分辨率和画质的小技巧

连上 VNC 后,如果觉得画面卡顿、文字模糊,有几个调优手段我不止一次用过,实测有效:

  • 把 VNC View 的“Picture Quality”从“自动”改成“高画质”或“中画质”,不要开“最优先流畅度”,否则文字会糊成一团。
  • 服务端分辨率不要盲目调大,1280x800 是性能和显示面积的平衡点,非要 1920x1080 也可以,但网络环境一般的话体验会明显变肉。
  • 如果网络延迟很高,可以在 VNC Viewer 的编码设置里选择压缩率更高的编码方式,TigerVNC 默认的编码策略在局域网内表现不错,跨公网时就得手动调。

7. 常见问题排查速查:一看就能定位问题

7.1 连接被拒绝:Connection refused / 10061

这是 VNC 排错里遇到最多的错误,Windows 下的 VNC Viewer 会直接弹 “Connection refused (10061)”,Linux 下则提示 “Unable connect to socket: Connection refused (10061)”。

这个报错的直接原因是“目标端口上没有服务在监听”。按这个思路,自上而下排查:

排查点命令/方法解决思路
服务是否运行systemctl status vncserver@1ps aux | grep Xtigervnc服务没起来就先启动,并看日志
端口是否监听ss -tlnp | grep 5901如果只看到 127.0.0.1:5901,说明只能本机访问
防火墙是否拦截sudo ufw status,检查 5901 是否放行放行端口或调整监听地址策略
客户端地址是否写对确认是IP:5901,不是裸 IPdisplay :1 就是 5901,:2 就是 5902
监听地址限制看 vncserver 启动命令是否带-localhost如果带了这个参数但不走 SSH 隧道,外部永远连不上

最后一条是我自己踩过的深坑:在 systemd 配置里加了-localhost,第二天客户直接说“VNC 连不上了”,实际上服务跑得好好的,纯粹是策略冲突,要么让客户走 SSH 隧道,要么把-localhost去掉并靠防火墙保护。最怕的就是两个策略混在一起,定位起来要绕半天。

7.2 连接后灰屏或者只看到桌面壁纸

连接成功但桌面起不来,是 VNC 配置里第二大高频问题。

灰屏的第一嫌疑是 xstartup 脚本问题。先确认你能看到的是完全灰色还是有一个空桌面。如果完全是灰色,说明 X 服务起来了,但桌面会话没有启动成功,直接看~/.vnc/*.log里的报错。最常见的是 xstartup 里写的命令不存在或需要指定绝对路径,比如startxfce4找不到时,得改成/usr/bin/startxfce4

第二个常见原因是 xstartup 文件没有执行权限。很多人创建完文件忘了chmod 755,VNC 启动时会认为脚本不可执行,直接跳过桌面拉起流程。这个坑好排查也很好避免,写完顺手敲一下 chmod 就行。

第三个原因和环境变量冲突有关,就是前面提到的SESSION_MANAGERDBUS_SESSION_BUS_ADDRESS。如果本机上已经跑了一个图形会话,某些环境变量会被继承到 VNC 会话里,导致新会话起不来。xstartup 开头那两行unset干的就是这事儿。

如果连上后能看到桌面壁纸,但 Dock 栏、任务栏、启动器都没起来,那说明桌面框架启动了一半就崩了。这种情况去日志里找段错误或者 D-Bus 相关错误,十有八九是 Unity 会话的问题,换 XFCE 能绕开一大批坑。

7.3 密码认证失败的隐藏原因

VNC 密码提示 incorrect 一般分三种情况:

  • 输错密码,这个没办法,只能重试。
  • 密码文件权限不对,确认一下ls -l ~/.vnc/passwd,权限必须是-rw-------(600)。
  • 密码超过 8 位被截断。16.04 上 TigerVNC 的老版本只认前 8 位,你客户端输入完整密码反而验证不过。这种情况只能vncpasswd重新设置,把密码控制在 8 位以内,或者升到新版本 TigerVNC。

还有一种非常隐蔽的问题:多用户服务器上 A 用户的 VNC 密码设置好了,B 用户启动的 VNC 服务却读不到,因为进程的运行用户不对。systemd 配置里User=是谁,密码就必须是谁的,这个对照关系经常被忽略。

7.4 VNC 会话里 GUI 程序打不开怎么办

内网运维最常见的场景就是“VNC 连上后,研发说跑不了 RVIZ 或者某个图形软件”。先别急着怀疑 VNC,从三个方向排查:

第一,DISPLAY 环境变量。打开终端跑echo $DISPLAY,如果是空的,手动指定一下:

export DISPLAY=:1

然后在同一个终端里再启动你的 GUI 程序。有些应用需要把 DISPLAY 环境变量带进 systemd 服务或者 crontab 任务,这类情况要单独处理。

第二,权限问题。X11 的访问控制有时候会拒绝特定用户连接显示服务,报xauth或者 “No protocol specified” 的错。遇到这种就检查~/.Xauthority是否存在且可读,要么把当前用户加入 VNC 会话的 Xauthority 列表,要么干脆临时关掉访问控制(不建议长期开)。

第三,OpenGL/GPU 渲染。Xorg 和 VNC 组合下,很多应用默认启用硬件加速,但无头服务器上根本没有 GPU,或者 GPU 驱动没配好,应用就会崩溃或黑屏。尝试给应用强制软件渲染,常见做法是设置环境变量LIBGL_ALWAYS_SOFTWARE=1再启动程序,很多时候能解决问题。

8. 我在实际运维中的几个小心得

技术流程讲完,最后分享几个不能量化的经验。

第一,Ubuntu 16.04 上配 VNC,最好一次到位选 TigerVNC + XFCE。我知道很多人纠结“系统自带的是 Unity,硬要装个 XFCE 是不是多此一举”,但 VNC 远程场景下 Unity 的体验确实一言难尽,启动慢、特效卡、依赖重。XFCE 装完也就一两百 MB 的额外占用,换来的是流畅得多的远程操作体验。为这个选择翻过车的不止我一个人,你可以先试,试完大概率回来换 XFCE。

第二,配置 VNC 之前,先确认这几样东西:系统装了桌面、密码文件权限是 600、xstartup 有执行权限、systemd 配置里 User 和 VNC 密码属主一致。这四项检查做完,90% 的基础坑都避开了。别一上来就改配置,基础没夯实越调越乱。

第三,旧系统的运维节奏是“能不动就不动”。16.04 源里的 TigerVNC 版本虽然是老,但它和系统的兼容性已经经过了多年磨炼。不要为了追新版去加乱七八糟的 PPA,除非有明确的安全合规要求。老版本 VNC 客户端兼容性更好,至少我在 Win/Mac/Linux 各种客户端上都连过,没出过协议不兼容的问题。

最后再提一句:如果你接手的环境后续有计划升级到 18.04 或 20.04,VNC 配置思路要跟着变,特别是桌面会话语法的写法几乎都变了,xstartup 脚本不能直接拷贝。但系统底层的服务托管、安全加固、排错思路是一脉相承的,这篇文章里的方法论换个版本照样能用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询