GUIDE: Headless Remote Access on Jeston Orin Nano (and other devices using linux nVidia drivers)

  • Note for far in the future readers of this: nVidia from time to time alters their driver options in a way that breaks previously working configurations. This guide was written for a machine running l4t driver version 35.4.1 using driver options that were available in the consumer nVidia driver version 535.113.01 under the assumption they share this part of their code. If you have problems, compare the instructions here and the options valid in that driver version to the instructions for the latest version at nVidia. (Click your version number, then README).

  • Version 35 .4.1 l4t drivers (and all nVidia version 535 drivers at least up to 535.113.01) have a bug that inhibits the use of the NoExtendedGpuCapabilitiesCheck option. This makes it impossible to use headless high resolution modes at the moment. See “Linux 535.xx does not honor ModeValidation, making headless Fullscreen RANDR usage with ConnectedMonitor impossible” If you wish to see this bug squashed, leave a comment letting them know the bug affects you.

  • These are “beta” instructions. Please post any errors you find or any hints you have for others so they don’t have to struggle like you did. ;)

I spent a lot of time figuring out how to do remote access with my Jetson Orin Nano. I know this is a common request, so I thought I’d post a guide for those who came after me.

If you have attempted to use a remote access program headless, you probably have run into the problem of the setup not working if the monitor is disconnected. This is because remote access programs require access to a video buffer that they can copy, and the X11 drivers normally do not create a video buffer if there is no monitor attached.

Luckily, the nVidia drivers provide an easy way to deal with this, but there’s a catch: if you boot the device headless, you will only be able to attach monitors that support the modelines (a line of text in a configuration file describing the exact timing requirements a specific monitor has at a specific resolution) you have previously manually entered during setup, and if you don’t boot headless you either can’t remote in or must use the same monitor you set your modelines to.

The good news is that, unlike some other methods that are possible, you will be able to remote in AND/OR use an external monitor on demand without requiring any mirroring or extra X11 setup.

Step 1. (optional) Move the X11 configuration file for your device to the folder /usr/share/X11xorg.conf.d/
sudo mv /etc/X11/xorg.conf /usr/share/X11/xorg.conf.d/99-Jetson.conf

Why?
X11 (the software subsystem used to interface with the display) searches folders in a specific order for .conf files and, within each of those folders separately, tests .conf files it finds in order alphabetically. It will use the first usable .conf file it finds. If our device is attached to an external monitor at boot that is not compatible with our manually written modelines, we (optionally) want the device to use that monitor’s .conf file. However, the .conf file we will modify will always be usable, thus if it is found first, it will always be used. We need it to be located in the folder that is checked last, with filename of low priority. By convention, a two-digit number at the start of the filename is used to determine the priority of .conf files. See X Org for futher details.
Note: If your X11 configuration file is not at the location shown above, you will need to find it. You can (probably) find the .conf file currently in use with the command:
cat /var/log/Xorg.0.log | grep "\.conf"

Step 2. Identify the modelines for your monitor.

With the monitor you wish to be compatible with headless mode connected, run the command:
xrandr --verbose
At the end of the output will be a series of modes that your monitor supports. Each will look like something like this:
1024x768 (0x18d) 65.000MHz -HSync -VSync +preferred
h: width 1024 start 1048 end 1184 total 1344 skew 0 clock 48.36KHz
v: height 768 start 771 end 777 total 806 clock 60.00Hz
Choose the resolutions you wish to use, and convert them for use in the next step to modelines in a text editor so they look like this:
ModeLine "1024x768_60.00" 65.00 1024 1048 1184 1344 768 771 777 806 -HSync -VSync

Note that the numbers are in the same order and be careful to use the correct signs (+ or -) with both HSync and VSync.

Write down lowest and highest values you find next to “Khz” from all of the modelines, not just the ones you wish to use — this is the horizontal frequency for the mode, and you need the highest and lowest to set the horizontal frequency range in the next step.

