Yule Seo
2026.05.21 · 8 min read

Openclaw webchat UI Tailscale 로 서빙하기

이번 글에서는 최근 세팅한 오픈클로(Openclaw) 환경에 대해 공유해보려고 한다. 오픈클로를 설치 및 세팅하는 방법과 가이드는 아주 많이 찾아볼 수 있고 이것조차 AI에게 해달라고 하면 다 해준다. 하지만 그 중에서도 tailscale 통해서 webchat dashboard UI 를 외부 네트워크에서 접근하는 방법에 대해 서술한 아티클은 별로 없는 것 같아 이야기해보려고 한다.

내 오픈클로 환경

불과 몇달 전에 오픈클로가 붐이 일었고 맥미니가 품귀현상을 빚었었는데, 나는 이제서야 조금 써보기 시작한 단계이다. 내가 세팅했던 오픈클로 환경은 대충 다음과 같다.

  • 하드웨어: N100 미니 PC (RAM 16GB, SSD 512GB). 팬리스에 전력도 거의 안 먹어서 24시간 켜두는 홈서버. 로컬 모델 돌려볼게 아니면 맥미니를 살 필요가 없다.
  • OS: Ubuntu Server 24.04 LTS. GUI 가 필요하지도 않다.
  • 운영 방식: 모니터 없이 메인 맥북(M1 Pro)에서 SSH로 접속해 관리. OpenClaw 게이트웨이는 systemd 유저 서비스로 24/7 상시 구동.
  • 바인딩: webchat/대시보드 게이트웨이는 127.0.0.1:18789(loopback)에만 떠 있음 → 기본 상태에선 그 머신 안에서만 접근 가능. 이게 뒤에서 중요해진다.
  • 채널: 텔레그램 연동
  • 모델: Claude CLI 런타임 (메인은 Opus, 보조로 Sonnet/Haiku)

많은 사람들이 오픈클로와 통신하는 채널로 텔레그램이나 디스코드와 같은 메신저를 연동하는데, 이게 결국에는 등록된 Cronjob 상태를 확인하고 싶다거나 Skill 을 확인하고 싶다거나, 다른 subagent 세션을 확인하고 싶은 상황이 생겨서 webUI 를 확인하게 되는 것 같았다. 그리고 결정적으로 다음 이미지에서와 같이 텔레그램으로부터 마크다운 텍스트를 받으면 줄바꿈이 시원찮아서 읽기가 불편해지는데, 이거 때문에 디스코드로 갈아타든가 아예 텔레그램 포크를 떠볼까 생각했지만 webchat UI 를 두고 굳이 그럴 것 까진 없을 것 같아, 결국엔 터널링 서비스인 tailscale 을 써서 로컬에 접속할 수 있게 해주기로 했다.

텔레그램의 마크다운 줄바꿈 문제
텔레그램의 마크다운 줄바꿈 문제

Tailscale 을 간단히 소개하자면, WireGuard 기반의 메시 VPN(mesh VPN) 서비스다. 내가 가진 기기들(서버, 맥북, 아이폰 등)을 tailnet 이라고 부르는 하나의 사설 네트워크로 묶어주고, 각 기기에 100.x.y.z로 시작하는 고정 IP와 머신이름.tailnet이름.ts.net 형태의 MagicDNS 주소를 자동으로 붙여준다. 가장 큰 장점은 양쪽 기기가 NAT나 방화벽 뒤에 있어도 알아서 구멍을 뚫어(NAT traversal) 암호화된 직접 연결을 만들어준다는 점이다. 심지어 개인용으로는 무료.

그래서 따로 내부 네트워크의 포트포워딩 설정을 건드리지 않아도 된다. 나 같은 경우엔 처음에 귀찮아서 포트포워딩해서 그냥 만천하에 공개해버릴까 했었는데(어차피 openclaw gateway token 으로 접속이 불가능하기도 하고), 왠진 자세히 모르겠지만 공유기 설정에서 포트포워딩이 적용이 잘 안됐다;; 그래서 어쩔 수 없이 더 좋은 방안을 선택하게 된 것도 있다.

tailscale serve 설정한 방법

그래서 결론은 다음과 같이 설정했다.

일단 서버랑 맥북 양쪽에 Tailscale을 깔고 같은 계정으로 로그인하는 것부터 시작한다. 우분투 쪽은 공식 설치 스크립트 한 줄이면 끝이다.

bash
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

