Ubuntu Multi-User Concurrent Remote Desktop Deployment

This document provides a reproducible Ubuntu-focused deployment workflow. The core objective is to enable concurrent multi-user remote desktops: services recover automatically after reboot, different accounts can connect simultaneously to isolated GNOME sessions, and Windows clients can connect reliably by account and display number.

What TurboVNC and VirtualGL Do

  • VNC (Virtual Network Computing): a remote desktop protocol based on the Remote Frame Buffer (RFB), which encodes and transmits the server-side graphical session to the client and sends keyboard/mouse input events back to the server.
  • TurboVNC: the “transport and session manager” for remote desktops. It compresses and streams the server desktop to clients and maintains isolated sessions for different users.
  • VirtualGL: a “3D acceleration bridge.” When OpenGL-dependent applications run inside remote desktops, it ensures the 3D rendering path is handled correctly, reducing lag, black screens, and major performance loss caused by pure software rendering.

Connection UI Example

TurboVNC Connection UI Example

1. Objectives

After completing this guide, you will have:

  • TurboVNC auto-starting after Ubuntu reboots (without GUI login)
  • Multiple Linux accounts concurrently using isolated remote desktops on the same server
  • Multiple VNC sessions per account (different display numbers/ports) without interference
  • Stable Windows client access to the corresponding GNOME desktop by account
  • Basic Linux commands: be comfortable with cd, mkdir, chmod, systemctl, and journalctl.
  • Ubuntu user model: understand regular users, sudo privileges, and the $HOME directory.
  • Basic networking: understand IP and ports, and distinguish :2 (display number) from 5902 (VNC port).
  • Remote access basics: be able to install and use a VNC Viewer on Windows.

2. Applicable Environment

  • Ubuntu 22.04
  • VNC display number: :2
  • VNC port: 5902
  • Installation mode: rootless in user space (no writes to system /opt)

4. One-Time Deployment Steps

4.1 Create directories and download packages

mkdir -p ~/.local/src ~/.local/opt/rootfs ~/.local/bin ~/.vnc ~/.config/systemd/user ~/.cache

curl -L 'https://sourceforge.net/projects/turbovnc/files/3.1/turbovnc_3.1_amd64.deb/download' -o ~/.local/src/turbovnc_3.1_amd64.deb
curl -L 'https://sourceforge.net/projects/virtualgl/files/3.1/virtualgl_3.1_amd64.deb/download' -o ~/.local/src/virtualgl_3.1_amd64.deb

4.2 Extract into user-space directory (rootless)

dpkg-deb -x ~/.local/src/turbovnc_3.1_amd64.deb ~/.local/opt/rootfs
dpkg-deb -x ~/.local/src/virtualgl_3.1_amd64.deb ~/.local/opt/rootfs

4.3 Create wrapper launch scripts

Write to ~/.local/bin/turbovnc-vncserver:

cat > ~/.local/bin/turbovnc-vncserver <<'EOF1'
#!/usr/bin/env 
set -euo pipefail
TVNC_HOME="$HOME/.local/opt/rootfs/opt/TurboVNC"
exec "$TVNC_HOME/bin/vncserver" "$@"
EOF1
chmod +x ~/.local/bin/turbovnc-vncserver

Write to ~/.local/bin/turbovnc-vncpasswd:

cat > ~/.local/bin/turbovnc-vncpasswd <<'EOF1'
#!/usr/bin/env 
set -euo pipefail
TVNC_HOME="$HOME/.local/opt/rootfs/opt/TurboVNC"
exec "$TVNC_HOME/bin/vncpasswd" "$@"
EOF1
chmod +x ~/.local/bin/turbovnc-vncpasswd

Write to ~/.local/bin/turbovnc-vncviewer:

cat > ~/.local/bin/turbovnc-vncviewer <<'EOF1'
#!/usr/bin/env 
set -euo pipefail
TVNC_HOME="$HOME/.local/opt/rootfs/opt/TurboVNC"
exec "$TVNC_HOME/bin/vncviewer" "$@"
EOF1
chmod +x ~/.local/bin/turbovnc-vncviewer

Write to ~/.local/bin/vglrun-local:

cat > ~/.local/bin/vglrun-local <<'EOF1'
#!/usr/bin/env 
set -euo pipefail
VGL_HOME="$HOME/.local/opt/rootfs/opt/VirtualGL"
VGL_LIB="$HOME/.local/opt/rootfs/usr/lib"
exec "$VGL_HOME/bin/vglrun" -ld "$VGL_LIB" "$@"
EOF1
chmod +x ~/.local/bin/vglrun-local

4.4 Configure GNOME session inside VNC

Write to ~/.vnc/xstartup:

cat > ~/.vnc/xstartup <<'EOF1'
#!/bin/sh
set -eu
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
unset DESKTOP_SESSION
unset GDMSESSION
unset XDG_SESSION_DESKTOP
export XDG_SESSION_TYPE=x11
export GNOME_SHELL_SESSION_MODE=ubuntu
exec dbus-launch --exit-with-session gnome-session --session=ubuntu
EOF1
chmod +x ~/.vnc/xstartup

4.5 Set the VNC password

turbovnc-vncpasswd

4.6 Configure systemd user service (auto-start entrypoint)

Write to ~/.config/systemd/user/turbovnc.service:

