SSD Nodes Learn Hosting plans →
가이드 Matt Connor작성자 Matt Connor

NetBird vs Headscale 비교: 셀프호스팅 메시 VPN 선택

Tailscale의 클라우드 조정 서버를 피하려면 선택지는 둘입니다. Headscale v0.29.3과 NetBird v0.79.0을 인증, 정책, 릴레이 위치, VPS 운영 비용 기준으로 비교하고 어느 쪽을 고를지 정리합니다.

결론부터: 같은 문제를 다른 층에서 푸는 두 프로젝트

NetBird와 Headscale 비교의 출발점은 두 프로젝트가 Tailscale의 어느 부분을 대체하느냐입니다. Headscale은 Tailscale의 조정 서버(coordination server)만 다시 구현한 프로젝트입니다. 클라이언트는 Tailscale 공식 앱을 그대로 씁니다. NetBird는 관리 서버, 시그널 서버, 릴레이, 대시보드, 클라이언트까지 전부 자체 구현한 풀스택입니다. 그래서 선택은 "어느 쪽이 더 좋은가"가 아니라 "Tailscale 클라이언트를 계속 쓸 것인가"에서 갈립니다.

결정 규칙은 짧습니다. 이미 모든 기기에 Tailscale 앱이 깔려 있고 개인용 소규모 네트워크라면 Headscale입니다. 팀 단위로 쓰고 SSO(single sign-on, 통합 로그인)와 웹 UI가 필요하면 NetBird입니다. 아래는 그 규칙이 왜 그렇게 나오는지, 2026년 9월 20일 기준 Headscale v0.29.3과 NetBird v0.79.0의 공식 문서를 대조해 정리한 근거입니다.

NetBird와 Headscale이 대체하는 범위

Tailscale은 세 부분으로 나뉩니다. 각 기기에 설치되는 클라이언트, 기기들의 공개 키와 주소를 교환해 주는 조정 서버, 그리고 직접 연결이 실패했을 때 패킷을 중계하는 DERP(Designated Encrypted Relay for Packets) 릴레이입니다. 클라이언트는 오픈 소스지만 조정 서버는 Tailscale 회사의 클라우드에서만 돕니다. 실제 트래픽은 WireGuard로 종단 간 암호화되므로 조정 서버가 내용을 볼 수는 없습니다. 그래도 어떤 기기가 어떤 기기와 연결되는지, 누가 로그인했는지는 그 서버가 결정합니다. Tailscale 자체가 처음이라면 Tailscale이 무엇이고 어떻게 동작하는지를 먼저 읽는 편이 빠릅니다. 이 의존을 없애고 싶을 때 나오는 답이 Headscale과 NetBird입니다.

두 프로젝트가 대체하는 범위가 다릅니다. Headscale은 조정 서버 하나만 대체합니다. 클라이언트는 그대로이고, DERP도 기본 설정에서는 그대로 Tailscale의 것을 씁니다. NetBird는 세 부분을 전부 자기 것으로 바꿉니다. 이 차이가 이후 모든 비교 축의 원인입니다.

아키텍처: 단일 바이너리 vs 컨테이너 묶음

Headscale은 Go로 작성된 바이너리 하나입니다. Debian 계열에서는 GitHub 릴리스의 .deb 패키지로 설치합니다. 설정은 /etc/headscale/config.yaml 한 파일, 상태는 /var/lib/headscale/db.sqlite라는 SQLite 파일 하나에 들어갑니다.

HEADSCALE_VERSION=0.29.3
HEADSCALE_ARCH=amd64
wget --output-document=headscale.deb \
  "https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_${HEADSCALE_ARCH}.deb"
sudo apt install ./headscale.deb

이 패키지는 전용 시스템 사용자, 기본 설정 파일, systemd 유닛 headscale을 함께 설치합니다. config.yamlserver_url을 실제 도메인으로 고친 뒤 sudo systemctl enable --now headscale로 시작합니다. 기본 listen_addr127.0.0.1:8080이므로 앞에 TLS(transport layer security)를 끝내 주는 리버스 프록시가 하나 필요합니다. 그게 전부입니다.