tailscale up을 하면 로그인 URL이 뜨는데, 브라우저로 열어서 인증하면 이 서버가 tailnet에 등록된다. 맥은 그냥 앱스토어(혹은 공식 사이트)에서 앱 받아서 같은 계정으로 로그인하면 끝. 이러면 양쪽이 같은 tailnet에 묶이고, 서버한테 user-xxx.xxxx.ts.net 같은 주소가 생긴다.

처음엔 단순하게 "그럼 이제 맥북에서 서버 Tailscale IP로 http://100.x.y.z:18789 치면 되겠네?" 싶었는데, 안 된다. 게이트웨이가 127.0.0.1(loopback)에만 바인딩돼 있어서다.

loopback이 뭐냐면, 127.0.0.1(localhost)은 '자기 자신에게만' 연결되는 가상 네트워크 인터페이스다. 이 주소에 띄운 서비스는 그 머신 안에서 도는 프로세스끼리만 접근할 수 있고, 다른 기기는 물론 같은 공유기에 물린 다른 PC나 Tailscale 인터페이스에서도 닿지 않는다. 외부에 함부로 노출하고 싶지 않은 서비스의 안전한 기본값으로 흔히 쓰인다.

그러니까 게이트웨이가 loopback에만 떠 있으면 Tailscale로 들어오는 연결도 당연히 못 받는다.

그리고 사실 이게 글 앞에서 "공유기 포트포워딩이 안 됐다"고 했던 진짜 이유이기도 했다. 포트포워딩이 하는 일은 공인 IP로 들어온 패킷을 내부 사설 IP(192.168.x.x):18789로 전달해주는 것뿐인데, 정작 게이트웨이는 그 랜 IP가 아니라 loopback에만 귀를 열고 있으니 받아줄 리스너가 없다. 즉 공유기 설정이 설령 완벽하게 먹혔어도 패킷은 서버의 랜 인터페이스까지만 도착하고 거기서 connection refused였을 거란 얘기다. 당시엔 "공유기가 왜 이러지?" 하고 라우터 탓만 했는데, 알고 보니 라우터 문제와 별개로 어차피 안 될 일이었던 것이다.

이걸 제대로 열려면 게이트웨이를 0.0.0.0(모든 네트워크 인터페이스에서 수신)이나 랜 IP로 다시 바인딩까지 했어야 했다. 근데 그러기는 싫었다. loopback only인 게 보안상 깔끔하니까.

이걸 해결해주는 게 tailscale serve다. tailnet으로 들어온 요청을 서버 로컬의 http://127.0.0.1:18789로 리버스 프록시해주는 기능인데, 게이트웨이는 loopback에 그대로 둔 채로 외부 노출만 Tailscale이 대신 해주는 셈이다. 덤으로 *.ts.net HTTPS 인증서까지 자동으로 붙여줘서 브라우저 자물쇠도 정상으로 뜬다. 포인트는 Tailscale 데몬(tailscaled)이 서버 위에서 직접 돌면서 로컬에서 127.0.0.1로 연결을 대신 맺어준다는 거다. "바깥에서 던지는" 포트포워딩과 달리 "안에서 대신 두드려주는" 방식이라, 게이트웨이를 loopback에 그대로 둬도 동작한다. 아까 포트포워딩이 안 됐던 이유가 여기서 정확히 뒤집히는 셈이다.

그래서 처음엔 그냥 수동으로 띄워봤다.

bash
tailscale serve --bg 18789

이러니까 https://user-xxx.xxxx.ts.net/로 진짜 들어가지긴 했다. 근데 webchat이 바로 열리는 게 아니라, OpenClaw 대시보드가 "인증 필요" 화면을 띄우면서 게이트웨이 토큰이나 비밀번호를 넣으라고 했다. (이게 위에서 말한 그 gateway token이다.) Tailscale로 거기까지 닿는 것과, 오픈클로 게이트웨이 자체의 인증을 통과하는 건 별개의 문제였던 거다.

토큰을 매번 복붙하긴 귀찮아서 더 찾아보니, OpenClaw가 Tailscale을 native하게 지원하고 있었다. 설정 파일(보통 openclaw.json)에 아래처럼 넣으면 두 가지가 한 번에 해결된다.

json5
{
  gateway: {
    bind: "loopback",
    tailscale: { mode: "serve" },     // OpenClaw가 tailscale serve를 알아서 띄워줌
    auth: { allowTailscale: true },   // Tailscale 신원 헤더로 인증 대체 (토큰 불필요)
  },
}

