Tailscale 속도 느려짐 해결: 직접 연결 vs 릴레이 확인 방법
Tailscale 연결이 느린 이유는 릴레이 모드 때문입니다. tailscale status 명령어로 직접 연결 여부를 확인하고, UDP 차단이나 NAT 환경에서 회선 속도를 최대로 끌어올리는 구체적인 해결책을 정리했습니다.
Tailscale이 느린 이유: 직접 연결 대신 릴레이(relayed) 연결 사용
연결이 릴레이 방식으로 이루어지면 Tailscale은 느려지며, 직접 연결일 때 비로소 회선 속도에 근접한 성능을 냅니다. 직접 연결은 암호화된 WireGuard 패킷을 한 기기에서 다른 기기로 곧바로 전달하므로, 두 인터넷 회선이 허용하는 최대 속도로 동작합니다. 반면 릴레이 연결은 모든 패킷을 제3의 기기를 거쳐 전송하므로, 해당 기기의 지연 시간과 대역폭 할당량에 영향을 받습니다. Tailscale의 성능 관련 문서에는 "직접 연결은 거의 항상 더 낮은 지연 시간과 더 높은 처리량을 제공한다"라고 명시되어 있습니다.
애플리케이션 내부에서는 이러한 차이를 확인할 수 없습니다. 파일 복사가 느리거나 SSH 세션이 지연되는 현상으로만 나타날 뿐입니다. 따라서 첫 번째 작업은 현재 어떤 방식의 연결을 사용 중인지 파악하는 것입니다. 두 가지 명령어를 사용하면 1분 이내에 이를 확인할 수 있으며, 그 이후의 과정은 연결 문제를 해결하는 단계입니다. 조정 서버(coordination server)와 WireGuard 데이터 평면은 별개의 시스템이며 데이터 평면만이 실제 데이터를 전송한다는 점을 이해하고 시작하는 것이 중요합니다.
직접 연결과 릴레이 연결을 구분하는 두 가지 명령어
측정을 시작하기 전에 피어(peer)로 트래픽을 일부 전송하십시오. Tailscale은 필요에 따라 경로를 생성하므로, 오늘 통신한 적이 없는 피어는 아직 경로 협상을 마치지 않았을 수 있으며, 이 경우 오래된 정보를 읽게 될 수 있습니다. 피어의 tailnet 주소로 ping 또는 curl을 한 번만 실행하면 충분합니다.
tailscale status결과는 각 피어 정보가 표시된 줄의 끝에서 확인할 수 있습니다.
100.113.160.82 device-a tagged-devices linux active; offers exit node; direct 203.0.113.9:41641
100.104.93.78 device-b you@ android active; relay "tor"direct 뒤에 주소와 포트가 표시된다면 패킷이 해당 주소로 직접 전달되고 있다는 의미입니다. relay "tor"는 DERP(designated encrypted relay for packets) 서버를 나타내며, 이는 Tailscale의 릴레이 서버 중 하나로 해당 피어로 향하는 모든 패킷이 이 서버를 거쳐 전달됨을 뜻합니다. 세 번째 값인 peer-relay에 대해서는 다음 섹션에서 다룹니다.
tailscale ping device-b정상적인 연결은 릴레이 상태로 시작하여 직접 연결로 전환됩니다. 두 기기가 협상하는 동안 첫 패킷은 가장 가까운 DERP 서버를 통과하며, 이후 경로가 자동으로 변경됩니다.
pong from device-b (100.113.160.82) via DERP(tor) in 51ms
pong from device-b (100.113.160.82) via DERP(tor) in 48ms
pong from device-b (100.113.160.82) via 203.0.113.9:41641 in 35ms--until-direct의 기본값이 true이므로 실행은 여기서 멈춥니다. 직접 연결이 불가능한 연결은 다음과 같이 표시되며, pong 대신 문장이 출력됩니다.
pong from device-b (100.104.93.78) via DERP(tor) in 53ms
pong from device-b (100.104.93.78) via DERP(tor) in 60ms
direct connection not established마지막 줄이 결과입니다. 이는 Tailscale이 시도할 수 있는 모든 프로브를 보냈음에도 직접 경로를 찾지 못했음을 의미합니다. 첫 번째 직접 경로에서 멈추지 않고 릴레이 경로를 계속 관찰하려면 tailscale ping --until-direct=false -c 20 device-b를 실행하여 지연 시간(latency)의 변화를 확인하십시오. 릴레이 경로는 사용자가 제어할 수 없는 서버를 거쳐 두 개의 인터넷 경로가 결합된 형태이므로, 일반적으로 더 높은 수치와 더 큰 변동 폭을 보입니다.
tailscale status에서 peer-relay는 무엇을 의미합니까?
peer relay는 직접 연결이 불가능할 때 tailnet 내의 다른 구성원을 위해 트래픽을 중계하는 귀하의 장비입니다. 이 장비는 사용자가 선택한 UDP 포트에서 대기하며, tailscaled 데몬은 DERP보다 이 장비를 우선적으로 사용합니다. tailscale status은 이러한 연결을 표시하며, peer-relay, 그리고 tailscale ping은 중계 장비의 엔드포인트를 출력합니다:
pong from device-b (100.97.143.93) via peer-relay(203.0.113.42:40000:vni:1) in 4ms
direct connection not established이 내용을 주의 깊게 읽으십시오. 여전히 직접 연결은 아니므로 실행 결과는 direct connection not established로 끝납니다. 변경된 점은 중계 주체입니다. 공인 IP 주소와 충분한 대역폭을 가진 VPS는 공유 DERP 노드보다 귀하의 트래픽을 중계하기에 훨씬 적합하며, 이것이 서버를 임대하는 사용자에게 중요한 이유입니다. 공인 엔드포인트가 확보된 장비에서 다음과 같이 활성화하십시오:
sudo tailscale set --relay-server-port=400000 포트를 지정하면 사용하지 않는 임의의 포트가 선택되며, 빈 문자열을 입력하면 중계 서버가 비활성화됩니다. 그런 다음 tailnet 정책 파일의 tailscale.com/cap/relay 기능을 사용하여 클라이언트 장치에 사용 권한을 부여하십시오:
{
"grants": [
{
"src": ["tag:us-east-vpc"],
"dst": ["tag:us-east-relays"],
"app": {
"tailscale.com/cap/relay": []
}
}
]
}중계 장치와 클라이언트 장치 모두 Tailscale 1.86 이상 버전이 필요하므로, 정책 파일을 수정하기 전에 각 장치에서 tailscale version 명령어로 버전을 확인하십시오. 데몬이 연결을 시도하는 순서를 기억해 두는 것이 좋습니다. 데몬은 먼저 직접 연결을 시도합니다. 실패할 경우 사용할 수 있는 peer relay를 찾습니다. 중계 장비가 없으면 DERP로 대체합니다. DERP는 두 장비가 최초로 통신을 협상하는 채널 역할을 하므로 완전히 배제되지는 않습니다.
원인 1: UDP를 차단하는 송신(egress) 방화벽
Tailscale 문서에는 연결이 릴레이 상태로 유지되는 두 가지 이유가 명시되어 있으며, 첫 번째는 UDP 차단입니다. 장비에 직접 확인하십시오:
tailscale netcheck보고서는 여기서 잘리며, 가장 상단 필드가 모든 것을 결정합니다:
Report:
* UDP: true
* IPv4: yes, 203.0.113.9:41641
* IPv6: no
* MappingVariesByDestIP: false
* PortMapping:
* Nearest DERP: DallasUDP: false가 표시된다면 그것이 전체 답변입니다. 장비가 Tailscale의 프로브 서버로 UDP 패킷을 보낼 수 없으므로 직접 경로가 형성되지 않으며, 데몬은 TCP 포트 443을 통한 DERP로 대체(fallback)합니다. 이 대체 동작 때문에 장비가 겉으로는 완전히 정상처럼 보입니다. 연결되어 있고, 접근 가능하며, 모든 바이트가 릴레이를 통해 전달되기 때문입니다.
두 가지 아웃바운드 규칙이 문서화되어 있습니다. "내부 장치가 :41641에서 *:*로 UDP를 시작하도록 허용"하는 것은 WireGuard 트래픽 자체를 위한 것이며, "내부 장치가 *:3478으로 UDP를 시작하도록 허용"하는 것은 장비가 자신의 공인 IP 주소와 포트를 학습하는 데 사용하는 프로토콜인 STUN(session traversal utilities for NAT)을 위한 것입니다. 목적지에는 와일드카드를 사용하십시오. Tailscale은 시간이 지남에 따라 릴레이 서버를 추가하므로, 수동으로 작성한 주소 목록은 1년 안에 잘못된 정보가 됩니다.
임대 서버의 경우, 흔한 범인은 공격적인 송신 정책입니다. 이는 강화된(hardened) 이미지에서 상속받았거나 제공업체가 업스트림에서 적용한 것일 수 있습니다. 먼저 기본 아웃바운드 정책을 확인하십시오:
sudo ufw status verbose
sudo nft list rulesetDefault: deny (incoming), allow (outgoing)은 정상이며 문제가 아닙니다. 기본 아웃바운드 정책이 deny이고 TCP 443과 DNS만 허용하는 짧은 목록을 가진 경우, 이것이 바로 서버가 계속 릴레이 상태에 머물게 하는 원인입니다. TCP 443을 통한 DERP 경로는 이 구멍을 통과할 수 있지만 직접 경로는 통과하지 못하기 때문입니다. 이러한 규칙이 실제로 어디에 존재하는지는 해당 장비가 내부적으로 iptables를 실행 중인지 nftables를 실행 중인지에 따라 다르며, 잘못된 것을 수정하는 것은 아무런 변화도 일으키지 못하는 흔한 실수입니다.
인바운드 측면도 중요합니다. VPS는 공인 IP 주소를 가지고 있으므로 연결 쌍의 쉬운 쪽이 될 수 있기 때문입니다. 방화벽이 tailscaled가 수신 대기하는 포트에서 인바운드 UDP를 허용하면, 까다로운 가정용 공유기 뒤에 있는 피어들도 별도의 트릭 없이 서버에 도달할 수 있습니다. 실제로 사용 중인 포트를 찾으십시오:
sudo ss -lunp | grep tailscaled
sudo ufw allow 41641/udp41641은 기본 고정 포트입니다. randomizeClientPort 설정이 켜진 테일넷(tailnet)은 클라이언트가 무작위 포트를 선택하게 만듭니다. 이 경우 이 페이지의 값이 아닌 ss 출력에서 실제 번호를 가져오십시오. 그런 다음 제공업체의 제어판을 확인하십시오. 대부분의 호스트는 서버 내부 방화벽과는 별개인 네트워크 방화벽을 운영하며, ufw로 추가한 규칙은 이에 대해 아무런 영향을 주지 못합니다.
원인 2: 양쪽 또는 한쪽 끝의 하드 NAT
두 번째로 문서화된 원인은 하드 NAT입니다. NAT(network address translation)는 라우터가 사설 주소를 공인 주소로 재작성하는 과정입니다. 일반적인 라우터는 통신 대상과 관계없이 내부 소켓에 대해 동일한 공인 포트를 유지하는데, 이를 endpoint independent mapping이라고 합니다. 반면 하드 NAT는 목적지마다 서로 다른 공인 포트를 할당하므로, STUN 서버로부터 확인한 주소를 상대방이 그대로 사용할 수 없습니다. Tailscale은 이 상태를 netcheck에서 MappingVariesByDestIP: true로 표시합니다.
한쪽만 하드 NAT인 경우에는 통신이 가능합니다. 상대방의 공인 엔드포인트가 안정적이라면 하드 NAT 뒤에 있는 장비가 접속을 시도하여 경로를 형성할 수 있기 때문입니다. 양쪽 모두 하드 NAT인 경우에는 서로 상대방의 포트를 예측할 수 없으므로 연결에 실패합니다.
공인 IPv4 주소를 사용하는 VPS라면 주소를 변환하는 장치가 없으므로 이 항목은 false로 표시되어야 합니다. 임대한 서버에서 true으로 표시된다면 제공자의 네트워크 어딘가에서 주소 변환이 일어나고 있는 것이며, 서버 내부의 방화벽 규칙으로는 이를 해결할 수 없습니다. 이 경우 공인 엔드포인트가 정상적인 장비에 피어 릴레이를 설정하거나, 워크로드를 다른 곳으로 이전해야 합니다. 서브넷 라우터에서 사설 대역을 광고하는 방식이 유용한 경우도 바로 이때입니다. 네트워크 내의 모든 장치로 직접 연결 경로를 확보할 필요 없이, 네트워크로 진입하는 하나의 안정적인 경로만 확보하면 되기 때문입니다.
왜 출구 노드를 사용하면 Tailscale이 실제보다 느려 보이는가
출구 노드는 두 번째 홉(hop)이므로, 사용자는 그 원인을 터널 탓으로 돌리곤 합니다. 출구 노드를 선택하면 요청은 노트북을 떠나 터널을 거쳐 VPS에 도달하고, 다시 VPS에서 공용 인터넷으로 나간 뒤, 응답이 동일한 경로를 통해 돌아옵니다. VPS에 대한 연결이 아무리 완벽하게 직접적이라 해도 전체 속도는 VPS 자체의 업링크 속도를 넘을 수 없으며, 추가된 물리적 거리는 모든 페이지 로드 시 지연으로 나타납니다.
두 구간을 분리하여 측정하십시오. 출구 노드를 끄고, VPS의 tailnet 주소를 대상으로 터널 자체의 성능을 테스트하십시오:
sudo tailscale set --exit-node=
sudo apt install -y iperf3
iperf3 -s클라이언트에서 해당 tailnet 주소로 iperf3 -c 100.113.160.82을 실행하십시오. 그 결과값이 터널의 성능입니다. 이제 sudo tailscale set --exit-node=100.113.160.82로 출구 노드를 다시 켜고 공용 인터넷을 대상으로 일반적인 속도 테스트를 수행하십시오. 그 결과값은 터널과 VPS의 업링크를 합친 성능입니다. 첫 번째 수치는 양호한데 두 번째 수치가 나쁘다면, Tailscale의 문제가 아니며 출구 노드 자체의 네트워크 및 규모를 확인해야 합니다. 어떤 노드를 선택했는지 확실하지 않다면 tailscale exit-node list를 통해 현재 제공되는 노드 정보를 확인할 수 있습니다.
CPU는 출구 노드의 성능 한계를 결정하는 또 다른 요소입니다. Tailscale은 코어 수가 많은 것보다 클럭 속도가 높은 최신 CPU 세대를 선호할 것을 권장하므로, vCPU가 더 많은 요금제라고 해서 반드시 더 빠른 것은 아닙니다. 혼잡한 공유 호스트 환경에서는 약속된 CPU 성능을 온전히 보장받지 못하며, 노이즈 이웃으로 인한 스틸 타임(steal time)이 발생하면 사용자 측의 변경 사항이 없어도 시간대에 따라 처리량이 변동하게 됩니다.
단 하나의 튜닝 설정: rx-udp-gro-forwarding
Tailscale은 단 하나의 Linux 설정을 문서화하고 있으며, 이는 트래픽을 전달하는 장비, 즉 exit node와 subnet router에 적용됩니다. 일반 클라이언트는 이 설정으로 이득을 얻지 못합니다. 이 설정은 Tailscale 1.54 이상 버전과 Linux 커널 6.2 이상이 필요하므로, 설정을 변경하기 전에 두 가지 모두 확인하십시오:
tailscale version
uname -r요건이 충족되었다면, 인터넷과 연결된 인터페이스에서 UDP GRO(generic receive offload) 포워딩을 활성화하십시오:
NETDEV=$(ip -o route get 8.8.8.8 | cut -f 5 -d " ")
sudo ethtool -K $NETDEV rx-udp-gro-forwarding on rx-gro-list off설정이 적용되었는지 확인하십시오:
ethtool -k $NETDEV | grep -E 'rx-udp-gro-forwarding|rx-gro-list'rx-udp-gro-forwarding: on 및 rx-gro-list: off이 출력되어야 합니다. 이 설정이 도움이 되는 이유는 Tailscale의 트래픽이 UDP 기반이기 때문입니다. 커널이 전달 경로상에서 작은 UDP 패킷들을 병합(coalesced)된 상태로 유지하게 하면, 데몬은 동일한 바이트 수에 대해 더 적고 큰 세그먼트를 처리하게 됩니다. ethtool -K 설정은 재부팅 시 유지되지 않으므로 영구적으로 적용해야 합니다. networkd-dispatcher를 사용하는 시스템의 경우 다음과 같이 하십시오:
printf '#!/bin/sh\n\nethtool -K %s rx-udp-gro-forwarding on rx-gro-list off \n' "$(ip -o route get 8.8.8.8 | cut -f 5 -d " ")" | sudo tee /etc/networkd-dispatcher/routable.d/50-tailscale
sudo chmod 755 /etc/networkd-dispatcher/routable.d/50-tailscale스크립트를 수동으로 한 번 실행하고 종료 상태가 0인지 확인하십시오. 포워딩 노드는 우선 IP 포워딩이 활성화되어 있어야 하며, 이는 별도의 설정 항목이자 별개의 실패 원인이 될 수 있습니다:
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conftailscale0 인터페이스는 어떤 MTU를 사용합니까?
이 값을 추측하지 말고 장비에서 직접 확인하십시오:
ip link show tailscale0출력값 중 mtu 항목이 실제 터널이 사용하는 값이며, 이더넷 인터페이스가 보고하는 1500보다 낮습니다. 이는 버그가 아니라 의도된 설계입니다. 터널 내부로 전송되는 모든 패킷은 캡슐화됩니다. IPv4는 20바이트, IPv6는 40바이트의 외부 IP 헤더, 8바이트의 UDP 헤더, 그리고 32바이트의 WireGuard 프레이밍 및 인증 태그가 추가됩니다. 이 모든 데이터는 실제 경로가 수용할 수 있는 크기 내에 들어가야 합니다. Tailscale은 PPPoE 연결, 일부 모바일 네트워크, IPv6 터널 등 1500바이트 패킷을 온전히 전달하지 못하는 링크에서도 통신이 가능하도록 충분히 낮은 값을 선택합니다.
MTU 문제의 증상은 명확하므로 단순히 속도가 느리다는 이유만으로 진단해서는 안 됩니다. SSH는 반응이 빠르고 ping 명령도 잘 작동하지만, 대용량 전송이나 큰 HTTPS 페이지를 불러올 때 속도가 느려지는 것이 아니라 아예 멈춰버립니다. 이러한 패턴은 크기가 너무 큰 패킷이 어딘가에서 폐기되고 있으며, 송신자에게 이를 알리는 ICMP 메시지가 돌아오지 않음을 의미합니다. tailscale0 MTU를 1500에 가깝게 높이는 것은 이미 맞지 않는 패킷을 더 크게 만들어 상황을 악화시킬 뿐입니다. 근본적인 해결책은 이분 탐색으로 실제 경로 MTU를 찾고 트래픽을 전달하는 라우터에서 TCP MSS를 클램핑하는 것입니다. 연결 방식은 이 과정에 영향을 주지 않으며, 릴레이 경로와 직접 경로 모두 동일한 인터페이스 MTU를 사용합니다.
FAQ
Tailscale 연결이 직접 연결인지 릴레이 연결인지 어떻게 확인합니까?
tailscale status 명령을 실행하고 피어 행의 끝부분을 확인합니다. direct 203.0.113.9:41641은 직접 연결을 의미하며, relay "tor"은 모든 패킷이 해당 DERP 서버를 거쳐 전달됨을 의미합니다. peer-relay는 패킷이 사용자의 tailnet 내 다른 장비를 거쳐 전달됨을 의미합니다. 추가 확인이 필요하면 tailscale ping <peer>을 실행하십시오. 정상적인 경로는 DERP로 시작하여 일반 주소와 포트가 포함된 pong을 출력하지만, 릴레이 경로는 실행이 끝날 때까지 DERP pong만 출력하다가 direct connection not established로 종료됩니다. Tailscale은 요청이 있을 때만 경로를 생성하므로, 먼저 해당 피어로 트래픽을 일부 전송한 뒤 확인하십시오.
VPS에서 직접 연결이 전혀 되지 않는 이유는 무엇입니까?
VPS에서 tailscale netcheck를 실행하십시오. 만약 UDP: false이 출력된다면, 송신 방화벽이 아웃바운드 UDP를 차단하고 있어 데몬이 TCP 443을 통한 DERP로 우회한 상태입니다. 이 때문에 장비가 연결된 것처럼 보입니다. 포트 41641에서 모든 목적지로 향하는 아웃바운드 UDP와, 모든 목적지의 포트 3478로 향하는 아웃바운드 UDP를 허용하십시오. 서버 내부 방화벽뿐만 아니라 클라우드 제공업체의 네트워크 방화벽도 확인해야 합니다. 이 둘은 별개의 제어 영역이며, ufw 규칙은 제공업체의 방화벽에 영향을 주지 않습니다.
릴레이된 Tailscale 연결은 직접 연결보다 보안이 떨어집니까?
아닙니다. DERP 서버는 복호화할 수 없는 WireGuard 패킷을 전달할 뿐입니다. 암호화 키는 사용자의 장비에서 생성되며 장비 외부로 유출되지 않기 때문입니다. 릴레이 사용 시 발생하는 비용은 기밀성이 아니라 지연 시간과 처리량입니다. 조정 서버(coordination server)가 제어하는 것은 어떤 장비가 서로를 인식할 수 있는지에 대한 정보이며, 키 자료와 연결 메타데이터의 분리에 대해 이해한 뒤 자체 호스팅 범위를 결정하는 것이 좋습니다.
rx-udp-gro-forwarding 설정은 모든 장비에 도움이 됩니까?
아닙니다. 이 설정은 다른 장비를 위해 트래픽을 전달하는 리눅스 장비, 즉 exit node나 subnet router를 위해 문서화된 기능입니다. 자신의 피어와만 통신하는 노트북이나 서버는 이 설정으로 얻는 이점이 없습니다. 또한 Tailscale 1.54 이상 버전과 리눅스 커널 6.2 이상이 필요하므로 먼저 tailscale version와 uname -r으로 확인하십시오. ethtool -K 설정은 재부팅 시 초기화되므로 영구 적용해야 함을 기억하십시오.
Tailscale이 일반 WireGuard보다 느립니까?
두 방식 모두 트래픽 암호화에 WireGuard를 사용합니다. Tailscale은 일반 WireGuard에서 사용자가 수동으로 수행해야 하는 연결 설정을 자동화하며, 이 설정 과정에서 간혹 릴레이를 거치게 됩니다. 일반 WireGuard는 릴레이가 없으므로 직접 연결되거나 아예 연결되지 않습니다. 따라서 동일한 조건에서 비교해야 하며, tailscale status 명령의 결과가 direct일 때만 Tailscale의 성능을 측정하십시오. 비교를 위해 직접 설정한 버전을 원한다면 직접 구축하는 WireGuard 서버는 약 40줄의 설정이 필요하며, 이 과정에서 감수해야 할 사항은 두 방식의 비교에서 확인할 수 있습니다.