Sunshine을 사용한 headless Linux의 원격 데스크탑 설정

The Elder Node·2026년 8월 25일

이 글에서는 모니터가 연결되지 않은 리눅스(Linux) 헤드리스(Headless) 서버에서 Nvidia GPU의 하드웨어 가속과 VirtualGL을 결합하여 리눅스 시스템과 컨테이너 환경에서 3차원 가속을 사용하면서 원격 접속이 가능하도록 하는 방법에 대해서 이야기하고 그 과정에서 내가 겪은 문제에 대해 이야기 하고자 한다.

(이 글에서 다루는 모든 설정 파일과 자동화 스크립트는 GitHub 저장소: sunshine-headless-setup에서 다운로드할 수 있다.)


🖥️ GPU 가속 원격 데스크탑

모니터가 없는 서버에서 3D 렌더링(예: RViz, Gazebo)이 필요한 어플리케이션을 구동하려면, 일반적인 VNC, XRDP로는 불가능하다.그래서 다른 방법을 찾아 보던 중 Sunshine server와 moonlight 클라이언트가 내가 원하는 3D 렌더링 상태에서의 원격 데스크탑이 가능하다는 것을 알게되어 이를 내 리눅스 시스템에 설정하였다.

X11과 MATE desktop

Ubuntu의 기본 데스크탑인 Gnome-Wayland 조합은 너무 무겁고 모니터가 물리적으로 연결되지 않은 상태에서는 원격 데스크탑 설정이 너무 어렵다. 또한, 컨테이너에서 VirtualGL을 사용하려면 X11이 가장 안정적이라는 점도 있다. 그래서 MATE desktop을 기본 데스크탑으로 변경하였다. 최소 패키지만 필요하기 때문에 mate-desktop-environment를 설치하였다.

sudo apt update
sudo apt install mate-desktop-environment

설치 완료 시점에 디스플레이 매니저(Display Manager)를 선택하는 화면이 나타나면 lightdm을 선택한다. (좀 더 가벼움)

Dummy Display 설정

기본적으로 Sunshine은 항상 Display:0를 찾는다. 하지만, 나는 headless 시스템이므로 가상 디스플레이를 만들고 Sunshine에 이 디스플레이를 설정해 주어야 했다.(저장소의 sunshine-dummy.conf 참고)

  • EDID 파일 생성 및 배치:
    모니터의 해상도 및 주사율 정보가 담긴 EDID 바이너리 파일을 생성(또는 추출)하여 /etc/X11/dummy_edid.bin 경로에 복사.
  • Xorg 설정 파일 작성: /etc/X11/sunshine-dummy.conf 파일을 생성하고, NVIDIA 드라이버가 가상 모니터 EDID를 읽어 화면을 강제 출력하도록 Option "CustomEDID"와 "AllowEmptyInitialConfiguration"을 추가.

Sunshine을 통한 Nvidia NVENC 하드웨어 스트리밍

가상 디스플레이에 그려진 화면을 클라이언트로 전송하기 위해 Sunshine server와 Moonlight client를 사용한다. Nvidia 하드웨어 인코더(NVENC)를 활용하여 시스템 부하를 줄이고 부드러운 화면을 볼 수 있다.

https://github.com/LizardByte/Sunshine/releases
일반적인 RTX-2xxx 이상의 그래픽 카드를 사용 중이라면 위의 공식 릴리즈 페이지에서 자신의 OS에 맞는 패키지를 다운로드 받아서 설치하면 된다.
나의 경우에는 구형 GTX-1050을 사용해야 해서 소스를 받아서 패치 후 빌드하여 인스톨하였다. 아래에 이와 관련된 내용이 설명되어 있다.

가상 모니터를 위한 초기화 스크립트