tailscale.mode: "serve"를 주면 위에서 내가 수동으로 했던 tailscale serve를 OpenClaw가 게이트웨이 띄울 때 알아서 해준다. 그리고 auth.allowTailscale: true가 핵심인데, Tailscale Serve가 요청에 붙여주는 신원 헤더(tailscale-user-login)를 게이트웨이가 tailscale whois로 검증해서, 토큰이나 비번을 안 넣어도 "tailnet에 로그인된 본인 기기"면 그냥 통과시켜준다. 토큰 복붙이 사라지니 훨씬 편하다. (대신 이 방식은 게이트웨이 호스트 자체가 신뢰된다는 전제이므로, 같은 머신에서 신뢰할 수 없는 코드가 돌 가능성이 있으면 그냥 토큰 인증을 쓰는 게 맞다.)

문제는 여기서 터졌다. 설정 바꾸고 게이트웨이를 재시작했더니 이번엔 아예 "연결 불가능"이라고 떴다. 아까는 그래도 인증 화면까진 갔는데, 이번엔 그것조차 안 되는 더 안 좋은 상황이었다;;

원인은 로그를 보고 바로 잡았다.

bash
journalctl --user -u openclaw-gateway.service -n 100 --no-pager | grep -i -E "tailscale|serve|error"
text
sudo: a password is required ; COMMAND=/usr/bin/tailscale serve --bg --yes 18789
[tailscale] serve failed: Command failed: /usr/bin/tailscale serve --bg --yes 18789
sending serve config: Access denied: serve config denied
Use 'sudo tailscale serve --bg --yes 18789'.
To not require root, use 'sudo tailscale set --operator=$USER' once.

요약하면, OpenClaw 게이트웨이는 systemd 유저 서비스(권한 없는 일반 유저)로 도는데, tailscale serve 설정을 바꾸려면 root 권한이 필요해서 막혔던 거다. 아까 내가 수동으로 띄울 땐 내 셸에서 직접 실행했으니 됐던 거고, 이번엔 OpenClaw가 대신 실행하려니까 권한이 없어서 조용히 실패 → serve가 안 떠서 "연결 불가능"이 된 거였다.

다행히 로그가 해결법까지 친절하게 알려줬다. 일반 유저가 sudo 없이 tailscale을 다룰 수 있도록 operator 권한을 딱 한 번만 주면 된다.

bash
sudo tailscale set --operator=$USER

그리고 아까 수동으로 띄워놨던 serve는 이제 OpenClaw가 관리할 거라 소유권이 겹친다. 깔끔하게 정리해주고,

bash
tailscale serve reset

게이트웨이를 다시 시작한다.

bash
openclaw gateway restart

이번엔 로그에 제대로 떴다.

text
[tailscale] serve enabled: https://user-xxx.xxxx.ts.net/ (WS via wss://user-xxx.xxxx.ts.net)

tailscale serve status로 확인해봐도 https://user-xxx.xxxx.ts.net → http://127.0.0.1:18789로 잘 잡혀 있다.

이제 끝이다. 맥북이든 아이폰이든 Tailscale 앱만 깔고 같은 계정으로 로그인한 뒤 켜놓으면, 집 밖 LTE에서도 https://user-xxx.xxxx.ts.net/로 바로 webchat에 접속된다. 토큰을 따로 넣을 필요도 없고(allowTailscale가 알아서 처리), 한 번 해두면 재부팅을 하든 게이트웨이를 재시작하든 serve 설정이랑 operator 권한이 유지돼서 주기적으로 인증을 갱신해줄 일도 없다.

딱 하나 신경 쓸 건 Tailscale 기기 키 만료다. 기본값이 약 180일이라 그냥 두면 그 주기마다 재로그인을 해야 하는데, 24시간 켜두는 서버는 admin 콘솔(login.tailscale.com/admin/machines)에서 해당 기기의 key expiry를 꺼주면 영영 안 끊긴다.

Tailscale admin 콘솔의 Disable Key Expiry
Tailscale admin 콘솔의 Disable Key Expiry

정리하면, 게이트웨이는 loopback에 그대로 두고 공유기 포트는 하나도 안 열었는데도 외부에서 HTTPS로 안전하게 접근할 수 있게 됐다. 처음에 포트포워딩으로 만천하에 공개하려던 것보다 훨씬 안전하고 깔끔한 결말이다.

마지막으로, 아이폰에서도 Tailscale 앱을 깔고 사파리에서 tailnet 주소로 들어가준 다음, 보기 좋게 PWA 로 홈화면에 저장해줬다.