x11vnc Setup: Share the X Desktop That’s Already Running

x11vnc doesn't start a desktop. It mirrors the X session that's already on the monitor, so the viewer drives the real screen, keyboard and mouse and whoever sits at the machine watches every click. On the host that comes down to four moves: make sure the session is Xorg, run x11vnc against the session's display, usually :0, with that session's Xauthority file, give it a password, and reach it through an SSH tunnel. Once the foreground run works, the same options go into a systemd unit so x11vnc is up at the login screen and after a reboot.

Two secrets get mixed up here. The Xauthority cookie is what x11vnc itself needs to open :0, and without it x11vnc exits immediately. The VNC password is what the viewer types when it connects to the port x11vnc opens, TCP 5900 (the viewer calls that :0, and 5901 :1). Get the cookie right first, then add the password.

One thing to weigh before you build on it: x11vnc is unmaintained and looking for a new maintainer. It still does the job, but on a host you'll keep for years, TigerVNC's x0vncserver shares the same kind of desktop from a maintained project.

Make sure the desktop runs on Xorg, not Wayland

Ubuntu 24.04's GDM can log you into a Wayland session, and x11vnc only attaches to real X11 displays. Start it from a terminal inside a Wayland session and it prints Wayland display server detected. and exits. So check first. In a terminal on the desktop, the session type comes back as x11 or wayland:

make sure the desktop runs on xorg, not wayland · bash
echo $XDG_SESSION_TYPE

From an SSH shell, find the desktop session's ID and ask for its Type property:

make sure the desktop runs on xorg, not wayland · bash
loginctl list-sessions
loginctl show-session <session-id> -p Type

If you get wayland, switch GDM over. /etc/gdm3/custom.conf ships with #WaylandEnable=false commented out in its [daemon] section, and uncommenting it forces the login screen onto Xorg. Restarting GDM kills the desktop session that's running, unsaved work included, so save first:

make sure the desktop runs on xorg, not wayland · bash
sudo sed -i 's/^#WaylandEnable=false/WaylandEnable=false/' /etc/gdm3/custom.conf
sudo systemctl restart gdm3

Log back in and run the echo check again. You want x11.

Install x11vnc on the host

x11vnc goes on the machine with the monitor, next to the X server it shares. The machine you connect from only needs a VNC viewer. On Ubuntu it's one package:

install x11vnc on the host · bash
sudo apt-get install x11vnc

Run it once in the foreground

Do the first run as the user logged into the desktop, from a terminal on that desktop. Check which display the session is on before you assume :0. GDM starts Xorg on the first free display number, so a session can land on :1. -finddpy prints a line like DISPLAY=:0.0 and exits, and that's the number to put after -display from here on. Then attach:

run it once in the foreground · bash
x11vnc -finddpy
x11vnc -display :0

There's no password yet, so x11vnc opens with a WARNING banner around YOU ARE RUNNING X11VNC WITHOUT A PASSWORD!!. Take it literally: while this test runs, anyone who can reach port 5900 on the host gets your desktop. Keep it short, and set the password in the next step before you leave anything running.

A good start ends with a The VNC desktop is: line naming your host and :0, then PORT=5900, and x11vnc sits waiting for a viewer. Without -forever it exits as soon as that viewer disconnects, which is what you want from a test. If something else already holds 5900, another VNC server for instance, x11vnc moves on and prints PORT=5901, and the viewer then needs :1.

When x11vnc stops instead with *** XOpenDisplay failed (:0) and a line saying it was unable to open the X DISPLAY, it couldn't get into the session. That's what you get from an SSH shell or as root when the session's cookie isn't where x11vnc looks. Let x11vnc find it: -findauth prints XAUTHORITY=/path/to/file for the display, and that path goes to -auth:

run it once in the foreground · bash
x11vnc -findauth :0
x11vnc -auth <xauthority-file> -display :0

As root, -auth guess runs the same search in one step and falls back to the display manager's cookie when nobody has logged in yet. That fallback is what lets the systemd unit further down come up at the login screen.

Give it a password

A laptop on a desk connected to a package cube, showing the X session and package requirement for x11vnc setup.