NetBird v0.79.0의 빠른 설치는 Docker Compose 묶음입니다. 공식 스크립트가 만드는 서비스는 netbirdio/netbird-server(관리, 시그널, 릴레이, STUN, 임베디드 Dex가 한 이미지에 통합됨), netbirdio/dashboard, 그리고 기본 선택지인 traefik 컨테이너입니다. 설치 중 선택에 따라 netbirdio/reverse-proxy와 CrowdSec 컨테이너가 추가됩니다.

curl -fsSL https://github.com/netbirdio/netbird/releases/latest/download/getting-started.sh | bash

스크립트는 실행 전에 Docker Compose v2, jq, curl이 있는지와 입력한 도메인이 이 서버의 공개 IP로 풀리는지를 확인합니다. 도메인이 없으면 여기서 멈춥니다. 주의할 점이 하나 있습니다. 생성되는 Compose 파일의 이미지 태그가 latest입니다. 재현 가능한 서버를 원하면 스크립트 실행 전에 NETBIRD_SERVER_IMAGE 환경 변수로 이미지 태그를 고정하십시오.

인증: pre-auth key와 부분 OIDC vs 임베디드 Dex

Headscale의 기본 인증은 두 가지입니다. 하나는 웹 인증입니다. 클라이언트에서 tailscale up --login-server https://hs.example.com을 실행하면 등록 URL이 출력되고, 관리자가 서버에서 그 요청을 승인합니다.

sudo headscale users create alice
sudo headscale auth register --user alice --auth-id <AUTH_ID>

다른 하나는 pre-auth key(사전 인증 키)입니다. 서버에서 키를 만들고 클라이언트에 넘기면 승인 단계가 사라집니다. 문서 기준 기본값은 1시간 유효, 1회 사용입니다.

sudo headscale users list
sudo headscale preauthkeys create --user 1
tailscale up --login-server https://hs.example.com --authkey <YOUR_AUTH_KEY>

headscale users create의 인수는 이름이고 preauthkeys create --user의 인수는 숫자 사용자 ID입니다. 헷갈리기 쉬우니 users list로 ID를 먼저 확인하십시오.

OIDC(OpenID Connect) 로그인도 됩니다. config.yamloidc: 블록에 issuer, client_id, client_secret을 넣고 pkce.enabled를 켜면 로그인 때마다 이메일과 이름이 동기화됩니다. allowed_domains, allowed_users, allowed_groups로 누가 로그인할 수 있는지 거를 수 있습니다. 여기까지가 Headscale 문서가 말하는 지원 범위입니다. 한계는 한 줄입니다. 문서 원문은 "OIDC groups cannot be used in policy rules"입니다. 즉 IdP(identity provider, 신원 공급자)의 그룹은 로그인 필터로만 쓰이고, 접근 정책에서 참조할 수 없습니다. 정책에 쓸 그룹은 정책 파일 안에서 사용자 이름을 나열해 따로 정의해야 합니다.

NetBird는 사용자 관리가 서버 안에 들어 있습니다. 설치 직후 https://netbird.example.com/setup 페이지에서 첫 관리자를 만듭니다. 이 페이지는 사용자가 한 명도 없을 때만 열립니다. 그 뒤로는 대시보드에서 사용자를 추가합니다. 뒤에서 도는 것은 임베디드 Dex 서버입니다. 외부 IdP를 붙이려면 Zitadel, Keycloak, Authentik, PocketID 같은 셀프호스팅 IdP나 Microsoft Entra ID, Google Workspace, Okta, Auth0, JumpCloud를 문서대로 연결합니다. 일반 OIDC 규격도 지원하므로 이미 Authentik으로 SSO를 셀프호스팅하고 있다면 그 계정 체계를 그대로 씁니다. 단, IdP의 사용자와 그룹을 자동으로 가져오는 IdP-Sync는 문서에 "only available in the cloud version of NetBird in the Team plan and above"라고 적혀 있습니다. 셀프호스팅에서는 그룹을 대시보드에서 직접 배정하거나 setup key의 자동 그룹 기능으로 채웁니다.

기기 등록은 setup key 하나로 끝납니다.

netbird up --setup-key <SETUP_KEY> --management-url https://netbird.example.com:443

정책 관리: HuJSON 정책 파일 vs 대시보드의 그룹과 정책

