Chapter 6 — VNC: The Protocol, the Server, and the Tunnel
VNC (Virtual Network Computing) predates RDP and works
on a simpler principle: it captures the screen as raw pixel data and sends
it to the viewer. There is no server-side rendering intelligence — whatever
appears on the display is streamed directly. This means VNC works with
virtually any desktop environment and on any operating system, but it also
means it uses more bandwidth than RDP for the same session quality.
VNC vs RDP — The Key Differences
Sends rendering commands — "draw this text at this position in this font"
Sends pixel data — a compressed copy of what's on screen
Creates a new virtual session — the physical display is unaffected
Can share the actual physical display — someone at the machine sees what you see
Encrypted by default (TLS)
Unencrypted by default — must tunnel over SSH
Better bandwidth efficiency — less data for same quality
Higher bandwidth use — more data, especially during motion
Native clipboard, drive, audio redirection
Clipboard sharing only (via extensions); no drive/audio natively
Windows-native; needs xRDP on Linux
Works on Linux, macOS, Windows — true cross-platform
When to prefer VNC over xRDP: when you want to share or
observe the actual physical desktop (great for remote support —
you see exactly what the user sees), or when setting up xRDP is more
trouble than it's worth on an unusual desktop environment. For headless
server access, xRDP or SSH is almost always better.
VNC Display Numbers and Ports
VNC uses display numbers starting at :1
(display :0 is the physical screen). Each display maps to a
TCP port: display number + 5900.
| Display | Port | Typical use |
| :0 | 5900 | Physical console — sharing the real screen |
| :1 | 5901 | First virtual VNC display (default when you run vncserver) |
| :2 | 5902 | Second virtual display (second user or second session) |
| :3 | 5903 | Third virtual display, and so on |
When connecting, you specify the host and display: 192.168.0.24:1
— or the full port: 192.168.0.24:5901. Both mean the same thing.
Installing TigerVNC Server on Debian / Ubuntu
debian — install TigerVNC
philip@debian:~$ sudo apt update
philip@debian:~$ sudo apt install -y tigervnc-standalone-server tigervnc-common
# set a VNC password (stored separately from your login password)
philip@debian:~$ vncpasswd
Password:
Verify:
Would you like to enter a view-only password (y/n)? n
# start a VNC server on display :1 with XFCE
philip@debian:~$ vncserver :1 -geometry 1920x1080 -depth 24
New Xtigervnc server 'debian:1 (philip)' on port 5901 for display :1.
# stop the server
philip@debian:~$ vncserver -kill :1
Killing Xtigervnc process ID 3421... success
Configure the desktop environment for VNC
TigerVNC reads ~/.vnc/xstartup to know which desktop to launch:
#!/bin/bash
# ~/.vnc/xstartup — launched by vncserver on startup
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
# launch XFCE
exec startxfce4
# --- alternatives ---
# exec mate-session
# exec gnome-session
# exec openbox-session
debian — make xstartup executable
philip@debian:~$ chmod +x ~/.vnc/xstartup
# restart the server to pick up the new config
philip@debian:~$ vncserver -kill :1 && vncserver :1 -geometry 1920x1080 -depth 24
Running VNC as a systemd Service
Starting the VNC server manually works for testing, but for a persistent
setup you want it to start automatically on boot:
# /etc/systemd/system/vncserver@.service
# the @ allows multiple instances: vncserver@1.service, vncserver@2.service
[Unit]
Description=TigerVNC server — display %i
After=network.target
[Service]
Type=forking
User=philip
Group=philip
WorkingDirectory=/home/philip
PIDFile=/home/philip/.vnc/%H:%i.pid
ExecStartPre=/bin/sh -c '/usr/bin/vncserver -kill :%i > /dev/null 2>&1 || :'
ExecStart=/usr/bin/vncserver :%i -geometry 1920x1080 -depth 24 -localhost no
ExecStop=/usr/bin/vncserver -kill :%i
[Install]
WantedBy=multi-user.target
debian — enable the service
philip@debian:~$ sudo systemctl daemon-reload
philip@debian:~$ sudo systemctl enable --now vncserver@1.service
philip@debian:~$ sudo systemctl status vncserver@1.service
VNC is Unencrypted — Always Use an SSH Tunnel
VNC transmits everything in plain text by default —
including your password (only weakly hashed) and every pixel of your
screen. On a local trusted network this may be acceptable. Over the
internet, or on any shared network, you must wrap VNC in an SSH tunnel.
Never open VNC port 5901 to the internet directly.
The SSH tunnel approach
Forward the remote VNC port to a local port over SSH, then connect
your VNC viewer to localhost rather than the remote IP.
The traffic travels inside the encrypted SSH connection:
your machine localhost:5901
│
SSH tunnel encrypted SSH connection :22
│
remote server 127.0.0.1:5901 (VNC — only accessible locally)
# Step 1 — open the SSH tunnel (background, no shell)
ssh -fNL 5901:localhost:5901 philip@192.168.0.24
# Step 2 — connect your VNC viewer to the local end of the tunnel
# (address is localhost:5901, NOT the remote IP)
vncviewer localhost:5901
# One-liner — tunnel + viewer in the same command (Linux/macOS)
ssh -fNL 5901:localhost:5901 philip@192.168.0.24 && vncviewer localhost:5901
# Keep VNC server locked to localhost only (prevents direct connections)
# In your vncserver@.service, remove -localhost no and use:
vncserver :1 -geometry 1920x1080 -depth 24 -localhost yes
# Server will only accept connections from 127.0.0.1 — forces SSH tunnel
-localhost yes is the right production setting. If VNC
only listens on 127.0.0.1, a direct connection from outside
is impossible — the only way in is through the SSH tunnel. This means you
never need to open port 5901 in your firewall at all.
VNC Viewer Clients
TigerVNC Viewer
Windows · Linux · macOS
The matching client for the TigerVNC server. Fast, lightweight, supports all TigerVNC extensions. Free and open-source. Best choice if your server is TigerVNC.
RealVNC Viewer
Windows · Linux · macOS · iOS · Android
Polished cross-platform client with optional cloud-relay (RealVNC Connect). Free tier available. The cloud relay avoids needing an SSH tunnel — useful but sends traffic through RealVNC's servers.
Remmina
Linux (GTK)
Supports both RDP and VNC in one app. Has a built-in SSH Tunnel tab so you can configure the tunnel inside the connection profile rather than running a separate ssh command.
KRDC / Vinagre
Linux (KDE / GNOME)
Desktop-environment native clients. KRDC for KDE, Vinagre for GNOME. Both support VNC and RDP. Good for occasional use; Remmina is more feature-rich for regular work.
Connecting from Windows with PuTTY + TigerVNC
If you're on Windows and prefer a GUI for the SSH tunnel, PuTTY can do it:
- Open PuTTY → enter the server IP in Host Name
- Go to Connection → SSH → Tunnels
- Source port:
5901 · Destination: localhost:5901 · click Add
- Go back to Session, save the profile, then click Open
- Once the SSH session is open, connect TigerVNC Viewer to
localhost:5901
Common VNC Problems
Grey screen / no desktop
xstartup is missing, not executable, or contains the wrong command for the installed desktop.
Check ~/.vnc/xstartup exists, is chmod +x, and references an installed desktop (e.g. startxfce4). Check ~/.vnc/debian:1.log.
Authentication failed
Wrong VNC password — note this is different from your Linux login password.
Reset with vncpasswd then restart the server. The password file lives at ~/.vnc/passwd.
Connection refused
VNC server not running, wrong port, or server started with -localhost yes but you're not going through the SSH tunnel.
Check ss -tlnp | grep 590. If tunnel is required, open the SSH tunnel first then connect to localhost:5901.
Slow / choppy display
VNC's pixel-data transport is bandwidth-hungry. Compositing effects multiply the data sent per frame.
Disable compositing in the desktop settings (XFCE: Window Manager Tweaks → Compositor → off). Use -depth 16 or TigerVNC's -ZlibLevel 9 compression flag.
Quick Reference — vncserver Flags
# start display :1, 1080p, 24-bit colour, locked to localhost
vncserver :1 -geometry 1920x1080 -depth 24 -localhost yes
# allow external connections (LAN only — still use SSH tunnel from internet)
vncserver :1 -geometry 1920x1080 -depth 24 -localhost no
# maximum zlib compression (slow CPU, saves bandwidth)
vncserver :1 -ZlibLevel 9
# list running VNC sessions
vncserver -list
# kill display :1
vncserver -kill :1
# kill all sessions for this user
vncserver -kill :*
# view the server log for display :1
cat ~/.vnc/$(hostname):1.log
Next — Chapter 7: NoMachine and Alternatives.
RDP and VNC are the two foundational remote desktop protocols. Beyond them,
a tier of modern alternatives offers better performance, easier setup, and
built-in encryption. Chapter 7 covers NoMachine (NX
protocol — often the fastest option on a LAN), AnyDesk,
and TeamViewer: when each makes sense, and when you should
stick with the open standards you already know.