x11vnc won't ask for a VNC password unless you tell it to. Run -storepasswd with no argument and it prompts Enter VNC password: and Verify password:, asks before it writes ~/.vnc/passwd, and answers with Password written to: and the path. Then point -rfbauth at that file:

give it a password · bash
x11vnc -storepasswd
x11vnc -rfbauth ~/.vnc/passwd -display :0

The WARNING banner is gone now, and the viewer gets a password prompt before it sees anything. The file is obscured with a fixed key, not encrypted, so treat it like the password itself and keep it readable by the account that runs x11vnc and nobody else.

The systemd unit runs as root and shouldn't depend on anyone's home directory, so give it its own file. Pass the path as the one argument to -storepasswd, then lock it down:

give it a password · bash
sudo x11vnc -storepasswd /etc/x11vnc.passwd
sudo chmod 600 /etc/x11vnc.passwd

Connect through an SSH tunnel, or directly on a LAN

Two-column labelled diagram comparing the x11vnc command with and without the -auth flag, showing the process exiting on the left and continuing on the right.

Use the tunnel unless the viewer sits on a LAN you trust. The password only decides who gets in, and x11vnc leaves encrypting the traffic to you. With -localhost it listens on loopback only, so nothing on the network sees port 5900 and SSH carries the session. For on-demand access to a desktop you're logged into, one command from your workstation opens the tunnel and starts x11vnc at the far end:

connect through an ssh tunnel, or directly on a lan · bash
ssh -t -L 5900:localhost:5900 <user>@<server-host> 'x11vnc -localhost -rfbauth ~/.vnc/passwd -display :0 -auth guess'

The -auth guess is what makes this work from SSH. GDM writes the session's cookie to /run/user/<uid>/gdm/Xauthority and sets XAUTHORITY only inside the desktop session, so an SSH shell doesn't know where it is.

In a second terminal on the workstation, point the viewer at your end of the tunnel. Local port 5900 is display :0 to the viewer, because the viewer's display number is the port minus 5900:

connect through an ssh tunnel, or directly on a lan · bash
vncviewer localhost:0

You get the password prompt, then the desktop exactly as it looks on the monitor. If your workstation runs its own VNC server on 5900, forward 5901:localhost:5900 instead and connect to localhost:1, or skip the arithmetic with the host::port form.

For a direct connection on the LAN, drop -localhost from the x11vnc command and let your client subnet, and only that, in on 5900 with a source-scoped ufw rule. On the server:

connect through an ssh tunnel, or directly on a lan · bash
sudo ufw allow proto tcp from <client-subnet> to any port 5900

Then run sudo ufw status before you trust that rule. Ubuntu installs ufw disabled, and a rule added to a disabled firewall filters nothing until you run sudo ufw enable. Working over SSH? Add sudo ufw allow 22/tcp before you enable it, so the firewall doesn't lock you out of your own session.

From the workstation:

connect through an ssh tunnel, or directly on a lan · bash
vncviewer <server-host>:0

Either way, check what x11vnc bound to with ss. 127.0.0.1:5900 means -localhost took effect; 0.0.0.0:5900 or *:5900 means every interface:

connect through an ssh tunnel, or directly on a lan · bash
sudo ss -ltnp | grep 5900

Keep it running with systemd

The one-liner is right for the odd session on a desktop you're already logged into. When x11vnc has to be there at the login screen, after a reboot and after every disconnect, give it a unit. It runs as root, so -auth guess picks up the greeter's cookie before anyone logs in. -forever keeps it listening after a viewer leaves, and Restart=on-failure brings it back when the process dies, for example when the X server restarts. Save this as /etc/systemd/system/x11vnc.service:

keep it running with systemd · ini
[Unit]
Description=x11vnc on display :0
After=display-manager.service

[Service]
ExecStart=/usr/bin/x11vnc -forever -localhost -display :0 -auth guess -rfbauth /etc/x11vnc.passwd
Restart=on-failure

[Install]
WantedBy=graphical.target