Why?
The number of pixels transmitted to the monitor is larger than the visible resolution would suggest. There is invisible padding on the left, right, and paddings for both the horizontal and ‘retrace’ periods. These paddings are necessary for compatibility with CRT displays. For your monitor to be compatible with headless mode you must copy this data exactly for each resolution you wish to use.

The format xrandr uses looks like this:
<resolution> (0x18d) <pixel freqency>MHz <-/+>HSync <-/+>VSync (+other info)
h: width <width> start <width+leftpad> end <width+leftpad+rightpad> total <width+leftpad+rightpad+hRetracePad> skew 0 clock <Horizontal frequency>KHz
v: height <height> start <height+toppad> end <height+toppad+bottompad> total <height+toppad+bottompad+vRetracePad> clock <Refresh rate>Hz

and the modlines format looks like this:
ModeLine "<identifier>" <pixel frequency Mhz> <width> <width+leftpad> <width+leftpad+rightpad> <width+leftpad+hRetracePad> <height> <height+toppad> <height+toppad+bottompad> <height+toppad+bottompad+vRetracePad> <+/->HSync <+/->VSync

Step 3. Edit the X11 configuration file for your device so the monitor is always on.
sudo {your favorite editor} /usr/share/X11/xorg.conf.d/99-Jetson.conf

…and Change Section “Device” to look like this:
Section "Device"
Identifier "<whatever is already here>"
Driver "nvidia"
Option "ModeValidation" "NoVesaModes,NoXServerModes,NoPredefinedModes,NoMaxPClkCheck,NoExtendedGpuCapabilitiesCheck,AllowNonEdidModes"
Option "ModeDebug" "True"
EndSection

Why?
Identifier… is used to identify this block of settings to other settings blocks
Driver… identifies the hardware driver to associate with these settings
Option… NoVesaModes,NoXServerModes,NoPredefinedModes prevents default modelines from being added to the list of modelines for the monitor. These modelines are often incompatible with modern monitors. Normally they are overridden by monitor provided EDID data, but there will be no monitor attached to supply this data while headless.
Option… AllowNonEdidModes allows us to add modelines manually. Without this, our modelines will be ignored.
Option… NoMaxPClkCheck The ConnectedMonitor option tells the driver to assume a monitor is connected, but without EDID from a physical monitor the maximum pixel clock rate is set to an HDMI 1.0 compatible 165 MHz. This option bypasses the pixel clock rate check since we know the monitor is capable of more than 165 Mhz (and the driver will know this too, once the monitor is actually connected.)
Option… NoExtendedGpuCapabilitiesCheck Because the maximum pixel clock rate is set to 165 Mhz (see above), the driver believes it will need to use Display Stream Compression to display resolutions requiring more than 165 Mhz (which is false). Since the card doesn’t support DSC, the “Extended GPU Capability” check would fail, and this option skips that check.
ModeDebug… tells the X11 server to log the reasons used to decide when a modeline is incompatible (and therefore unusable) with the hardware in use and the declared options. This information is in the X11 log:
cat /var/log/Xorg.0.log

Add or change Section “Monitor” to look like this:
Section "Monitor"
Identifier "AlwaysOnMonitor"
ModeLine "1920x1080_60.00" 148.50 1920 2008 2052 2200 1080 1084 1089 1125 +HSync +VSync **EXAMPLE ONLY**
ModeLine "1600x1200_60.00" 162.00 1600 1664 1856 2160 1200 1201 1204 1250 +HSync +VSync **EXAMPLE ONLY**
ModeLine "1280x1024_60.02" 108.00 1280 1328 1440 1688 1024 1025 1028 1066 +HSync +VSync **EXAMPLE ONLY**
ModeLine "1024x768_60.00" 65.00 1024 1048 1184 1344 768 771 777 806 -HSync -VSync **EXAMPLE ONLY*
HorizSync 63-76 **EXAMPLE ONLY**
EndSection

