VNC Server Installation on Linux
TigerVNC is a high-performance VNC server for Linux that provides remote desktop access with X11 display management, making it suitable for both headless servers and desktop systems. This guide covers installing TigerVNC, configuring display sessions, SSH tunneling for security, and creating systemd services for persistent access.
Prerequisites
- Ubuntu 22.04/Debian 12 or CentOS/Rocky 9
- Root or sudo access
- A desktop environment installed (XFCE, GNOME, MATE)
- SSH access to set up tunneling
- A VNC viewer: TigerVNC Viewer, RealVNC, or Remmina
Install TigerVNC Server
# Ubuntu/Debian
sudo apt update
sudo apt install -y tigervnc-standalone-server tigervnc-common
# CentOS/Rocky
sudo dnf install -y tigervnc-server
# Verify installation
vncserver --version
Install a lightweight desktop environment if not already installed:
# XFCE (recommended for VNC - lightweight)
# Ubuntu/Debian
sudo apt install -y xfce4 xfce4-goodies dbus-x11
# CentOS/Rocky
sudo dnf groupinstall -y "Xfce"
# MATE (alternative lightweight option)
sudo apt install -y mate-desktop-environment mate-desktop-environment-extras
# Minimal install for headless servers
sudo apt install -y xfce4 xterm
Configure VNC Display Sessions
VNC sessions use display numbers starting at :1 (port 5901), :2 (port 5902), etc.
Set the VNC password for the user:
# Run as the user who will use VNC (not as root)
vncpasswd
# This creates ~/.vnc/passwd
# Enter a password (up to 8 characters enforced)
# Optionally set a view-only password
# Verify the password file was created
ls -la ~/.vnc/
Start a VNC session manually to test:
# Start VNC server on display :1 (port 5901)
vncserver :1 -geometry 1920x1080 -depth 24
# The server creates a log at ~/.vnc/hostname:1.log
cat ~/.vnc/$(hostname):1.log
# List running VNC sessions
vncserver -list
# Kill a specific session
vncserver -kill :1
Set Up a Desktop Environment
Configure which desktop environment VNC launches via the ~/.vnc/xstartup file:
# Create or edit the xstartup file (run as the VNC user)
cat > ~/.vnc/xstartup << 'EOF'
#!/bin/bash
# VNC startup script - runs when a VNC session starts
# Fix common issues
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
# Export required variables
export XDG_CURRENT_DESKTOP=XFCE
export XDG_SESSION_TYPE=x11
# Start D-Bus if not running
if [ -z "$DBUS_SESSION_BUS_ADDRESS" ]; then
eval $(dbus-launch --sh-syntax --exit-with-session)
fi
# Start the window manager
if command -v startxfce4 >/dev/null 2>&1; then
exec startxfce4
elif command -v mate-session >/dev/null 2>&1; then
exec mate-session
elif command -v gnome-session >/dev/null 2>&1; then
exec gnome-session
else
# Fallback to basic window manager with terminal
exec xterm &
exec openbox
fi
EOF
chmod +x ~/.vnc/xstartup
For GNOME on Ubuntu (requires additional setup):
cat > ~/.vnc/xstartup << 'EOF'
#!/bin/bash
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
export GNOME_SHELL_SESSION_MODE=ubuntu
export XDG_CURRENT_DESKTOP=ubuntu:GNOME
export XDG_SESSION_TYPE=x11
exec /usr/lib/gnome-session/gnome-session-binary --session=ubuntu
EOF
chmod +x ~/.vnc/xstartup
Test the desktop environment:
# Start VNC and verify it's running
vncserver :1 -geometry 1280x800 -depth 24
vncserver -list
SSH Tunneling for Security
Never expose VNC ports directly to the internet. Always use SSH tunneling:
From a Linux/macOS client:
# Create an SSH tunnel - forward local port 5901 to remote 5901
ssh -L 5901:localhost:5901 -N user@your-server-ip &
# Now connect your VNC viewer to localhost:5901
# The connection is encrypted through the SSH tunnel
# For multiple displays:
ssh -L 5901:localhost:5901 \
-L 5902:localhost:5902 \
-N user@your-server-ip &
# Use a persistent tunnel with autossh
sudo apt install -y autossh
autossh -M 20000 -f -N -L 5901:localhost:5901 user@your-server-ip
From Windows using PuTTY:
- Open PuTTY, enter server IP and SSH port 22
- Go to Connection > SSH > Tunnels
- Source port:
5901, Destination:localhost:5901 - Click Add, then Open
- Connect VNC viewer to
localhost::5901
From Windows using PowerShell/OpenSSH:
# Windows 10+ with built-in SSH
ssh -L 5901:localhost:5901 -N user@your-server-ip
# Then connect VNC viewer to localhost::5901
Block direct VNC access in the firewall:
# Ubuntu - deny direct VNC access, allow SSH only
sudo ufw deny 5900:5910/tcp # Block VNC ports
sudo ufw allow 22/tcp # Allow SSH (tunnel)
# CentOS/Rocky
sudo firewall-cmd --permanent --remove-port=5901/tcp
sudo firewall-cmd --reload
Create a Systemd Service
Create a user-level systemd service for persistent VNC sessions:
# Create the systemd user service directory
mkdir -p ~/.config/systemd/user/
cat > ~/.config/systemd/user/[email protected] << 'EOF'
[Unit]
Description=TigerVNC remote desktop server display %i
After=network.target
[Service]
Type=forking
ExecStartPre=/bin/sh -c '/usr/bin/vncserver -kill :%i > /dev/null 2>&1 || true'
ExecStart=/usr/bin/vncserver :%i \
-geometry 1920x1080 \
-depth 24 \
-localhost yes \
-SecurityTypes VncAuth
ExecStop=/usr/bin/vncserver -kill :%i
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
EOF
# Enable lingering so user services start at boot (without login)
sudo loginctl enable-linger $USER
# Enable and start VNC on display :1
systemctl --user enable [email protected]
systemctl --user start [email protected]
systemctl --user status [email protected]
# Check logs
journalctl --user -u [email protected] -n 50
For a system-wide VNC service (for a specific user):
# Create system-level service that runs VNC for user 'alice'
sudo tee /etc/systemd/system/[email protected] << 'EOF'
[Unit]
Description=TigerVNC remote desktop server display %i
After=syslog.target network.target
[Service]
Type=forking
User=alice
Group=alice
WorkingDirectory=/home/alice
ExecStartPre=/bin/sh -c '/usr/bin/vncserver -kill :%i > /dev/null 2>&1 || true'
ExecStart=/usr/bin/vncserver :%i \
-geometry 1920x1080 \
-depth 24 \
-localhost yes
ExecStop=/usr/bin/vncserver -kill :%i
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now [email protected]
Multi-User VNC Access
To run VNC sessions for multiple users simultaneously, each user gets their own display number:
# User alice - display :1, port 5901
sudo -u alice vncpasswd
sudo -u alice vncserver :1
# User bob - display :2, port 5902
sudo -u bob vncpasswd
sudo -u bob vncserver :2
# List all sessions from all users
ps aux | grep Xvnc
# Create a per-user systemd service for each
sudo tee /etc/systemd/system/[email protected] << 'EOF'
[Unit]
Description=VNC Server for alice (display 1)
After=network.target
[Service]
Type=forking
User=alice
ExecStartPre=-/usr/bin/vncserver -kill :1
ExecStart=/usr/bin/vncserver :1 -geometry 1280x800 -depth 24 -localhost yes
ExecStop=/usr/bin/vncserver -kill :1
[Install]
WantedBy=multi-user.target
EOF
Security Best Practices
# 1. Always use -localhost flag (only listen on loopback)
vncserver :1 -localhost yes
# 2. Verify VNC only listens on localhost
ss -tlnup | grep -E "590[0-9]"
# Should show 127.0.0.1:5901, NOT 0.0.0.0:5901
# 3. Set a strong VNC password (up to 8 chars, use SSH as primary auth)
vncpasswd
# 4. Use NLA (for XRDP) or SSH keys (for VNC) rather than password auth
# Disable VNC password auth and require SSH key for tunnel:
# In ~/.vnc/config:
echo "SecurityTypes=None" >> ~/.vnc/config # No VNC password - rely on SSH auth for tunnel
# 5. Set session idle timeout
# In xstartup, add:
# xautolock -time 10 -locker 'vncserver -kill $DISPLAY' &
# 6. Limit SSH tunnel access to specific users
# In /etc/ssh/sshd_config:
# AllowUsers alice bob
# PermitTunnel yes
Troubleshooting
"Connection refused" when connecting:
# Check if VNC server is running
vncserver -list
ps aux | grep Xvnc
# Check what port it's listening on
ss -tlnup | grep 5901
# Restart the session
vncserver -kill :1
vncserver :1
# Check firewall (should BLOCK 5901 externally if using SSH tunnel)
sudo ufw status
Black screen on connection:
# Check xstartup permissions
chmod +x ~/.vnc/xstartup
# Check VNC session log
cat ~/.vnc/$(hostname):1.log
# Start with verbose logging
vncserver :1 -verbose -geometry 1280x800
# Test with a minimal xstartup
cat > ~/.vnc/xstartup << 'EOF'
#!/bin/bash
xterm &
exec openbox
EOF
Session freezes or disconnects frequently:
# Increase VNC server verbosity to diagnose
vncserver -kill :1
vncserver :1 -verbose
# Check network quality (SSH tunnel drops?)
# Add ServerAlive settings to SSH config:
echo "ServerAliveInterval 30" >> ~/.ssh/config
echo "ServerAliveCountMax 3" >> ~/.ssh/config
"Authentication is required to create a color managed device" GNOME popup:
# This is a polkit issue with GNOME over VNC
# Disable the colord check:
sudo tee /etc/polkit-1/localauthority/50-local.d/45-allow-colord.pkla << 'EOF'
[Allow Colord all Users]
Identity=unix-user:*
Action=org.freedesktop.color-manager.create-device;org.freedesktop.color-manager.create-profile;org.freedesktop.color-manager.delete-device;org.freedesktop.color-manager.delete-profile;org.freedesktop.color-manager.modify-device;org.freedesktop.color-manager.modify-profile
ResultAny=no
ResultInactive=no
ResultActive=yes
EOF
Conclusion
TigerVNC provides reliable remote desktop access to Linux servers with support for multiple simultaneous sessions and flexible desktop environment configuration. The most important security practice is always running VNC with -localhost yes and accessing it exclusively through SSH tunnels — never expose VNC ports directly to the internet. Use systemd user services with loginctl enable-linger to ensure VNC sessions automatically start and persist across server reboots without requiring manual login.


