Ubuntu – Forward X11 Works but Can’t Open Display: localhost:11.0

port-forwardingsshUbuntuxorg

On the local network, I have a remote (headless) ubuntu server. I'd like to access the
desktop on that machine, but I really don't want to install vnc, etc… I already have
lxde so why can't I forward x11 and startlxde and be happy? Well, I can,

ssh -X user@server
# and now on the server:
xeyes
# shows up on my pc and
startlxde
# Also shows up on my PC. So there are no issues with permissions or xhost or whatever.
# But then that polutes my PC desktop with the server desktop.
exit 
# I want to "capture" the server x11 display into a window on my PC. 
# Enter Xyfer, said to do exact that. Back on the PC
Xephyr -ac -br -keybd ephyr,,,xkbmodel=pc105,xkblayouts -noreset -screen 800x600 :1 &
# And that starts perfectly. No problem. And I can 
export DISPLAY=:1
xeyes
# and the eyes show up in the Xephyr window. Brilliant! But then when I
ssh -X user@server
export DISPLAY=localhost:11.0
xeyes
# I get:
# Error: Can't open display: localhost:11.0
# and the same for
export DISPLAY=<myPCsIP>:11.0
export DISPLAY=<myPCsIP>:1.0
export DISPLAY=<myPCsIP>:1
export DISPLAY=<myPCsIP>:11
# I also tried sshing with -Y vs -X, and xhost +

EDIT: As per Kenster, the answer is to
export DISPLAY=:2
or whatever Xephyr is running on, BEFORE sshing into the remote system. DISPLAY is then set on the remote system just fine.

I've done rather a lot of searching and reading, but I seem to be swamped with answers to many other questions that are /part/ of this question but not the entire question. e.g. I find gobs of articles on enabling x11 forwarding; but that's already done and works… just not on the :1 display. Searching for forwarding multiple displays finds lots of articles on forwarding through multiple connections. And so on.

Any thoughts? I hope this question and it's eventual answer is educational for others, I'll try to document it well.

As requested, here are log files with -vv. They are sanitized. "laptop" is my local PC, "server" is the remote (local network) machine which is a jetson nano running Ubuntu 20.04. Those user names are not real. "snip" where unrelated stuff has been removed.

Note that my config has changed slightly, as I realised I needed this to work with both my physical monitors plugged in, so I have a local :0 and :1 display, and Xephyr is now running on :2 (as if it were a third monitor). Interestingly enough, I seem to have xauth entries for :1 but still can't connect to it.

jamesn@laptop:~$ ssh -X -vv jetson@server
OpenSSH_8.2p1 Ubuntu-4ubuntu0.7, OpenSSL 1.1.1f  31 Mar 2020


snip


debug1: Next authentication method: password
jetson@server's password: 
debug2: we sent a password packet, wait for reply
debug1: Authentication succeeded (password).
Authenticated to server ([server]:22).
debug1: channel 0: new [client-session]
debug2: channel 0: send open
debug1: Requesting [email protected]
debug1: Entering interactive session.
debug1: pledge: exec
debug1: client_input_global_request: rtype [email protected] want_reply 0
debug2: channel_input_open_confirmation: channel 0: callback start
debug2: x11_get_proto: /usr/bin/xauth  list :1 2>/dev/null
debug1: Requesting X11 forwarding with authentication spoofing.
debug2: channel 0: request x11-req confirm 1
debug2: fd 3 setting TCP_NODELAY
debug2: client_session2_setup: id 0
debug2: channel 0: request pty-req confirm 1
debug1: Sending environment.
debug1: Sending env LANG = en_CA.UTF-8
debug2: channel 0: request env confirm 0
debug2: channel 0: request shell confirm 1
debug2: channel_input_open_confirmation: channel 0: callback done
debug2: channel 0: open confirm rwindow 0 rmax 32768
debug2: channel_input_status_confirm: type 99 id 0
debug2: X11 forwarding request accepted on channel 0
debug2: channel_input_status_confirm: type 99 id 0
debug2: PTY allocation request accepted on channel 0
debug2: channel 0: rcvd adjust 2097152
debug2: channel_input_status_confirm: type 99 id 0
debug2: shell request accepted on channel 0
Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 4.9.253-tegra aarch64)

snip

