> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.stereolabs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.stereolabs.com/_mcp/server.

# Using the ZED SDK with a Virtual Display on NVIDIA® Jetson™

When working with a ZED camera and a ZED SDK-based application running on a remote NVIDIA® Jetson™ device, you may need to access the display output without physically connecting a monitor (headless mode). While a dummy HDMI dongle is a common solution, you can also configure a virtual display entirely through software.

This guide shows how to set up a virtual display on your NVIDIA® Jetson™ device, allowing you to use the ZED camera with remote desktop applications like VNC, NoMachine, or X11 forwarding without requiring a physical dongle.

## Prerequisites

Before starting, ensure you have:

* Set up your remote NVIDIA® Jetson™ device to [run ZED SDK applications](/docs/development/zed-sdk/linux/work-with-nvidia-jetson/#download-and-install-the-zed-sdk).
* Access to the device physically or remotely via SSH.
* \[Optional] Installed and configured a remote desktop application if you plan to use one.

## Virtual Display Setup

### Create the Setup Script

First, create a script file to configure the virtual display.

The script below targets the Jetson AGX Orin with JetPack 5.x (L4T R35.x) or JetPack 6.x (L4T R36.x). You can download a pre-made script from [this link](https://gist.githubusercontent.com/adujardin/9154a619cbf5a5508e5fad336839702b/raw/d4d3789928fb4855a6759508171836286f9d991e/setup-virtual-display.sh) or manually create a file named `setup-virtual-display.sh` with the following content:

<details>
  <summary>
    Expand to view the content of 

    `setup-virtual-display.sh`
  </summary>

  ```bash
  #!/bin/bash
  # =============================================================================
  # Jetson Headless Virtual Display Setup (GPU-Accelerated)
  # =============================================================================
  #
  # Creates a virtual 1920x1080@60Hz display on Jetson AGX Orin without a
  # physical monitor. EGL, CUDA, and NVIDIA Argus all work. A real monitor
  # can still be hot-plugged at any time. Works with NoMachine/VNC for remote
  # desktop access.
  #
  # Platform: Jetson AGX Orin
  #   - JetPack 5.x (L4T R35.x, Ubuntu 20.04) — Xorg "nvidia" DDX driver
  #   - JetPack 6.x (L4T R36.x, Ubuntu 22.04) — Xorg "nvidia" DDX driver
  #
  # HOW IT WORKS:
  # -------------
  # Uses the nvidia DDX Xorg driver with ConnectedMonitor + CustomEDID options
  # to fake a connected 1920x1080 display on the DP port. This provides:
  #   - Full NVIDIA EGL support (nvbufsurface, Argus camera can create EGLImages)
  #   - CUDA-EGL interop (cuGraphicsEGLRegisterImage works)
  #   - Hardware-accelerated X11/GLX rendering via NVIDIA GPU
  #   - Proper DRI2 for remote desktop protocols (NoMachine, VNC, etc.)
  #
  # JP6-SPECIFIC NOTES:
  # -------------------
  # nvidia-drm.modeset=1 is NOT compatible with nvidia DDX driver — it causes
  # "Failed to acquire modesetting permission". We remove it from boot params.
  # The nvidia DDX handles modesetting directly without nvidia-drm KMS.
  #
  # EGL/CUDA/Argus work fine WITHOUT nvidia-drm.modeset=1 when using nvidia DDX,
  # because applications access NVIDIA EGL/CUDA directly via the ICD, not through
  # the kernel DRM modesetting path.
  #
  # REAL MONITOR:
  # -------------
  # If you plug in a real DP monitor, it appears automatically in xrandr
  # and is usable alongside (or instead of) the virtual framebuffer.
  #
  # USAGE:
  #   chmod +x setup-virtual-display.sh
  #   sudo ./setup-virtual-display.sh
  #   sudo reboot
  #
  # VERIFY:
  #   DISPLAY=:0 xrandr                                    # 1920x1080 screen
  #   DISPLAY=:0 eglinfo 2>&1 | head -15                   # EGL vendor: NVIDIA
  #   python3 -c "import ctypes; c=ctypes.CDLL('libcuda.so.1'); print(c.cuInit(0))"  # 0
  #   # Connect via NoMachine — should show full GNOME desktop
  #
  # TO REVERT:
  #   sudo cp /etc/X11/xorg.conf.bak.original /etc/X11/xorg.conf  # or rm it
  #   sudo rm -f /etc/X11/edid-1080p.bin
  #   sudo rm -f /etc/udev/rules.d/99-jetson-drm-seat.rules
  #   sudo systemctl enable seatd 2>/dev/null  # if you need seatd back
  #   # For JP6: add back nvidia-drm.modeset=1 to /boot/extlinux/extlinux.conf if needed
  #   sudo reboot
  #
  # =============================================================================

  set -euo pipefail

  if [[ $EUID -ne 0 ]]; then
      echo "ERROR: Must run as root (sudo)."
      exit 1
  fi

  # --- Detect JetPack / L4T version ---
  detect_jetpack_version() {
      local l4t_major=""
      if [[ -f /etc/nv_tegra_release ]]; then
          l4t_major=$(sed -n 's/^# R\([0-9]*\).*/\1/p' /etc/nv_tegra_release)
      fi
      if [[ -z "$l4t_major" ]] && command -v dpkg-query &>/dev/null; then
          l4t_major=$(dpkg-query -W -f='${Version}' nvidia-l4t-core 2>/dev/null \
                      | sed -n 's/^\([0-9]*\)\..*/\1/p') || true
      fi
      if [[ -n "$l4t_major" && "$l4t_major" -ge 36 ]]; then
          echo 6
      elif [[ -n "$l4t_major" && "$l4t_major" -ge 35 ]]; then
          echo 5
      else
          echo 0
      fi
  }

  # --- Detect first available DP connector ---
  detect_dp_connector_drm() {
      # DRM naming (1-indexed): DP-1, DP-2, etc.
      for card_dp in /sys/class/drm/card*-DP-*; do
          if [[ -d "$card_dp" ]]; then
              basename "$card_dp" | sed 's/card[0-9]*-//'
              return
          fi
      done
      echo "DP-1"
  }

  detect_dp_connector_xorg() {
      # nvidia DDX uses 0-indexed: DP-0, DP-1, etc.
      local drm_idx
      drm_idx=$(detect_dp_connector_drm | sed 's/.*DP-//')
      echo "DP-$((drm_idx - 1))"
  }

  JP_VERSION=$(detect_jetpack_version)
  DP_DRM=$(detect_dp_connector_drm)
  DP_XORG=$(detect_dp_connector_xorg)

  echo "=== Jetson Virtual Display Setup ==="

  if [[ "$JP_VERSION" -eq 6 ]]; then
      echo "Detected: JetPack 6 (L4T R36.x)"
  elif [[ "$JP_VERSION" -eq 5 ]]; then
      echo "Detected: JetPack 5 (L4T R35.x)"
  else
      echo "WARNING: Could not detect JetPack version, defaulting to JP5 path"
      JP_VERSION=5
  fi

  # =========================================================================
  # COMMON PATH (JP5 + JP6): nvidia DDX driver with ConnectedMonitor + CustomEDID
  # =========================================================================

  echo "Using nvidia DDX driver with connector ${DP_XORG}"
  STEP=0
  TOTAL_JP5=3
  TOTAL_JP6=5

  # ------------------------------------------------------------------
  # Step 1: Backup xorg.conf
  # ------------------------------------------------------------------
  STEP=$((STEP+1))
  if [[ -f /etc/X11/xorg.conf && ! -f /etc/X11/xorg.conf.bak.original ]]; then
      cp /etc/X11/xorg.conf /etc/X11/xorg.conf.bak.original
      echo "[${STEP}] Backed up xorg.conf"
  else
      echo "[${STEP}] Backup: skipped (already exists or no file)"
  fi

  # ------------------------------------------------------------------
  # Step 2: EDID binary
  # ------------------------------------------------------------------
  STEP=$((STEP+1))
  base64 -d > /etc/X11/edid-1080p.bin << 'EDID_EOF'
  AP///////wBZ5QEAAQAAAAEiAQOAAAB4Cu6Ro1RMmSYPUFQhCAABAQEBAQEBAQEBAQEBAQEBAjqA
  GHE4LUBYLEUADyghAAAeAAAA/ABWaXJ0dWFsIDEwODBwAAAA/QAySx5REQAKICAgICAgAAAA/wAw
  MDAwMDAwMDAwMDAxAJc=
  EDID_EOF
  chmod 644 /etc/X11/edid-1080p.bin
  echo "[${STEP}] Installed EDID binary"

  # ------------------------------------------------------------------
  # Step 3: xorg.conf (nvidia DDX)
  # ------------------------------------------------------------------
  STEP=$((STEP+1))
  cat > /etc/X11/xorg.conf << XORG_EOF
  # Jetson JP${JP_VERSION} - nvidia DDX driver, virtual display
  Section "DRI"
      Mode 0666
  EndSection

  Section "Module"
      Disable     "dri"
      SubSection  "extmod"
          Option  "omit xfree86-dga"
      EndSubSection
  EndSection

  Section "Device"
      Identifier  "Tegra0"
      Driver      "nvidia"
      Option      "AllowEmptyInitialConfiguration" "true"
      Option      "ConnectedMonitor"               "${DP_XORG}"
      Option      "CustomEDID"                     "${DP_XORG}:/etc/X11/edid-1080p.bin"
      Option      "HardDPMS"                       "false"
  EndSection

  Section "Monitor"
      Identifier  "Virtual-1080p"
      HorizSync   30-81
      VertRefresh 50-75
      ModeLine    "1920x1080_60" 148.50 1920 2008 2052 2200 1080 1084 1089 1125 +HSync +VSync
      Option      "PreferredMode" "1920x1080_60"
  EndSection

  Section "Screen"
      Identifier  "Default Screen"
      Device      "Tegra0"
      Monitor     "Virtual-1080p"
      DefaultDepth 24
      SubSection "Display"
          Depth    24
          Modes    "1920x1080_60"
      EndSubSection
  EndSection
  XORG_EOF
  echo "[${STEP}] Wrote xorg.conf (nvidia DDX)"

  # ------------------------------------------------------------------
  # JP6-SPECIFIC: Additional steps
  # ------------------------------------------------------------------
  if [[ "$JP_VERSION" -eq 6 ]]; then

      # ------------------------------------------------------------------
      # Step 4: Remove nvidia-drm.modeset=1 from boot params
      # ------------------------------------------------------------------
      STEP=$((STEP+1))
      EXTLINUX_CONF="/boot/extlinux/extlinux.conf"
      if [[ -f "$EXTLINUX_CONF" ]]; then
          if grep -q "nvidia-drm.modeset=1" "$EXTLINUX_CONF"; then
              cp "$EXTLINUX_CONF" "${EXTLINUX_CONF}.bak.$(date +%Y%m%d%H%M%S)"
              sed -i 's| nvidia-drm\.modeset=1||g' "$EXTLINUX_CONF"
              echo "[${STEP}] Removed nvidia-drm.modeset=1 from extlinux.conf"
          else
              echo "[${STEP}] nvidia-drm.modeset=1 not present (OK)"
          fi
      else
          echo "[${STEP}] WARNING: ${EXTLINUX_CONF} not found"
      fi

      # ------------------------------------------------------------------
      # Step 5: Disable seatd if present (optional, helps avoid conflicts)
      # ------------------------------------------------------------------
      STEP=$((STEP+1))
      if systemctl is-enabled seatd &>/dev/null 2>&1; then
          systemctl stop seatd 2>/dev/null || true
          systemctl disable seatd 2>/dev/null || true
          echo "[${STEP}] Disabled seatd (avoids DRM master conflicts)"
      else
          echo "[${STEP}] seatd not present or already disabled"
      fi

  fi

  echo ""
  echo "=== Done! ==="
  echo ""
  echo "A reboot is REQUIRED:"
  echo "  sudo reboot"
  echo ""
  echo "After reboot, verify:"
  echo "  DISPLAY=:0 xrandr                                    # 1920x1080 screen"
  echo "  DISPLAY=:0 eglinfo 2>&1 | head -15                   # EGL vendor: NVIDIA"
  echo "  python3 -c 'import ctypes; c=ctypes.CDLL(\"libcuda.so.1\"); print(c.cuInit(0))'  # 0"
  echo ""
  if [[ "$JP_VERSION" -eq 6 ]]; then
      echo "JP6 NOTES:"
      echo "  - nvidia DDX driver active (NOT modesetting)"
      echo "  - nvidia-drm.modeset=1 removed from boot (incompatible with nvidia DDX)"
      echo "  - EGL/CUDA/Argus work via nvidia DDX's own rendering path"
      echo "  - NoMachine/VNC will show full hardware-accelerated desktop"
      echo ""
  fi
  echo "Connect via NoMachine or VNC to access the desktop remotely."
  ```
</details>

Make the script executable:

```bash
chmod +x setup-virtual-display.sh
```

### Apply the Virtual Display Configuration

Run the setup script, then reboot to apply the changes (on JetPack 6, the script also modifies the kernel boot parameters, which only take effect after a reboot):

```bash
sudo ./setup-virtual-display.sh && sudo reboot
```

This script configures your system's X server to create a virtual display. After the reboot, you can connect via remote desktop applications and use the ZED camera as if a physical monitor were connected.

> **Warning**
>
> This configuration replaces `/etc/X11/xorg.conf` and, on JetPack 6, removes `nvidia-drm.modeset=1` from the boot parameters. A real DisplayPort monitor can still be hot-plugged, but to go back to your original display setup, revert the configuration (see below).

## Reverting to Physical Display

To restore the original display configuration, run:

```bash
# Restore the original X server configuration (if no backup exists, remove the file instead: sudo rm /etc/X11/xorg.conf)
sudo cp /etc/X11/xorg.conf.bak.original /etc/X11/xorg.conf
sudo rm -f /etc/X11/edid-1080p.bin
sudo rm -f /etc/udev/rules.d/99-jetson-drm-seat.rules
sudo systemctl enable seatd 2>/dev/null  # only if you need seatd back
sudo reboot
```

On JetPack 6, also add `nvidia-drm.modeset=1` back to the kernel command line in `/boot/extlinux/extlinux.conf` if your setup needs it (the script saved a timestamped backup of that file next to it) before rebooting.

## How It Works

The virtual display setup configures the NVIDIA X driver to report a virtual 1920x1080 monitor (through a custom EDID) on the DisplayPort output, so no physical display connection is required. This allows the system to:

* Create a display session without a physical monitor
* Enable GPU acceleration for graphics applications
* Allow remote desktop connections to access the display
* Run GUI applications that require a display environment

This is useful for headless robotic systems where you need to run ZED SDK applications with GUI components or perform development and debugging remotely.

## Troubleshooting

If you encounter issues after setting up the virtual display:

* **Remote desktop not connecting**: Ensure your remote desktop server (VNC, NoMachine, etc.) is properly configured and running.
* **Need to switch back to physical display**: Use the [revert command](#reverting-to-physical-display) shown above.
* **Display resolution issues**: You may need to modify the script to adjust the virtual display resolution. The current script sets it to 1920x1080\@60Hz (Full HD).