I have written a blog for fully headless setup, covering NVMe SSD flashing and two stable ways to get remote GUI access: GNOME via x11vnc and XFCE fallback. Blog link: Jetson Orin Nano Headless Setup: NVMe Boot, SDK Flashing, and VNC (GNOME & XFCE) - Nandan Manjunatha
Copy-pasting my blog content so that the info is not lost in the future:
This post walks through SSD-based flashing using NVIDIA SDK Manager with a host<->target model, then covers two proven strategies for remote desktop access: a GNOME setup using x11vnc, and a lightweight XFCE fallback using TigerVNC. Expect explicit steps, rationale for each design choice, and configurations that survive reboots and updates, no monitor, no keyboard, and no fragile shortcuts.
Audience & Assumptions:
This guide assumes familiarity with Linux, SSH, and systemd. It is written for developers running Jetson Orin Nano headlessly (no monitor/keyboard) who want reliable remote GUI access without fragile workarounds.
Part 1:
Flashing OS onto Jetson Headlessly with an SSD
This section explains how to flash and boot an NVMe SSD on a Jetson Orin Nano without attaching a monitor or keyboard. The process uses NVIDIA SDK Manager , which enforces a strict host–target model. Confusing the two is the most common cause of failure, so we state them clearly.
Before proceeding, I would recommend to watch this setup guide from DronBot Workshop on YouTube, which covers the same ground visually: YouTube Link . Even I followed the same until acquiring the IP address of the Jetson after flashing.
Host and Target Overview
Host Machine (Required)
-
Purpose: Flashes the Jetson and installs NVIDIA SDK components
-
System: Ubuntu 20.04 or 22.04 (x86_64 PC or laptop)
-
Software: NVIDIA SDK Manager
-
Connectivity: USB-C (data cable) during flashing, Ethernet used later for SDK installation
Target Device (Jetson)
-
Device: NVIDIA Jetson Orin Nano Developer Kit
-
Storage: NVMe SSD (M.2 2230 or 2280, PCIe Gen3 only; minimum 256 GB recommended)
-
Flashing state: Recovery mode
-
Peripherals: No display or keyboard required (headless)
-
Networking: Ethernet required after flashing
The host only orchestrates the process. The target is the device being flashed and configured.
Step 1: Install NVMe SSD on the Target
Power off the Jetson Orin Nano. Install the NVMe SSD into one of the M.2 slots on the underside of the carrier board and secure it with the provided screw.
This SSD will become the boot device. No microSD card is involved.
Step 2: Put the Target into Recovery Mode
To allow the host to flash the target, recovery mode is mandatory.
-
Locate the 14-pin button header on the Jetson carrier board.
-
Short FC REC and GND (pins 2 and 3) using a jumper.
-
Leave the jumper connected.
-
Power on the Jetson.
If recovery mode is not active, the host will not detect the target.
Step 3: Prepare the Host Machine
On the host (Ubuntu PC):
-
Install NVIDIA SDK Manager (
.debpackage). -
Disable sleep and screen blanking.
-
Ensure internet connectivity.
Nothing in this step affects the Jetson directly.
Step 4: Flash the Target from the Host
All actions below happen on the host, but they operate on the target.
-
Launch SDK Manager on the host and log in.
-
Connect the Jetson (in recovery mode) to the host using USB-C.
-
Power on the Jetson.
-
SDK Manager should detect the device:
- Select Jetson Orin Nano 8GB Developer Kit
-
Accept the license agreement.
-
Click Download and Flash.
-
When prompted:
-
Choose Preconfig (recommended for headless use), or
-
Choose Runtime if you prefer first-boot setup.
-
-
Select
nvmeas the target storage device. -
Start flashing and wait.
Do not disconnect power or USB. If this fails, you start over.
Step 5: First Boot and Network-Based SDK Installation
-
After flashing completes:
-
Power off the Jetson
-
Remove the recovery jumper. I used female dupont connectors for easy handling.
-
-
Power on the Jetson normally (no USB flashing cable required anymore).
-
Connect the Jetson to your network using Ethernet. This is mandatory, the remaining SDK components are installed over the network.
-
Determine the Jetson’s IP address without using a monitor or keyboard.
Since this is a headless setup, do not rely on local login. Use one of the following host-side methods:
Scan the local subnet (recommended)
- On the host, identify the active Ethernet interface and subnet:
ip addr show eth0Example output:
inet 10.42.0.1/24- Scan the subnet for live devices: Install
nmapif not already present:sudo apt install nmap
nmap -sn 10.42.0.0/24Identify the Jetson by vendor or as a newly appeared IP (typically
10.42.0.xxx). -
Return to NVIDIA SDK Manager on the host.
-
Provide:
-
Target IP address
-
Target username
-
Target password
-
-
Resume and complete SDK component installation.
Once this step finishes, the Jetson is fully operational and no longer depends on the host.
Note: The same IP address is used for SSH, SCP, and VNC access going forward.
Part 2:
Setting Up VNC for Remote Desktop Access * GNOME Edition
Running GNOME headlessly on a Jetson-class device is non-trivial. GNOME expects a real display, valid EDID, and an active login session. GNOME cannot run headlessly without a valid display pipeline. This setup mirrors the real :0 display using x11vnc, which is the only method that remains stable across reboots and driver updates. This guide shows a working, production-grade setup using x11vnc, mirroring the real GNOME display over VNC, no hacks, no virtual desktops. This method was recommended by NVIDIA engineers themselves on their developer forums.
This Nvidia forum post was the inspiration for this method, but we are not doing it exactly the same way. Here they have used monitor for a setting change, we are doing it completely headless.
The approach is explicit:
-
Force GNOME to start with a valid EDID
-
Ensure a real
:0display exists -
Attach
x11vncto that display at boot
Prerequisites
-
GNOME desktop installed and enabled
-
Root access
-
SSH access (since this is headless)
-
A known-good EDID from any working monitor
-
A VNC client on your host machine
This setup is commonly used on Jetson devices, but applies to any NVIDIA system running GNOME with the proprietary driver.
Step 1: Install and Initialize x11vnc
Install x11vnc and set a VNC password:
sudo apt update
sudo apt install x11vnc -y
x11vnc -storepasswd
x11vnc does not create a display. It attaches to an existing one. Everything that follows exists to guarantee that display.
Step 2: Force Graphical Boot Target
GNOME must start at boot, even without a monitor.
sudo systemctl set-default graphical.target
sudo reboot
If this step is skipped, x11vnc will start successfully, and attach to nothing.
Step 3: Extract EDID from a Working Monitor (Host Side)
On any Linux system with a monitor attached (your host machine):
xrandr --props
Locate the EDID: block, copy only the hex values, and paste them into a file without spaces or line breaks:
nano edid.hex
Convert it to binary:
xxd -r -p edid.hex EDID.bin
Verify the EDID:
edid-decode EDID.bin
External Image You should see a valid output describing the monitor capabilities.
Step 4: Install EDID on the Target (Jetson)
⚠️ Use an EDID from a monitor that supports the resolution you intend to use. Invalid EDIDs will cause GDM to fail silently.
Copy the EDID to the Jetson:
scp EDID.bin YOUR_USER@jetson:/tmp
Move it into firmware location:
sudo mkdir -p /lib/firmware/edid
sudo cp /tmp/EDID.bin /lib/firmware/edid/EDID.bin
Step 5: Replace Xorg Configuration (Critical)
Back up the existing config:
sudo cp /etc/X11/xorg.conf /etc/X11/xorg.conf.backup
sudo nano /etc/X11/xorg.conf
Replace the entire file with the following config:
If /etc/X11/xorg.conf does not exist, create it. Jetson images often rely on autogenerated defaults.
Section "ServerLayout"
Identifier "Layout0"
Screen 0 "Screen0" 0 0
InputDevice "Keyboard0" "CoreKeyboard"
InputDevice "Mouse0" "CorePointer"
EndSection
Section "Module"
SubSection "extmod"
Option "omit xfree86-dga"
EndSubSection
Load "glx"
Disable "dri"
EndSection
Section "InputDevice"
Identifier "Keyboard0"
Driver "kbd"
EndSection
Section "InputDevice"
Identifier "Mouse0"
Driver "mouse"
Option "Protocol" "auto"
Option "Device" "/dev/mouse"
Option "Emulate3Buttons" "no"
Option "ZAxisMapping" "4 5"
EndSection
Section "Device"
Identifier "Tegra0"
Driver "nvidia"
Option "AllowEmptyInitialConfiguration" "true"
Option "ConnectedMonitor" "DFP-0"
Option "CustomEDID" "DFP-0:/lib/firmware/edid/EDID.bin"
Option "UseDisplayDevice" "DFP-0"
Option "IgnoreEDIDChecksum" "DFP-0"
Option "ModeValidation" "AllowNonEdidModes"
EndSection
Section "Monitor"
Identifier "Monitor0"
VendorName "FakeVendor"
ModelName "Fake1920x1080"
Option "DPMS" "false"
EndSection
Section "Screen"
Identifier "Screen0"
Device "Tegra0"
Monitor "Monitor0"
DefaultDepth 24
SubSection "Display"
Depth 24
Modes "1920x1080"
EndSubSection
EndSection
Restart GNOME:
sudo systemctl restart gdm3
Sanity check:
DISPLAY=:0 xrandr
If this fails, VNC will never work. Fix this first.
Step 6: Enable GNOME Auto-Login (Mandatory)
x11vnc requires an active user session. No login → no desktop.
Edit GDM configuration:
sudo nano /etc/gdm3/custom.conf
Enable auto-login:
[daemon]
AutomaticLoginEnable=true
AutomaticLogin=YOUR_USERNAME
Reboot:
sudo reboot
Verify again:
DISPLAY=:0 xrandr
Step 7: Create a systemd Service for x11vnc
Create the service file:
sudo nano /etc/systemd/system/x11vnc.service
Paste the following:
[Unit]
Description=Start x11vnc at startup
Requires=display-manager.service
After=display-manager.service
[Service]
Type=simple
User=YOUR_USERNAME
ExecStart=/usr/bin/x11vnc -auth guess -display WAIT:0 -loop -usepw -forever -rfbport 5900
Restart=on-failure
[Install]
WantedBy=graphical.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable x11vnc.service
sudo systemctl start x11vnc.service
Finally, Connect via VNC Viewer
Install a VNC viewer on your host machine (e.g., tigervnc-viewer, RealVNC, etc.) I recommend tigervnc-viewer for its simplicity and performance. Or RealVNC for its user-friendly interface.
Recommended: SSH Tunnel + Local VNC Viewer
From your host machine:
ssh -N -L 5900:localhost:5900 yourusername@JETSON_IP
vncviewer localhost:5900
You should now see the real GNOME desktop, hardware-accelerated, exactly as if a monitor were attached.
If you see a blank screen or errors, revisit the EDID and Xorg configuration steps. These are proven to be the most fragile parts of this setup. Many people reported rectifying in EDID and xorg.conf fixed their issues.
Part 3:
Setting Up VNC for Remote Desktop Access * XFCE Edition
This is completely optional. If you prefer GNOME, skip this section. I keep it here for completeness and serves as a backup plan if GNOME VNC setup fails by some updates in future.
If you want reliable headless VNC without GNOME’s complexity, XFCE is the pragmatic choice. XFCE is lightweight, predictable, and works cleanly with TigerVNC using a user-level systemd service. No EDID hacks, no display manager dependencies, no GPU quirks.
This guide sets up a persistent, headless XFCE desktop accessible over VNC, designed to survive reboots and run without any local login.
Why XFCE + TigerVNC
-
No dependency on a physical display
-
No Xorg/Wayland/GDM edge cases
-
Clean separation from system display managers
-
Works perfectly over SSH tunnels
-
Ideal for Jetson, servers, and lab machines
If you need stability over aesthetics, this is the correct stack.
Step 1: Install XFCE (Target Machine)
Install the XFCE desktop environment:
sudo apt update
sudo apt install -y xfce4 xfce4-goodies
Do not install a display manager. We are running XFCE entirely inside VNC.
Step 2: Install TigerVNC
sudo apt install -y tigervnc-standalone-server tigervnc-common
Set a VNC password:
vncpasswd
This creates ~/.vnc/passwd, which TigerVNC will use for authentication.
Step 3: Quick Sanity Test
Start a temporary VNC server:
tigervncserver :1
If this launches without errors, the core components are working. Stop it afterward:
tigervncserver -kill :1
XFCE runs on a separate X display (:1) and does not interfere with GNOME (:0). Both can coexist safely. Also that the port
5900is for GNOME VNC, while5901is for XFCE VNC. Adjust accordingly based on which desktop you are using.
Step 4: Disable Legacy VNC Startup Scripts
Remove or empty any existing VNC startup files to avoid race conditions with systemd:
rm -f ~/.vnc/xstartup
TigerVNC will be launched and managed only via systemd from this point onward.
Step 5: Create a User-Level systemd Service
Create the user systemd directory if it doesn’t exist:
mkdir -p ~/.config/systemd/user
nano ~/.config/systemd/user/tigervnc.service
Paste the following service definition (adjust username if needed):
[Unit]
Description=TigerVNC server (Xfce headless)
After=network.target
[Service]
Type=simple
Environment=DISPLAY=:1
ExecStart=/usr/bin/Xtigervnc \
:1 \
-localhost \
-SecurityTypes VncAuth \
-rfbauth /home/yourusername/.vnc/passwd \
-geometry 1920x1080
ExecStartPost=/bin/sh -c 'sleep 2; DISPLAY=:1 dbus-launch --exit-with-session xfce4-session & disown'
Restart=on-failure
RestartSec=3
[Install]
WantedBy=default.target
Why this works
-
Xtigervnccreates a real X server -
XFCE runs inside that server
-
dbus-launchensures session services work correctly -
No dependency on login managers or GPUs
Step 6: Enable User Lingering (Critical)
By default, user services do not start unless the user logs in. Headless systems need lingering enabled.
sudo loginctl enable-linger yourusername
Verify:
loginctl show-user yourusername | grep Linger
Expected output:
Linger=yes
If this is not set, your VNC server will not start after reboot.
Step 7: Enable and Start the Service
Reload user services and enable TigerVNC:
systemctl --user daemon-reload
systemctl --user enable tigervnc
systemctl --user start tigervnc
Check status:
systemctl --user status tigervnc
Reboot to confirm persistence:
sudo reboot
Step 8: Access VNC Securely Over SSH
Same here, use an SSH tunnel to secure your VNC connection.
From your host machine:
ssh -N -L 5901:localhost:5901 yourusername@JETSON_IP
vncviewer localhost:5901
This keeps VNC off the network and avoids exposing port 5901.
Final Notes
Troubleshooting
If you encounter issues, First do a google search to see if there are nvidia forum posts. Keep taking LLMs help as last resort. as often I see it can go crazy and overcomplicate things.
Ports & Displays
Display :0 → GNOME + x11vnc → TCP 5900
Display :1 → XFCE + TigerVNC → TCP 5901
Security Note
Do not expose VNC ports directly to the network. Always use SSH tunneling.