Last login: Tue Jun 13 20:35:21 2023 from 192.168.0.170
debug1: client_input_channel_open: ctype x11 rchan 3 win 65536 max 16384
debug1: client_request_x11: request from 127.0.0.1 33894
debug2: fd 7 setting O_NONBLOCK
debug1: channel 1: new [x11]
debug1: confirm x11
debug2: channel 1: rcvd adjust 40228
debug2: channel 1: rcvd adjust 49152
debug2: channel 1: rcvd adjust 38112
debug2: channel 1: rcvd adjust 49152
debug2: channel 1: rcvd adjust 49152
debug2: channel 1: rcvd adjust 49152
debug2: channel 1: rcvd adjust 49152
debug2: channel 1: rcvd adjust 49152
debug2: channel 1: rcvd adjust 49152
debug2: channel 1: rcvd adjust 49152
debug2: channel 1: rcvd adjust 49152
X Error of failed request:  BadLength (poly request too large or internal Xlib length error)
  Major opcode of failed request:  156 (NV-GLX)
  Minor opcode of failed request:  1 ()
debug2: channel 1: rcvd eof
debug2: channel 1: output open -> drain
debug2: channel 1: obuf empty
debug2: channel 1: chan_shutdown_write (i0 o1 sock 7 wfd 7 efd -1 [closed])
debug2: channel 1: output drain -> closed
debug1: channel 1: FORCE input drain
debug2: channel 1: ibuf empty
debug2: channel 1: send eof
debug2: channel 1: input drain -> closed
debug2: channel 1: send close
  Serial number of failed request:  19
  Current serial number in output stream:  19
debug2: channel 1: rcvd close
debug2: channel 1: is dead
debug2: channel 1: garbage collecting
debug1: channel 1: free: x11, nchannels 2
jetson@nano:~$ echo $DISPLAY
localhost:10.0
jetson@nano:~$ xeyes
debug1: client_input_channel_open: ctype x11 rchan 3 win 65536 max 16384
debug1: client_request_x11: request from 127.0.0.1 33896
debug2: fd 7 setting O_NONBLOCK
debug1: channel 1: new [x11]
debug1: confirm x11
debug2: channel 1: rcvd eof
debug2: channel 1: output open -> drain
debug2: channel 1: obuf empty
debug2: channel 1: chan_shutdown_write (i0 o1 sock 7 wfd 7 efd -1 [closed])
debug2: channel 1: output drain -> closed
debug1: channel 1: FORCE input drain
debug2: channel 1: ibuf empty
debug2: channel 1: send eof
debug2: channel 1: input drain -> closed
debug2: channel 1: send close
^C
jetson@nano:~$ debug2: channel 1: rcvd close
debug2: channel 1: is dead
debug2: channel 1: garbage collecting
debug1: channel 1: free: x11, nchannels 2

jetson@nano:~$ export DISPLAY=localhost:11.0
jetson@nano:~$ xeyes
Error: Can't open display: localhost:11.0
jetson@nano:~$ export DISPLAY=localhost:12.0
jetson@nano:~$ xeyes
Error: Can't open display: localhost:12.0

Best Answer

If you want ssh to forward X apps to your Xephyr display, then set the DISPLAY environment on the local system to the value for the Xephyr display when running ssh:

DISPLAY=:2 ssh -X user@remote ...

On the remote system, if an ssh session is forwarding X, then the remote session will have a value for DISPLAY variable set by the SSH server handling the session. It shouldn't be necessary to change DISPLAY on the remote system unless you deliberately want to use something other than the forwarded display for that session.

If you want to forward some apps to one display and other apps to a different display, you could run ssh twice:

DISPLAY=:0 ssh -X ...
DISPLAY=:2 ssh -X ...

In this particular case, the two sessions on the remote system will have different values for DISPLAY, for example "localhost:10.0" and "localhost:11.0". Each DISPLAY value would be valid while the corresponding ssh session was running.

So, what's really going on here? Historically, an X client (application) connected to an X server through TCP. The default TCP port for X is port 6000, and a DISPLAY value like ":0" or ":0.0" means port 6000 (6000 + 0) on the local system. A display of "somehost:1.0" means port 6001 (6000 + 1) on host "somehost".

When you run the ssh utility--specifically the OpenSSH version, which is what almost everyone uses--and request X forwarding, ssh will forward X to the X server specified by its DISPLAY variable. DISPLAY can only have one value at a time, so ssh can only forward to one particular X server per invocation.

On the server, the SSH server process will allocate a TCP port to act as the "fake" X server for the session. It normally starts with port 6010, then 6011 and so on until it finds a free port. Once it settles on a port, it listens for connections to that port and forwards them through the SSH connection to the client.

If the SSH server process also starts a command or terminal session, it will set the DISPLAY environment variable for that session to the correct value for the "fake" X server that it set up. This DISPLAY value will probably be "localhost:10.0" or similar. The number in the DISPLAY will depend on the actual TCP port that was allocated--10 means 6010 (6000 + 10). "localhost" means to connect to the system which the application is running on.

Related Question