Headscale의 접근 제어는 Tailscale과 같은 HuJSON(주석과 후행 쉼표를 허용하는 JSON) 형식의 정책 파일입니다. config.yaml에서 policy.mode: file을 두고 policy.path에 경로를 지정합니다. 빈 파일 {}는 전체 허용이고 {"grants": []}는 전체 차단입니다. Headscale 문서는 ACL(access control list) 문법 대신 grants를 권장합니다. Tailscale이 ACL을 레거시로 보고 새 기능을 붙이지 않기 때문입니다. autogroup:member, autogroup:tagged, autogroup:internet 같은 자동 그룹과 태그도 쓸 수 있습니다. 다만 문서는 autogroup:self가 큰 배포에서 서버 성능을 떨어뜨릴 수 있다고 경고합니다.

파일을 고친 뒤에는 서버에 알려야 합니다. 그냥 저장만 하면 예전 정책이 그대로 적용됩니다.

sudo systemctl reload headscale

정책이 파일이라는 점은 장점이자 단점입니다. Git으로 이력을 남기고 리뷰할 수 있습니다. 반면 공식 웹 UI가 없어서 기기 목록, 키 발급, 정책 편집을 모두 SSH로 들어가 CLI로 합니다. 서드파티 UI가 여럿 있지만 Headscale 프로젝트가 관리하는 것은 아닙니다.

NetBird는 대시보드가 곧 정책 도구입니다. 피어를 그룹에 넣고, 정책은 출발 그룹, 목적 그룹, 프로토콜, 포트, 방향(양방향 또는 단방향)으로 정의합니다. 계정을 만들면 All 그룹을 출발과 목적으로 삼는 Default 정책이 자동으로 생깁니다. 즉 처음에는 모든 피어가 서로 통신하는 풀 메시 상태입니다. 팀 환경이라면 이 정책부터 비활성화하고 그룹별로 다시 여는 것이 순서입니다. 그룹 배정은 피어 페이지의 수동 선택과 setup key에 미리 지정한 자동 그룹, 두 경로가 있습니다. 정책에 posture check(기기 상태 검사)를 붙이는 기능도 문서에 있습니다.

각각 못 하는 것

Headscale 문서의 미지원 목록에는 Funnel, Serve, 네트워크 흐름 로그가 올라 있습니다. Funnel과 Serve는 Tailscale에서 서비스를 tailnet 안 또는 인터넷으로 내보내는 기능인데, Headscale에서는 둘 다 동작하지 않습니다. 이 두 기능에 의존하고 있다면 Headscale로 옮기는 순간 리버스 프록시를 따로 세워야 합니다. 앞서 말한 OIDC 그룹 제한과 공식 UI 부재도 같은 목록에 들어갑니다.

NetBird의 빈칸은 문서를 뒤져야 보입니다. 첫째, Tailscale 클라이언트와 호환되지 않습니다. 프로토콜이 다르므로 모든 기기에 NetBird 클라이언트를 새로 설치합니다. 둘째, Taildrop 같은 기기 간 파일 전송 기능이 문서에 없습니다. 셋째, IdP-Sync와 EDR(endpoint detection and response) 통합은 문서에 클라우드 전용으로 명시되어 있습니다. 넷째, 플랫폼 목록이 다릅니다. NetBird 문서는 Linux, macOS, Windows, iOS, Android에 더해 Synology, TrueNAS, pfSense, OPNsense, OpenWrt, MikroTik, Proxmox VE 설치 안내를 제공하지만 OpenBSD와 tvOS는 없습니다. Headscale은 Tailscale 클라이언트가 도는 곳이면 어디든 됩니다.

반대로 Tailscale Funnel에 해당하는 기능은 NetBird에 있습니다. 설치 스크립트에서 NetBird Proxy 서비스를 켜면 대시보드에서 고른 내부 리소스만 인터넷에 노출할 수 있습니다. 이때 *.netbird.example.com 와일드카드 DNS 레코드가 서버 IP를 가리켜야 합니다. 설치 때 건너뛰었다면 문서의 마이그레이션 가이드로 나중에 추가할 수 있습니다.

릴레이 위치: 한국에는 공개 DERP가 없다

직접 연결이 되면 릴레이는 쓰이지 않습니다. 문제는 안 될 때입니다. 통신사 CGNAT(carrier-grade NAT, 통신사 단위 주소 변환) 뒤의 모바일 기기나 회사 방화벽 안의 PC는 자주 릴레이로 떨어집니다. 그때 릴레이가 어디 있느냐가 체감 속도를 정합니다.