display-manager.service in After= lines it up behind GDM's unit, and WantedBy=graphical.target starts it along with the graphical login. If you took the direct LAN route, drop -localhost from ExecStart. Then load, enable and start it, and check:

keep it running with systemd · bash
sudo systemctl daemon-reload
sudo systemctl enable --now x11vnc.service
systemctl status x11vnc.service
journalctl -u x11vnc.service -b
sudo ss -ltnp | grep 5900

status should read active (running), the unit's journal should end with PORT=5900, and ss should show x11vnc on the loopback address. Before anyone has logged in, the viewer lands on the login screen. Connect through a plain tunnel, where -N forwards the port without running anything on the far end. Then reboot once and connect again before you count on it:

keep it running with systemd · bash
ssh -N -L 5900:localhost:5900 <user>@<server-host>
vncviewer localhost:0

When the viewer doesn't show your desktop

What you're after is the viewer showing the same desktop as the monitor, not a fresh one. When the unit is the thing misbehaving, don't debug it through systemd: stop it, run the ExecStart line by hand as root and read what comes back. Then match what you see:

  • *** XOpenDisplay failed (:0) right after start. x11vnc can't open the display: the cookie is missing or wrong, or the session is Wayland. Run echo $XDG_SESSION_TYPE on the desktop, then x11vnc -findauth :0 and pass the path with -auth.
  • Wayland display server detected. The session is Wayland. Switch GDM to Xorg as in the first step and log in again.
  • PORT=5901 instead of 5900. Another program holds 5900. sudo ss -ltnp | grep 5900 names it; stop it or connect to :1.
  • systemctl status shows failed. The ExecStart line, the password-file path or the display was wrong at start. journalctl -u x11vnc.service -b shows x11vnc's last lines; fix the unit, then sudo systemctl daemon-reload and sudo systemctl restart x11vnc.service.
  • The viewer can't connect at all. The listener is on loopback (-localhost) or the firewall is closed. Check the bound address with sudo ss -ltnp | grep 5900, then use the tunnel, or drop -localhost and add the ufw rule.
  • The viewer shows the login screen or another session, not the desktop you're logged into. x11vnc opened a different display. Run x11vnc -finddpy as the desktop user and put that display in -display, in the unit too.
  • No password prompt. x11vnc started without -rfbauth, and the WARNING banner in its output gives it away. Add -rfbauth with the stored file and restart.

Stop it and close the port

A foreground run without -forever exits when its viewer disconnects, and Ctrl+C stops it at any time, in its own terminal or in the SSH terminal of the tunnel one-liner. For the unit, stop ends it until the next boot and disable --now takes it out of startup as well. Once the listener is gone, the ss check prints nothing:

stop it and close the port · bash
sudo systemctl stop x11vnc.service
sudo systemctl disable --now x11vnc.service
sudo ss -ltnp | grep 5900

If you opened the firewall for a direct connection, delete the rule too:

stop it and close the port · bash
sudo ufw delete allow proto tcp from <client-subnet> to any port 5900

What changes on Debian 13

Debian 13 ships x11vnc 0.9.17-1 against Ubuntu 24.04's 0.9.16-10, with the same options, so the install, x11vnc, systemd and ss steps carry over as written. The GDM step doesn't. Debian builds GDM to read /etc/gdm3/daemon.conf instead of custom.conf, so point the sed at that file. The #WaylandEnable=false line under [daemon] is the same, and systemctl restart gdm3 still works because Debian links gdm3.service to gdm.service.

A second admin who wants to watch alongside you needs -shared, because x11vnc won't share the screen between viewers by default. Add -shared to ExecStart, then run sudo systemctl daemon-reload and sudo systemctl restart x11vnc.service.

FAQs

Can x11vnc read its options from a file?

Yes. If $HOME/.x11vncrc exists, x11vnc reads each line as one command-line option, with or without the leading dash. Put forever and localhost on their own lines and you stop typing them; -norc skips the file for one run.

Can x11vnc find the user's display by itself?

Yes. Start it with -find in place of -display :0. It's short for -display WAIT:cmd=FINDDISPLAY: x11vnc waits until a viewer connects, then looks up the user's display and Xauthority, so the command never has to name :0.