Why?
Identifier “AlwaysOnMonitor” simply identifies this set of options so it can be referenced in other places.
ModeLine … lists the technical requirements of your monitor to display a specific resolution. Add one per resolution you wish to be able to use from Display Settings → Resolution while using the desktop.
HorizSync … tells X11 what the range of horizontal frequencies the monitor can support. Modelines with horizontal frequencies beyond this range will be REJECTED by X11. The horizontal frequency for each mode is shown to you when you use the xrandr --verbose command while creating the list of modelines. Enter a range that goes from the lowest freqeuncy to the highest freqency reported for the entire list, not just the modelines you chose (so if you add another modeline later, it will just work.) Round down to the nearest integer for the low, and up to the nearest integer for the high. I.e., 34.51-91.04 should be entered as 34-92.

Add or change Section “Screen” to look like this:
Section "Screen"
Identifier "Jeston"
Device "<whatever is already here>"
Monitor "AlwaysOnMonitor"
SubSection "Display"
Depth 24
Modes "1600x1200_60.00" **EXAMPLE ONLY - CHOOSE A DEFAULT MODELINE FROM THOSE YOU ENTERED ABOVE **
EndSubSection
Option "ConnectedMonitor" "DFP-1" **ASSUMES JETSON ORIN NANO**
EndSection

Why?
Identifier… simply identifies this set of options so it can be referenced in other places.
Device… identifies the device section above (which references the driver running the actual video “card” hardware)
Monitor… identifies the monitor section above with your modelines (which identifies the “connected physical monitor’s” capabilities)
Depth… sets the color mode to 8 bits (0-255) per color (R, G, B)
Modes… is supposed to be a list of all the modes made available to the user in priority order, but in practice all modelines are made available and whichever one you put here will be the default (the one used at first login.)
Option… ConnectedMonitor tells the nVidia driver that there is a monitor connected to the given port, even if it doesn’t detect one. (This is the magic!)
Note: DFP-1 stands for “Display Flat Panel, port 1”. This is the port used by the Jetson Orin Nano for the external display. You can use the command
xrandr --verbose
to identify what channel your monitor is connected to on your device. You are looking for a line that says something like “DP-1 connected”. Note that while it says “DP-1” (display port 1) is connected, “DFP-1” is still the correct identifier as far as the options are concerned. This is because the same protocol is used for DVI, Display Port, HDMI, etc. See the nVidia driver manual if you are not using a flat panel for other port identifiers. (Choose a driver version, latest is on the bottom, and then the README file.)

Step 4. Install your favorite remote access program

Teamviewer
Easy to use but only free for non-commercial use and might tag you as a commercial user if you connect to often or for too long or for any of many other reasons.
sudo apt install teamviewer-host
NOTE: as of this writing, 15.45.3 was buggy and caused repeated crashes and temporary hangs on the Jetson. Version 15.44.5 was functional. I have not tested 15.46.5. If you encounter problems, you can install another version using:
apt list teamviewer-host -a (get available versions)
sudo apt install teamviewer-host=15.44.5 (use specific version)
sudo apt-mark hold teamviewer-host (optional, prevent automatic upgrade)
sudo apt-mark unhold teamviewer-host (release the hold)

RustDesk
Completely free and very fast when self-hosted using the non-pro version, semi-open-source, and highly recommended, but comes with mild (probably unwarranted?) security concerns for some people, as the company that makes the software is based in China. Once properly set up, runs out-of-the-box with zero issues. Interface is nearly identical to TeamViewer. Written in Rust, a low-resource, fast, and high-stability programming language. For quick performance you must set up a server (self-host), but ideally not on the Jetson. Once you manually connect to any remote host using that host’s RustDesk ID, it will appear in your recently used list and you can then favorite it for future quick access. You can set a permanent password in settings and have it remembed in your client so accessing the jetson is only one click away.
sudo apt install rustdesk

Others
Just about any solution should work, YMMV.

Great! Thanks for your sharing to the community!

Additional note: Consider setting your monitor to never turn off. If the Jetson “turns off the monitor”, it can be a little tricky to get the display back. You may need to unplug and replug the monitor and/or cycle the monitor’s power supply to get the Jetson to wake up the monitor again.