2026년 9월 20일 기준 Tailscale 공개 DERP 맵에는 서울 리전이 없습니다. 동아시아 리전은 도쿄, 홍콩, 싱가포르입니다. 한국 기기 둘이 릴레이로 통신하면 패킷이 일본이나 홍콩을 거쳐 돌아옵니다. 여기서 지연 수치를 제시하지는 않습니다. 셀프호스팅의 이점은 측정된 밀리초가 아니라 릴레이를 국내 VPS에 둘 수 있다는 배치의 자유입니다.

중요한 사실이 하나 있습니다. Headscale을 셀프호스팅해도 릴레이는 기본값에서 여전히 Tailscale의 것입니다. config-example.yamlderp.urlshttps://controlplane.tailscale.com/derpmap/default를 가리키고 derp.server.enabledfalse이기 때문입니다. 조정 서버만 내 것이고 릴레이는 남의 것인 상태입니다. 릴레이까지 내 것으로 만들려면 임베디드 DERP를 켜고 공개 맵을 지웁니다.

derp:
  server:
    enabled: true
    region_id: 999
    region_code: "seoul"
    region_name: "My Seoul DERP"
    stun_listen_addr: "0.0.0.0:3478"
  urls: []
  paths: []
  auto_update_enabled: false

이렇게 하면 UDP 3478(STUN, Session Traversal Utilities for NAT)이 추가로 열려야 하고, DERP 트래픽은 Headscale의 HTTPS 주소를 그대로 탑니다. NetBird는 이 고민이 없습니다. 릴레이가 스택의 일부라서 서버를 둔 곳이 곧 릴레이 위치입니다. v0.29.0부터 도입된 WebSocket 기반 릴레이가 리버스 프록시 뒤에서 TCP 443을 함께 쓰고, STUN은 UDP 3478입니다. 두 프로젝트 모두 릴레이는 WireGuard로 암호화된 패킷을 그대로 넘길 뿐 내용을 복호화하지 못합니다. NetBird 문서 원문은 "traffic that flows through the Relay cannot be decrypted"입니다. 지금 연결이 직접인지 릴레이인지 확인하는 방법은 Tailscale이 느릴 때 직접 연결과 릴레이를 구분하는 방법에 정리되어 있습니다.

작은 VPS에서의 운영 비용

NetBird 문서가 요구하는 최소 사양은 1 CPU, 2 GB 메모리입니다. 공개로 열어야 하는 포트는 TCP 80, TCP 443, UDP 3478입니다. 리버스 프록시 없이 직접 노출하는 고급 구성에서는 TCP 33073(관리 gRPC), 10000(시그널), 33080(릴레이)이 더 열립니다. 공개 도메인은 필수이고, 기본 선택지인 Traefik이 Let's Encrypt로 인증서를 받아 줍니다. 컨테이너가 여럿이라 업그레이드는 docker compose pulldocker compose up -d이며, 이미지 태그를 고정하지 않으면 그 순간 어느 버전으로 올라가는지 알 수 없습니다.

Headscale 문서에는 최소 사양이 적혀 있지 않습니다. 바이너리 하나와 SQLite 파일 하나이므로 가장 작은 VPS 플랜에서도 부담이 없다는 것이 일반적인 경험이지만, 측정한 수치는 제시하지 않습니다. 포트는 리버스 프록시의 TCP 443 하나이고, 임베디드 DERP를 켤 때만 UDP 3478이 추가됩니다. 업그레이드는 새 .deb를 설치하고 재시작하는 것이며, 백업은 config.yamldb.sqlite 두 파일입니다.

정리하면 Headscale이 가볍고 NetBird가 무겁습니다. 다만 그 무게의 대가로 NetBird는 UI, 사용자 관리, 릴레이, 인증서 발급을 한 번에 줍니다. Headscale에서 같은 것을 갖추려면 서드파티 UI, 외부 IdP, DERP 설정, 리버스 프록시를 각각 손으로 얹어야 합니다.

그냥 Tailscale을 쓰는 것이 나은 경우

