스팀덱은 들고 다니기 좋은 게임기지만, 무거운 게임은 집 PC가 훨씬 잘 돌린다. 그래서 하고 싶은 건 두 가지였다.
- 밖에서도 스팀덱으로 집 PC 게임을 스트리밍하기
- 집에서는 스팀덱 게임을 거실 TV 큰 화면으로 보내 패드로 조작하기
스트리밍 도구는 양쪽 다 Moonlight 계열을 썼다. Moonlight는 클라이언트고, 호스트 쪽에는 Sunshine(또는 그 포크)을 띄운다. 1번은 잘 됐고, 2번은 아직 반쯤이다.
스팀덱 설정은 스팀덱을 직접 만지지 않고 NAS에 상주하는 Claude Code에서 SSH로 붙어서 했다. 키 인증으로 접속을 열어 두고, 비밀번호가 필요한 sudo만 PC에서 ssh -t deck@스팀덱 "sudo ..." 한 줄로 직접 쳤다.
구성

| 호스트 (화면을 보내는 쪽) | 클라이언트 (받는 쪽) | 경로 | |
|---|---|---|---|
| ① 어디서든 | 집 PC — Apollo | 스팀덱 — Moonlight | tailnet (WireGuard) |
| ② 집 안 | 스팀덱 — Sunshine (Decky 플러그인) | Google TV — Moonlight → 지금은 Steam Link | 집 LAN |
스팀덱에 Tailscale 깔기
집 VPN은 headscale이다. 제어 서버만 NAS에 있고 기기에는 Tailscale 공식 클라이언트를 깐다. 문제는 SteamOS였다.
SteamOS는 루트 파일 시스템(/)이 읽기 전용이고, OS 업데이트 때 A/B 파티션을 통째로 바꿔 끼운다. pacman으로 뭘 깔아도 다음 업데이트에 사라진다. 그래서 Tailscale이 직접 관리하는 스팀덱용 설치 스크립트 tailscale-dev/deck-tailscale을 썼다. 스크립트를 먼저 받아 읽어 보니 하는 일은 단순했다.
pkgs.tailscale.com에서 최신 정적 바이너리(tarball)를 받아/opt/tailscale에 둔다. 스팀덱에서/opt는 실제로/home쪽 offload 영역이라 OS 업데이트에도 남는다tailscaled.service와override.conf를/etc/systemd/system에 넣고enable— 부팅하면 게임 모드에서도 알아서 뜬다/etc/default/tailscaled,/etc/profile.d/tailscale.sh를/etc/atomic-update.conf.d/에 등록해 OS 업데이트 때도 유지되게 한다
# PC에서 한 줄로 (sudo 비밀번호는 여기서 입력)
ssh -t deck@<스팀덱 IP> "cd ~/deck-tailscale && sudo bash tailscale.sh && sudo /opt/tailscale/tailscale set --operator=deck"--operator=deck을 주면 이후 tailscale up/status는 sudo 없이 deck 사용자로 할 수 있다. 그 다음 headscale에 붙였다.
/opt/tailscale/tailscale up \
--login-server=https://vpn.example.com \
--hostname=steamdeck \
--accept-dns=false \
--authkey=<10분짜리 1회용 키>- 사용자 등록은 브라우저 로그인 대신 headscale에서 10분짜리 사전 인증 키를 만들어 넘겼다(키는 화면에 찍지 않고 바로 전달)
--accept-dns=false: MagicDNS를 쓰지 않아서 스팀덱 DNS는 그대로 둔다. 그래서 뜨는systemd-resolved and NetworkManager are wired together incorrectly경고는 무시해도 된다- 노드 만료는 없게 두었다. 게임기가 몇 달에 한 번 재인증을 요구하면 귀찮다
이걸로 스팀덱은 100.64.0.6을 받았고, 집 밖에서도 NAS에서 ssh가 붙는다(절전 중일 때는 당연히 안 된다).
집 PC: Sunshine 대신 Apollo
집 PC(Windows)에는 원래 Tailscale이 깔려 있어서 tailnet 주소가 이미 있었다. 호스트로는 Sunshine 대신 포크인 Apollo를 깔았다.
Apollo의 가장 큰 차이는 가상 디스플레이다. 클라이언트가 붙으면 그 해상도·주사율에 맞는 가상 모니터를 만들어 그 화면을 보낸다. 스팀덱 화면은 1280×800(16:10)인데, 일반 Sunshine으로 16:9 모니터를 그대로 보내면 위아래에 검은 띠가 생기고 글자가 작아진다. Apollo는 스팀덱에 맞는 화면을 새로 만들어 주니 그런 게 없다. 연결이 끝나면 가상 모니터도 사라진다.
페어링은 Sunshine과 같다. 스팀덱 Moonlight에서 호스트를 추가하면 PIN이 뜨고, PC의 Apollo 웹 UI에 그 PIN을 넣으면 끝.
Moonlight에 tailnet IP를 넣으면 끝
여기가 이 구성의 핵심이다. 스팀덱 Moonlight에서 호스트를 추가할 때 집 LAN 주소가 아니라 PC의 tailnet IP(100.64.0.x)를 넣는다. 그러면 스팀덱이 집 Wi-Fi에 있든, 카페 Wi-Fi든, 휴대폰 테더링이든 같은 주소로 붙는다.
원래 Moonlight를 집 밖에서 쓰려면 공유기에 포트를 열어야 한다.
| 프로토콜 | 포트 | 용도 |
|---|---|---|
| TCP | 47984, 47989, 48010 | HTTPS/HTTP 제어, RTSP |
| UDP | 47998–48000 | 영상·제어·음성 스트림 |
게임 스트리밍 호스트를 인터넷에 그대로 열어 두는 건 내키지 않는다. tailnet 안에서는 이 포트들이 WireGuard 터널 안으로만 오가니 공유기는 아무것도 열 필요가 없다.
네트워크 쪽에서 보면 이게 되는 이유는 이렇다.
- headscale은 각 기기의 공개키와 엔드포인트(공인 IP:포트)만 나눠 준다. 영상 트래픽은 지나가지 않는다
- 두 기기는 STUN으로 알아낸 서로의 엔드포인트로 UDP 홀 펀칭을 시도해 직접 WireGuard 연결을 맺는다
- 대칭형 NAT처럼 직접 연결이 안 되는 망이면 DERP 중계를 거친다. 기본 DERP는 도쿄라 멀어서 집 NAS에 DERP를 하나 따로 두었고, 이 경우에도 집까지 한 번 거치는 정도로 끝난다
스트리밍은 지연에 민감해서 중계보다 직접 연결이 훨씬 낫다. tailscale status에서 상대가 direct로 보이는지 확인해 두면 좋다.
반대로: 스팀덱 게임을 거실 TV로
이번엔 스팀덱을 호스트로 쓰고, 거실 Google TV의 Moonlight로 받아 큰 화면에서 하려 했다. 스팀덱에는 Decky Loader 플러그인 Decky Sunshine을 썼다. 이 플러그인이 Flatpak Sunshine을 root 권한으로 띄워 KMS로 화면을 직접 잡는다. 여기서는 몇 가지에 걸렸다.
플러그인이 starting에서 멈춤 — ~/homebrew/plugins에 이름이 같은 "Decky Sunshine" 플러그인이 두 개(신버전·구버전) 들어 있었다. 부팅할 때마다 나중에 뜬 구버전이 신버전을 내리고, 구버전이 띄운 Sunshine은 몇 초 뒤 죽었다. 구버전 폴더를 밖으로 빼고 plugin_loader를 재시작해서 해결.
웹 UI에서 설정을 저장하면 503 — Sunshine 웹 UI에서 인코더(vaapi)·언어를 바꿔 저장했더니, Moonlight에서 앱을 실행할 때 503이 났다. 로그를 보니 저장 직후 Sunshine이 같은 프로세스로 재시작하면서 root 권한을 잃고 화면 캡처에 실패하고 있었다.
Couldn't get handle for DRM Framebuffer
Unable to find display or encoderDecky에서 Sunshine을 껐다 켜면(플러그인이 다시 root로 띄움) 돌아온다. 설정을 바꾼 뒤엔 꼭 플러그인에서 재시작해야 한다.
화면이 느리게 바뀜 — 소리는 정상인데 화면만 늦게 따라왔다. 스팀덱 쪽(Sunshine 로그, Wi-Fi 신호·링크 속도)은 문제가 없었고, Google TV Moonlight의 코덱을 HEVC에서 H.264로 바꾸니 해결됐다. TV 쪽 HEVC 디코딩 문제로 보인다.
남은 문제: 게임을 켜면 회색 화면
여기까지 하면 스팀덱 홈 화면은 TV에 잘 나온다. 그런데 게임을 실행하면 화면이 회색으로 바뀐다. 원인은 아직 못 찾았다.
그래서 TV 쪽은 당분간 Steam Link를 쓴다. Google TV에 Steam Link 앱을 깔면 같은 LAN에 있는 스팀덱을 Steam 원격 플레이 호스트로 바로 잡는다. 따로 깔 것도, 설정할 것도 없고 게임 화면도 정상으로 나온다. 회색 화면을 해결하면 Moonlight로 다시 옮길 생각이다.
정리
- SteamOS는
/가 읽기 전용이라 패키지 대신deck-tailscale스크립트로 Tailscale을 깐다./opt·atomic-update.conf.d덕에 OS 업데이트에도 남는다 - 호스트 PC와 스팀덱이 같은 tailnet에 있으면 Moonlight에 tailnet IP만 넣으면 어느 네트워크에서든 붙는다. 포트포워딩은 필요 없다
- 스팀덱 해상도에 맞춘 가상 디스플레이가 필요하면 Sunshine보다 Apollo
- 스팀덱을 호스트로 쓰는 Decky Sunshine은 웹 UI 저장 후 재시작이 필수, Google TV에서는 H.264. 게임 실행 시 회색 화면은 아직 숙제라 그동안은 Steam Link