Install the packages

sudo apt update && sudo apt upgrade
sudo apt install tightvncserver xfce4 xfce4-goodies xorg dbus-x11 \
                 x11-xserver-utils autocutsel

XFCE is lightweight enough to run smoothly even on a 1 GB VPS. GNOME is an option but it's significantly heavier and its compositor sometimes misbehaves under VNC. autocutsel enables clipboard sharing between the VNC session and your local machine.

Create a dedicated VNC user

Running a desktop session as root is a bad idea — some applications (notably Chrome / Chromium) refuse to launch as root, and a compromised graphical session has much more reach as root than as a normal user.

sudo adduser vncuser
sudo usermod -aG sudo vncuser       # optional: if this user needs sudo

Initialize the VNC password

sudo -u vncuser bash -c 'vncserver'
# It prompts for a password (max 8 chars — a TightVNC quirk).
# It also offers to set a view-only password — useful for pair debugging; say 'n' otherwise.

sudo -u vncuser bash -c 'vncserver -kill :1'

The init puts the password in /home/vncuser/.vnc/passwd (mode 600). It also generates a default xstartup that launches an ancient twm session — we'll replace that next.

Configure the XFCE session

sudo -u vncuser tee /home/vncuser/.vnc/xstartup <<'EOF'
#!/bin/sh
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS

# Enable clipboard sharing
autocutsel -fork

# Start XFCE
startxfce4 &
EOF

sudo chmod +x /home/vncuser/.vnc/xstartup
VNC is plain-text by itself

TightVNC's authentication exchange is decent, but the actual screen contents and keystrokes after connect are not encrypted. Never expose the VNC port (5901) directly to the internet — always reach it through an SSH tunnel (see below) or a VPN.

systemd service (instead of the old init.d script)

The 2019 version of this tutorial used a SysV init script. On any Debian version released since then, systemd is the right tool. Drop in a user-templated service:

sudo tee /etc/systemd/system/vncserver@.service <<'EOF'
[Unit]
Description=TightVNC server on display %i
After=syslog.target network.target

[Service]
Type=forking
User=%i
Group=%i
WorkingDirectory=/home/%i

PIDFile=/home/%i/.vnc/%H:1.pid
ExecStartPre=-/usr/bin/vncserver -kill :1 > /dev/null 2>&1
ExecStart=/usr/bin/vncserver :1 -geometry 1920x1080 -depth 24 -localhost yes
ExecStop=/usr/bin/vncserver -kill :1

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now vncserver@vncuser.service
sudo systemctl status vncserver@vncuser.service

The key flag here is -localhost yes. It binds VNC to 127.0.0.1 only, refusing any direct external connection. Combined with the SSH tunnel on the client side, this closes the "someone found my VNC port with Shodan" class of problem.

Connect from the client via SSH tunnel

From your local machine:

ssh -L 5901:localhost:5901 vncuser@your-server

Leave that session open. Then point your VNC client at localhost:5901. Traffic tunnels through SSH — encrypted, authenticated, and invisible to anyone watching the wire.

Good VNC clients: TightVNC Viewer or TigerVNC Viewer on Windows, remmina or vinagre on Linux, RealVNC Viewer on macOS.

Multiple users

To add a second VNC session for a different user, create the vncuser2 account, initialize the password as above, and enable the same unit with a different instance name:

sudo systemctl enable --now vncserver@vncuser2.service

By convention, first user on :1 (port 5901), second on :2 (5902), etc. Edit the unit's ExecStart to pass :2 for the second one, or just create a separate unit file.

When VNC is the wrong tool

X2Go — if both ends are Linux, X2Go is strictly better than VNC: it uses NX compression (much faster on slow links), runs over SSH natively (no separate tunnel), resumes sessions across disconnects, and handles per-app windowing (not just whole-desktop). sudo apt install x2goserver x2goserver-xsession on the server, the x2goclient on the desktop. Done.

noVNC — if you need zero-install access from any browser, noVNC wraps VNC in a WebSockets proxy you can reach over HTTPS. Good for occasional access from a machine where you can't install a VNC client.

RDP (xrdp) — if your clients are Windows users who already know Remote Desktop, xrdp gives them the familiar RDP experience against a Linux host. Works with Windows' built-in client.

For anything production-facing I'd reach for X2Go or RDP first. VNC's strength is that it's universal — every VNC viewer in the world can connect, and sometimes that's what matters.