솔직한 답도 필요합니다. 2026년 9월 기준 Tailscale 무료 Personal 플랜은 사용자 6명, 기기 수 무제한입니다. 가족과 개인 기기만 묶는다면 이 한도에 닿지 않습니다. 검색어 "Tailscale 단점"과 "Tailscale 무료"로 사람들이 찾는 것은 결국 두 가지입니다. 무료 플랜의 한도, 그리고 로그인이 Google, Microsoft, GitHub, Apple 같은 외부 계정에 묶인다는 점입니다. 한도가 문제라면 Tailscale 무료 플랜의 정확한 한도를 먼저 확인하십시오. 외부 계정 의존이 문제라면 그것이 바로 위의 두 선택지가 존재하는 이유입니다. Tailscale과 순수 WireGuard의 차이는 WireGuard와 Tailscale 비교에, Tailscale을 대체할 수 있는 다른 도구들은 Tailscale 대안 정리에 있으므로 여기서 다시 다루지 않습니다.

결정 규칙

  • 이미 모든 기기에 Tailscale 앱이 깔려 있고, 사용자가 한두 명이며, 정책 파일을 직접 편집하는 데 거부감이 없다: Headscale입니다. 다음 단계는 Headscale로 Tailscale 제어 서버를 셀프호스팅하는 가이드입니다.
  • 팀이 쓰고, SSO로 사용자를 관리하고, 웹 UI에서 정책을 편집해야 한다: NetBird입니다. 다음 단계는 NetBird 서버를 VPS에 셀프호스팅하는 가이드입니다.
  • 릴레이까지 국내에 두어야 한다: 어느 쪽이든 되지만 NetBird는 기본값이고 Headscale은 임베디드 DERP 설정이 추가로 필요합니다.
  • Funnel이나 Serve를 쓰고 있다: Headscale은 둘 다 지원하지 않으므로 NetBird Proxy로 가거나 리버스 프록시를 따로 세워야 합니다.
  • 기기가 서버 하나와 노트북 하나뿐이고 메시가 필요 없다: 둘 다 과합니다. VPS에 WireGuard VPN을 직접 구축하는 방법이 설정 40줄로 끝납니다.

FAQ

Headscale에 Tailscale 공식 앱을 그대로 쓸 수 있나요?

예. Headscale 문서는 Tailscale 클라이언트의 최근 10개 릴리스를 지원 목표로 밝히고 있습니다. Linux, OpenBSD, FreeBSD는 tailscale up --login-server <URL>로 바로 연결됩니다. Windows, macOS, iOS, tvOS, Android는 로그인 서버를 바꾸는 별도 설정이 필요하며, 그 절차는 Headscale 서버가 /windows/apple 경로에서 자동으로 생성해 보여 줍니다.

NetBird 셀프호스팅에 Docker가 꼭 필요한가요?

공식 빠른 설치 스크립트는 Docker Compose v2를 전제로 하고, jqcurl이 없으면 시작하지 않습니다. 문서의 고급 가이드도 Docker Compose 기반입니다. 관리, 시그널, 릴레이 바이너리를 직접 띄우는 것은 가능하지만 공식 문서가 안내하는 경로가 아닙니다. Docker를 피하고 싶다면 그것 자체가 Headscale 쪽으로 기우는 이유가 됩니다.

Headscale을 셀프호스팅하면 릴레이 트래픽도 Tailscale 서버를 안 거치나요?

기본값에서는 거칩니다. derp.urls가 Tailscale 공개 DERP 맵을 가리키기 때문입니다. 조정만 내 서버이고 릴레이는 Tailscale인 상태입니다. 릴레이까지 끊으려면 derp.server.enabled: true로 임베디드 DERP를 켜고 urls를 빈 목록으로 바꾼 뒤 UDP 3478을 엽니다. 릴레이를 거치더라도 패킷은 WireGuard로 암호화되어 있어 릴레이가 내용을 읽지는 못합니다.

Headscale에서 OIDC 그룹으로 접근 정책을 만들 수 있나요?

아니요. Headscale 문서는 "OIDC groups cannot be used in policy rules"라고 명시합니다. IdP 그룹은 allowed_groups로 로그인을 거르는 데만 쓰입니다. 정책 파일의 groups 블록에 사용자를 직접 나열해야 하고, IdP에서 팀원이 바뀌면 파일도 같이 고쳐야 합니다. 이 동기화가 부담스러운 규모라면 NetBird의 대시보드 그룹이 맞고, 그마저 자동화하는 IdP-Sync는 NetBird 클라우드의 Team 플랜 이상에서만 제공됩니다.