cat > ~/.config/systemd/user/turbovnc.service <<'EOF1'
[Unit]
Description=TurboVNC Server Session (:2)
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStartPre=/usr/bin/rm -f /tmp/.X2-lock /tmp/.X11-unix/X2
ExecStart=%h/.local/bin/turbovnc-vncserver :2 -fg -geometry 1920x1080 -depth 24 -xstartup %h/.vnc/xstartup -noautokill
ExecStop=%h/.local/bin/turbovnc-vncserver -kill :2
Restart=always
RestartSec=2

[Install]
WantedBy=default.target
EOF1

Enable and start:

systemctl --user daemon-reload
systemctl --user enable turbovnc.service
systemctl --user restart turbovnc.service

4.7 Enable linger (ensure startup after reboot without login)

Check first:

loginctl show-user "$USER" -p Linger

If the output is not Linger=yes, run (requires admin privileges):

sudo loginctl enable-linger "$USER"

4.8 Add @reboot fallback (optional)

Create fallback script:

cat > ~/.local/bin/turbovnc-autostart-ensure <<'EOF1'
#!/usr/bin/env 
set -euo pipefail
LOG="$HOME/.cache/turbovnc-autostart.log"
mkdir -p "$HOME/.cache"
{
  echo "===== $(date '+%F %T') reboot ensure ====="
  if systemctl --user is-active --quiet turbovnc.service; then
    echo "turbovnc.service already active"
  else
    echo "starting turbovnc.service"
    systemctl --user start turbovnc.service || true
  fi
} >>"$LOG" 2>&1
EOF1
chmod +x ~/.local/bin/turbovnc-autostart-ensure

Write to crontab:

(crontab -l 2>/dev/null | grep -v 'turbovnc-autostart-ensure' ; \
 echo '@reboot /bin/ -lc "sleep 25; $HOME/.local/bin/turbovnc-autostart-ensure"') | crontab -

5. Post-Deployment Verification

5.1 Check service state

systemctl --user is-enabled turbovnc.service
systemctl --user is-active turbovnc.service

Expected output:

  • enabled
  • active

5.2 Check port listening

ss -ltnp | grep 5902

Expected: 0.0.0.0:5902 is listened by Xvnc.

5.3 Verify after reboot

sudo reboot

After system startup (GUI login not required), connect from another device:

  • <Ubuntu_IP>:5902

6. Windows Client Connection Steps

6.1 Install a client

Recommended: TurboVNC Viewer (or TigerVNC Viewer).

6.2 Enter connection address

  • <Ubuntu_IP>:5902
  • or <Ubuntu_IP>:2

6.3 Login

Enter the password set in turbovnc-vncpasswd.

7. Common Operations Commands

# Check status
systemctl --user status turbovnc.service

# Restart service
systemctl --user restart turbovnc.service

# Stop service
systemctl --user stop turbovnc.service

# Manually start/stop a display
~/.local/bin/turbovnc-vncserver :2 -geometry 1920x1080 -depth 24 -xstartup ~/.vnc/xstartup -noautokill
~/.local/bin/turbovnc-vncserver -kill :2

# View logs
journalctl --user -u turbovnc.service -n 200 --no-pager
tail -n 200 ~/.vnc/localhost:2.log

8. FAQ

8.1 http://<Ubuntu_IP>:5902 does not open

5902 is a VNC (RFB) port, not an HTTP web port. Use a VNC client, not a browser.

8.2 Windows cannot connect

Troubleshoot in order:

  1. Check service status: systemctl --user status turbovnc.service
  2. Check port listening: ss -ltnp | grep 5902
  3. Check current IP: hostname -I
  4. If firewall is enabled, allow 5902/TCP

8.3 Black screen or frozen session

systemctl --user restart turbovnc.service

Reconnect once.

8.4 Conflict with GNOME Remote Desktop

If gnome-remote-desktop.service exists in the session, it may conflict with TurboVNC.

Diagnostics:

  1. systemctl --user status gnome-remote-desktop.service
  2. ps -ef | rg -i "gnome-remote-desktop|vncserver|Xvnc" | rg -v rg
  3. ss -ltnp | rg "3389|5902"

Fix:

  1. systemctl --user disable --now gnome-remote-desktop.service
  2. systemctl --user mask gnome-remote-desktop.service
  3. gsettings set org.gnome.desktop.remote-desktop.rdp enable false
  4. gsettings set org.gnome.desktop.remote-desktop.vnc enable false
  5. systemctl --user restart turbovnc.service

8.5 Shortcuts intercepted by local machine (for example Ctrl+Alt+T)

This is caused by TurboVNC Viewer keyboard grab mode. Add this parameter to the connection command:

turbovnc-vncviewer -grabkeyboard always 127.0.0.1:2

Or write the following in ~/.vnc/default.turbovnc:

GrabKeyboard=Always

9. VirtualGL Usage Notes (Optional)

Run 3D applications in the VNC session:

vglrun-local <your-3d-app>

For full GPU authorization policy (vglserver_config) and system-level optimization, additional administrator-level configuration is required.

10. Key File Paths

  • ~/.config/systemd/user/turbovnc.service
  • ~/.vnc/xstartup
  • ~/.local/bin/turbovnc-vncserver
  • ~/.local/bin/turbovnc-vncpasswd
  • ~/.local/bin/turbovnc-vncviewer
  • ~/.local/bin/vglrun-local
  • ~/.local/bin/turbovnc-autostart-ensure