가상 모니터를 통해 Sunshine을 사용하는 경우 리눅스의 Display Manager를 거치지 않고 시스템 서비스를 통해 실행된다. 따라서, 시스템 환경 변수 설정이나 MATE 데스크톱 환경을 시작하고 Sunshine을 실행시키기 위한 스크립트를 만들어서 시스템 서비스 시작 때 이를 호출하도록 하였다.
(https://github.com/hwjeon0123/sunshine-headless-setup/blob/main/configs/start-sunshine-x.sh)

해상도 동적 변경 스크립트 (xrandr)

접속하는 기기(태블릿, 노트북 등)의 화면 비율에 맞춰 해상도가 자동으로 변경되도록 하기 위해 Sunshine의 apps.json 설정에서 준비 명령(prep-cmd)으로 커스텀 쉘 스크립트(sunshine-resolution.sh)를 호출하여, 접속 시 xrandr 명령어로 가상 모니터의 해상도를 동적으로 추가하고 변경할 수 있도록 하였다.

systemd 서비스 등록 (sunshine-xorg.service)

시스템 부팅 시 백그라운드에서 가상 디스플레이를 할당하고 MATE 데스크탑 세션(start-sunshine-x.sh)을 실행하도록 시스템 서비스 유닛을 작성해서 시스템에 등록한다.
YOUR_USERNAME은 자신의 아이디로 교체한다.

  • WorkingDirectory 항목을 설정해야지 원격 접속 때 홈 디렉토리에서 시작한다.
[Unit]
Description=Ultimate Sandbox for Headless Sunshine
After=network.target

[Service]
# Replace 'YOUR_USERNAME' with your actual linux username
User=YOUR_USERNAME

# CRITICAL FIX: Ensure the working directory is set to the user's home.
# Without this, systemd defaults to '/' (root), causing terminal/file managers
# to open in '/' and breaking screenshot tools due to write permission errors.
WorkingDirectory=/home/YOUR_USERNAME

# Start X server with the dummy configuration and execute the MATE session script
ExecStart=/usr/bin/xinit /home/YOUR_USERNAME/.config/sunshine/start-sunshine-x.sh -- :99 -config sunshine-dummy.conf
Restart=always
RestartSec=3

[Install]
WantedBy=graphical.target

참고: 크로미움(Brave/Chrome) 브라우저 GPU 가속 에러

NVIDIA 가상 드라이버(Dummy Xorg)를 통해 하드웨어 가속 세션을 구축했음에도 불구하고, 세션 내에서 Brave나 Chrome 같은 최신 크로미움 기반 브라우저를 실행하면 창이 뜨지 않고 터미널에 다음과 같은 에러를 출력하며 멈추는 증상이 발생하였다.

MESA-LOADER: failed to open dri: /usr/lib/x86_64-linux-gnu/gbm/dri_gbm.so: 동적 오브젝트 파일을 열 수 없습니다: 허가 거부

원인: DRM Modesetting 누락

이 문제는 그래픽카드가 Xorg에서는 정상적으로 작동하고 있으나, 리눅스 커널 단에서 NVIDIA의 화면 렌더링 신호를 사용자 공간(User Space)의 어플리케이션들에게 넘겨주는 기능(DRM Modesetting)이 꺼져 있기 때문이다.

최신 브라우저들은 하드웨어 가속 렌더링을 위해 GBM(Generic Buffer Management) API를 사용하여 /dev/dri/renderD128 등의 렌더링 전용 장치 파일에 접근하려고 시도한다. 하지만 NVIDIA 드라이버 설정에서 DRM Modesetting이 비활성화되어 있어서 이 장치 파일(/dev/dri/*)이 아예 생성되지 않았다.

해결 방법: 커널 부트 파라미터 추가

아래 링크의 문서를 참고하여 커널 부트 파라미터를 추가하였다.
https://download.nvidia.com/XFree86/Linux-x86_64/580.173.02/README/kms.html

/etc/default/grub 파일을 관리자 권한(sudo)으로 열고 GRUB_CMDLINE_LINUX_DEFAULT 항목을 찾아 맨 끝에 nvidia-drm.modeset=1 옵션을 추가한다.
ex) GRUB_CMDLINE_LINUX_DEFAULT="quiet splash nvidia-drm.modeset=1"
수정 후 아래 명령어로 GRUB을 업데이트하고 시스템을 재부팅한다.

sudo update-grub
sudo reboot

GTX 1050 (Pascal architecture) GPU의 NVENC 인코딩 문제와 해결

나의 경우, 그래픽 카드 2개를 가지고 AI 관련 기능은 고성능 주력 GPU에 맡기고 일반적인 GUI는 구형 GTX 1050 GPU에 전담시키고자 하였다. 그러나 이 구형 그래픽카드 환경에서 Moonlight client로 접속을 시도하면 Multiple reference frames are not supported라는 에러 메시지와 함께 스트리밍 화면이 까맣게 나오거나 끊어지는 현상이 발생하였다.

이 문제는 GTX 1050이 사용하는 Pascal 아키텍처 GPU의 하드웨어적 한계 때문에 발생한다. Sunshine 내부에서 영상 인코딩을 담당하는 FFmpeg은 기본적으로 다중 참조 프레임(B-프레임, refs > 0)을 사용하도록 요청하는데, 구형 GPU의 하드웨어 인코더(NVENC)가 이 다중 참조를 지원하지 못해 인코더에서 오류가 발생하게 되는 것이다.

안타깝게도 공식 배포되는 패키지나 설정 파일 옵션만으로는 이 참조 프레임 요청을 완전히 비활성화할 수 없다. 따라서 Sunshine 소스코드를 직접 다운로드하여 NVENC 초기화 로직을 수정한 뒤 소스 빌드를 진행해야 한다.

src/video.cpp 파일 내 NVENC 초기화 구문을 찾아, 아래와 같이 참조 프레임 변수를 강제로 0 (AUTO)으로
고정하도록 코드를 수정하였다.

// src/video.cpp 
// ... 기존 코드 ...
if (config.numRefFrames && video_format[encoder_t::REF_FRAMES_RESTRICT]) {
  ctx->refs = config.numRefFrames;
} else {
  // GTX 1050 (Pascal) 등 구형 GPU 인코더 에러 방지를 위해 
  // refs를 0 (AUTO)으로 강제 고정
  ctx->refs = 0; 
}

또한 h264_nvenc 및 hevc_nvenc 설정 배열에 {"b_ref_mode"s, 0}과 {"bf"s, 0} 옵션을 명시적으로 추가하여 B-프레임 사용을 차단하였다.

소스 빌드 시 화면 캡처 권한 에러 (Cannot create capture session)

앞서 설명한 GTX 1050 NVENC 패치를 위해 Sunshine 소스코드를 직접 빌드(make install)하여 설치한 후 구동해 보면, 스트리밍이 시작되지 않고 터미널에 아래와 같은 에러가 발생하는 것을 확인할 수 있다.

Cannot create capture session: the display server is in modeset

이 문제는 Linux의 setcap 명령어를 사용하여, 빌드된 Sunshine 실행 파일에 시스템 관리 권한(cap_sys_admin)과 프로세스 우선순위 권한(cap_sys_nice)을 명시적으로 부여해주면 해결된다.

sudo setcap cap_sys_admin,cap_sys_nice+p /usr/local/bin/sunshine
profile
무선/임베디드 엔지니어의 ROS2 & AI 개척기

0개의 댓글