Headscale 설치 및 Tailscale 자가 호스팅 방법
Tailscale 제어 서버를 VPS에 직접 구축하는 Headscale 설치 가이드입니다. 공식 .deb 패키지 설치부터 server_url 설정 및 노드 연결까지의 과정을 상세히 설명합니다. 자가 호스팅을 통해 네트워크 제어권을 확보하고 관리 효율을 높여 보십시오.
Headscale이란 무엇인가
Headscale은 Tailscale 제어 서버를 직접 호스팅하는 구현체입니다. 즉, 사설 네트워크를 조정하는 기기를 사용자가 직접 소유한 VPS에서 운영하게 됩니다. 이는 커뮤니티 프로젝트이며 Tailscale Inc.에서 운영하지 않습니다. 모든 기기는 여전히 공식 tailscale 클라이언트를 실행하며, --login-server 플래그를 사용하여 사용자의 서버를 가리키도록 설정합니다.
제어 서버는 네트워크에 누가 속해 있는지 파악하는 역할을 합니다. 각 노드에 100.64.0.0/10 대역의 주소를 할당하고, 공개 키를 배포하며, 노드들이 서로를 찾을 수 있도록 안내합니다. 터널은 노드 간에 직접 연결되는 WireGuard 방식을 유지합니다. 두 기기 사이의 트래픽은 직접 경로를 구축할 수 없어 릴레이로 전환되는 경우를 제외하면 Headscale 서버를 거치지 않습니다. 조정 역할을 직접 수행하는 것은 시스템의 기능이 아니라 관리 주체를 바꾸는 것이므로, 이 전환을 그 자체로 보안상의 이득으로 간주하기 전에 이 모델에서 제어 서버가 접근할 수 있는 범위와 없는 범위를 먼저 이해해야 합니다.
Headscale은 인스턴스마다 하나의 tailnet(하나의 Tailscale 네트워크)을 제공한다. 프로젝트에서는 이를 개인 사용자 또는 소규모 조직에 적합한 구성으로 설명한다. 장비가 3~4대라면 자신이 소유한 VPS에 일반 WireGuard VPN을 구성하는 방법이 실행할 소프트웨어도 적고 장애가 발생할 지점도 적다. 새 노트북을 추가할 때마다 [Peer] 블록을 직접 작성하고 싶지 않을 때 Headscale의 장점이 커진다. 비용 때문에 처음 찾아보는 경우가 많으므로 서버를 직접 운영하기 전에 호스팅 무료 플랜에서 실제로 제공하는 범위를 확인할 가치가 있다. 개인 장비 몇 대라면 대개 무료 플랜에 포함된다. 이미 그 한도를 넘었다면 사용자별 및 태그가 지정된 리소스별 유료 플랜 비용과 비교해 계산해야 한다. 사용자 장비는 제한이 없지만 태그가 지정된 리소스에는 별도의 허용량과 요금이 적용된다. 자체 호스팅 제어 플레인을 원하지만 Tailscale의 드롭인 대체 제품보다는 피어를 관리할 자체 클라이언트와 웹 인터페이스를 선호한다면 단일 VPS에서 NetBird를 운영하는 방법을 검토할 만한 대안으로 고려할 수 있다. 두 제품 모두 WireGuard를 통해 네트워크 트래픽을 전달한다. 따라서 선택은 접근 규칙을 작성하는 방식과 릴레이의 동작에 따라 달라진다. 어느 쪽이든 설치하기 전에 이러한 측면에서 NetBird와 Tailscale을 비교하는 방법을 확인할 가치가 있다. 두 모델을 더 넓게 비교하려면 WireGuard와 Tailscale의 차이를 참고한다.
설치 전 준비 사항
- 공인 IPv4 주소와 sudo 권한을 가진 Ubuntu 24.04 기반 VPS가 필요합니다. 서버를 새로 구축했다면 먼저 새 VPS 설정 초기 10분 가이드를 진행하십시오.
- 해당 주소를 가리키는 DNS A 레코드가 필요합니다. 이 가이드에서는
headscale.example.com을 사용합니다. - MagicDNS를 위한 두 번째 도메인 또는 서브도메인이 필요합니다. 이 가이드에서는
tailnet.example.net을 사용합니다. 이는server_url에 사용된 도메인과 달라야 합니다. - 연결할 클라이언트 기기 1대가 필요하며, Linux, macOS, Windows, Android 또는 iOS를 지원합니다. Headscale 0.29.6 버전은 Tailscale 클라이언트 1.80.0 버전 이상을 요구합니다.
공식 .deb에서 headscale 설치
프로젝트는 GitHub releases 페이지에서 .deb 패키지를 배포한다. 2026년 10월 27일 기준 최신 stable release는 0.29.6이며, 2026년 10월 24일에 release되었다. 파일 이름에 아키텍처가 포함되므로 먼저 아키텍처를 확인한다.
sudo apt update
sudo apt install -y wget
dpkg --print-architecture일반적인 x86 VPS에서는 amd64가 출력되고, Ampere 또는 Graviton 계열 플랜에서는 arm64가 출력된다. 결과를 아래 변수에 저장한다.
HEADSCALE_VERSION="0.29.6"
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 -y ./headscale.deb
headscale version파일 이름 앞의 ./가 필요하다. 이 옵션이 없으면 apt가 저장소에서 headscale.deb라는 패키지를 찾다가 실패한다.
패키지는 headscale system user를 생성하고, 기본 /etc/headscale/config.yaml를 기록하며, systemd unit을 설치한 다음 서비스를 시작한다. 따라서 apt가 반환될 때 headscale은 이미 실행 중이며, sudo systemctl is-active headscale는 active를 출력한다. 그러나 새로 설치한 경우에도 패키지는 unit을 enable하지 않는다. 따라서 아래의 restart 단계에서 enable할 때까지 재부팅 후 서비스가 다시 시작되지 않는다. 또한 서비스는 패키지에 포함된 설정으로 실행되며, 이 설정은 server_url를 http://127.0.0.1:8080으로 지정한다. 이 주소에는 사용자의 어떤 client도 접근할 수 없다. 패키지에 포함된 listen_addr도 loopback에 연결되어 있으므로 아직 외부에 노출된 것은 없다. 하지만 server_url을 설정하고 서비스를 재시작하기 전에는 client를 연결하지 않는다. 다음 2단계에서 이 작업을 수행한다.
클라이언트가 연결되기 전에 server_url 설정하기
/etc/headscale/config.yaml를 sudo nano /etc/headscale/config.yaml으로 편집하거나, sed을 사용하여 동일한 세 가지 변경 사항을 적용합니다. 파일이 길고 주석이 많으며 나머지 설정에 대한 가장 좋은 참조 자료이므로 원본 사본을 보관하십시오.
sudo cp /etc/headscale/config.yaml /etc/headscale/config.yaml.orig
sudo sed -i 's|^server_url:.*|server_url: https://headscale.example.com|' /etc/headscale/config.yaml
sudo sed -i 's|^listen_addr:.*|listen_addr: 127.0.0.1:8080|' /etc/headscale/config.yaml
sudo sed -i 's|^ base_domain:.*| base_domain: tailnet.example.net|' /etc/headscale/config.yaml
sudo grep -E '^(server_url|listen_addr):|^ base_domain:' /etc/headscale/config.yamlserver_url은 headscale이 등록 URL을 생성할 때 사용하는 공용 주소입니다. 클라이언트는 --login-server로 설정된 제어 주소를 사용하므로, https://에는 두 곳 모두 동일한 공용 이름을 사용해야 하며 127.0.0.1은 절대 사용하지 마십시오. headscale 문서에 따르면 일반 HTTP나 443 이외의 포트에서 HTTPS를 사용하는 것도 가능하지만, 프로덕션 환경에서는 443 포트에서 HTTPS를 사용할 것을 강력히 권장하며 이 가이드의 나머지 부분도 이를 전제로 합니다.
listen_addr는 프로세스가 바인딩되는 위치입니다. 루프백으로 두십시오. 동일한 서버에 있는 리버스 프록시가 TLS(전송 계층 보안)를 종료하고 전달하므로, 서버 외부에서 8080 포트에 접근할 필요는 없습니다.
base_domain은 MagicDNS 접미사로, 노드가 이름을 할당받을 도메인입니다. 이는 끝에 점이 없는 정규화된 도메인 이름(FQDN)이어야 하며, server_url에 있는 도메인과는 달라야 합니다. 그렇지 않으면 두 이름 공간이 충돌하게 됩니다.
데이터베이스 섹션은 그대로 두십시오. 기본값은 /var/lib/headscale/db.sqlite에 위치한 SQLite이며, 패키지가 생성하고 소유하는 디렉터리에 있습니다. 이 규모의 테일넷(tailnet)에는 SQLite로 충분합니다.
headscale를 재시작하고 실행 상태 확인
sudo systemctl restart headscale
sudo systemctl enable headscale
sudo systemctl is-active headscale
sudo systemctl is-enabled headscale
curl -sS -o /dev/null -w '%{http_code}\\n' http://127.0.0.1:8080/healthis-active은 active을 출력하고, is-enabled은 enabled을 출력하며, curl은 200을 출력합니다. 재시작해야 수정한 내용이 적용됩니다. headscale는 시작할 때 설정을 한 번 읽으므로, 패키지가 시작한 프로세스는 재시작하기 전까지 배포된 server_url을 계속 사용합니다. sudo systemctl enable --now headscale은 이 작업을 수행하지 않습니다. 서비스가 이미 실행 중이므로 --now start는 아무것도 변경하지 않으며 이전 값이 계속 적용됩니다. 부팅 시 시작하도록 설정하는 작업은 enable이 담당합니다. 이 설정은 자동으로 적용되지 않습니다. 새로 설치하면 패키지는 서비스를 시작하지만 활성화하지 않으므로, 위에서 설명한 것처럼 sudo systemctl enable headscale을 한 번 실행해야 합니다.
is-active이 active 이외의 값을 출력하거나 curl가 200을 출력하지 않으면 sudo journalctl -u headscale -n 50 --no-pager로 journal을 확인합니다. 이 단계의 실패 원인은 거의 항상 설정 파일입니다. headscale는 소켓을 열기 전에 파일 전체를 파싱하므로, 들여쓰기가 잘못되었거나 필수 설정이 유효하지 않으면 어떤 항목도 listen하기 전에 프로세스가 중지됩니다. 파일을 수정한 다음 sudo systemctl restart headscale합니다. 이후 설정을 변경할 때마다 같은 방식으로 재시작해야 합니다. 그러면 클라이언트는 자동으로 다시 연결합니다. systemd unit이 익숙하지 않다면 systemd로 직접 서비스와 타이머 실행하기에서 여기서 사용하는 명령을 설명합니다.
셸에 있는 동안 상태 파일도 확인합니다.
stat -c '%U %n' /var/lib/headscale/db.sqlite /var/lib/headscale/noise_private.key두 줄 모두 패키지가 생성한 권한이 없는 사용자 headscale으로 시작합니다. noise_private.key은 서버가 클라이언트에 자신을 식별하는 데 사용하는 값입니다. 이 파일을 유지해야 합니다. 삭제하면 headscale가 새 값을 생성하고, 클라이언트는 다시 연결하기 전에 새 서버 key를 가져와야 합니다. 기존 등록 정보는 database에 남아 있습니다.
headscale 앞에 TLS 배치하기
클라이언트는 HTTPS를 통해 server_url에 접근해야 합니다. Caddy는 스스로 인증서를 요청하고 갱신하므로 가장 간편한 방법입니다.
sudo apt install -y caddy/etc/caddy/Caddyfile을 headscale 문서에 있는 블록으로 교체하십시오:
headscale.example.com {
reverse_proxy 127.0.0.1:8080 {
header_up True-Client-IP {remote_host}
header_up X-Real-IP {remote_host}
}
}sudo caddy validate --adapter caddyfile --config /etc/caddy/Caddyfile
sudo systemctl restart caddy
sudo systemctl is-active caddyvalidate는 파일이 정상적으로 구문 분석되면 adapted config to JSON를 출력합니다. 파일 형식이 지정되지 않았다는 경고는 외관상의 문제일 뿐입니다. 노트북에서 curl -sS -o /dev/null -w '%{http_code}\\n' https://headscale.example.com/health을 실행했을 때 200이 출력되어야 합니다. 이 단일 확인 과정으로 DNS, 방화벽, 인증서, 프록시가 모두 정상적으로 작동하고 있음을 증명할 수 있습니다.
프록시 설정에서 많은 사용자가 시간을 허비하는 세부 사항이 있습니다. Tailscale 제어 연결은 HTTP 업그레이드 방식이며, GET이 아닌 POST로 시작하고 Upgrade 헤더의 값은 tailscale-control-protocol입니다. Caddy는 별도의 설정 없이 이를 그대로 전달합니다. 반면 nginx는 그렇지 않으므로, nginx를 프록시로 사용할 경우 Upgrade 및 Connection 헤더를 명시적으로 전달해야 합니다. headscale 문서에서는 다음과 같이 업그레이드 맵을 사용하여 이를 처리합니다:
map $http_upgrade $connection_upgrade {
default keep-alive;
'' close;
}
server {
listen 443 ssl;
server_name headscale.example.com;
location / {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
proxy_pass http://127.0.0.1:8080;
}
}이 줄들을 누락하면 일반적인 요청은 성공하므로 /health가 200을 반환하여 모든 것이 정상인 것처럼 보입니다. 하지만 실제로는 제어 연결이 형성되지 않아 새로운 노드를 등록할 수 없고, 기존 노드도 재연결하거나 온라인 상태를 보고할 수 없습니다. nginx를 선택할 경우, Ubuntu 24.04에서 nginx와 Certbot 사용하기 문서를 참조하여 인증서 관련 설정을 진행하십시오.
UFW에서 열어야 할 포트
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose443번 포트는 모든 클라이언트가 headscale로 연결하는 제어 연결을 담당합니다. 나중에 내장된 DERP 릴레이를 활성화하면 아래에서 다룰 3478/udp 포트도 필요합니다. 80번 포트는 ACME(자동 인증서 관리 환경) HTTP 챌린지와 HTTPS 리다이렉트를 위해 사용합니다. Caddy는 80번 포트가 닫혀 있어도 443번 포트에서 TLS-ALPN 챌린지를 사용하여 인증서를 발급받을 수 있지만, 80번 포트를 열어두면 두 챌린지를 모두 사용할 수 있고 일반 http:// 요청을 리다이렉트할 수 있습니다.
8080번 포트는 닫아둡니다. listen_addr는 127.0.0.1:8080이므로, 프록시는 루프백 인터페이스를 통해 headscale에 접근하며 방화벽 규칙이 관여하지 않습니다. 8080번 포트를 UFW에서 열어도 외부에서 수신 대기 중인 서비스가 없으므로 아무것도 노출되지 않으며, 얻을 수 있는 이점도 없습니다. 대부분의 제공업체는 UFW와 별도로 제어판에서 두 번째 방화벽을 운영한다는 점을 기억하십시오. 따라서 서버 내부에서는 포트가 열려 있어도 네트워크 경계에서는 닫혀 있을 수 있습니다. VPS에서의 UFW 방화벽 기초에서 규칙 문법을 더 자세히 다룹니다.
사용자 생성 및 사전 인증 키 생성
sudo headscale users create alice
sudo headscale users listheadscale 명령어는 클라이언트 역할을 합니다. 이 명령어는 /var/run/headscale/headscale.sock에 위치한 유닉스 소켓을 통해 실행 중인 데몬과 통신하며, 해당 소켓은 0770 모드로 설정되어 있고 headscale 그룹이 소유합니다. 이로 인해 두 가지 결과가 발생합니다. 서비스가 중지된 상태에서는 명령어가 실패하며, 이것이 본 가이드의 순서가 중요한 또 다른 이유입니다. 또한, 본인의 계정을 headscale 그룹에 추가하지 않았다면 sudo 권한이 필요합니다.
users list 명령어는 각 이름 옆에 ID를 출력합니다. 키 명령어는 이름이 아닌 숫자 형태의 사용자 ID를 인자로 받으므로 이 숫자가 필요합니다.
sudo headscale preauthkeys create --user 1 --expiration 24h키는 한 번만 출력됩니다. 지금 복사하십시오. 사전 인증 키는 별도의 설정이 없다면 1회용이며 1시간 동안만 유효하므로, 테스트 중이라면 --expiration 24h 옵션을 설정하는 것이 좋습니다. 여러 대의 장비를 등록해야 하는 키라면 --reusable 옵션을 추가하십시오. 이 키를 가진 사람은 누구나 네트워크에 참여할 수 있으므로 비밀번호처럼 안전하게 관리해야 합니다.
--login-server를 사용하여 첫 번째 클라이언트 연결하기
연결하려는 머신에서 다음을 수행합니다:
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --login-server https://headscale.example.com --auth-key 'hskey-auth-PASTE-YOUR-KEY-HERE'
tailscale status
tailscale ip -4tailscale ip -4은(는) headscale이 할당한 주소를 출력하며, 100.64.0.1과 같은 형태입니다. 서버로 돌아와 sudo headscale nodes list을(를) 실행하면 노드의 ID, 사용자, 온라인 상태를 확인할 수 있습니다.
--login-server을(를) 사용할 때는 server_url의 공개 HTTPS 주소를 입력하십시오. 클라이언트는 제어 URL 끝의 슬래시를 자동으로 제거하므로, 슬래시를 생략해야 하는 엄격한 문자열 비교는 없습니다.
이전에 Tailscale의 호스팅 서비스를 사용하던 머신은 해당 로그인 정보를 유지합니다. 먼저 sudo tailscale logout을(를) 실행하여 로그아웃한 뒤, --login-server 옵션을 사용하여 tailscale up을(를) 실행하십시오.
--auth-key 옵션을 생략하면 클라이언트는 URL을 출력합니다. 해당 URL을 열면 등록 시도 식별자가 표시되며, 서버에서 이를 승인하면 됩니다:
sudo headscale auth register --user alice --auth-id PASTE-THE-ID-FROM-THE-PAGE이 방식은 개인 노트북에서 사용하기에 편리합니다. 스크립트를 통한 자동화가 필요한 경우에는 사람이 직접 확인할 필요가 없는 Preauth 키를 사용하는 것이 좋습니다. VPS 자체가 노드가 되면 다른 머신의 인터넷 트래픽을 전달할 수 있는데, 이것이 exit node 설정입니다. 호스팅 관리 콘솔 대신 서버에서 headscale 명령을 사용하여 광고된 경로를 승인한다는 점만 다릅니다. 인터넷으로 나가는 경로가 아니라 VPS 뒤에 있는 사설 네트워크에 접근하려는 경우에도 동일한 승인 단계를 거쳐 해당 서브넷을 tailnet의 나머지 부분에 광고할 수 있습니다. 노드에서 전체 네트워크를 라우팅하는 대신 특정 애플리케이션 하나만 게시하는 것은 별개의 작업이며, serve와 funnel이 이를 수행하는 두 가지 방법입니다. 다만 두 방법 모두 Tailscale 자체 인증서 및 인그레스 메커니즘에 의존하므로, headscale이 직접 제공하는 기능이라기보다는 호스팅된 tailnet의 기능으로 이해해야 합니다.
DERP와 직접 경로 실패 시 트래픽을 중계하는 원리
DERP(designated encrypted relay for packets)는 대체 경로입니다. 두 노드가 직접 WireGuard 연결을 맺지 못할 때, 보통 양쪽 모두 엄격한 NAT(network address translation) 뒤에 위치한 경우, 대신 릴레이를 통해 패킷을 전송합니다. 릴레이는 키를 보유하지 않으므로 트래픽을 읽을 수 없습니다. 다만 어떤 노드끼리 통신하는지, 데이터가 얼마나 이동하는지는 확인할 수 있습니다.
기본 설정이 수행하는 동작을 명확히 이해해야 합니다. Headscale은 기본적으로 https://controlplane.tailscale.com/derpmap/default을 가리키며 auto_update_enabled: true 및 update_frequency: 3h을 사용하도록 설정되어 있습니다. 따라서 제어 평면은 사용자가 직접 관리하지만, 릴레이는 Tailscale의 것을 사용하게 됩니다. 대부분의 사용자에게는 합리적인 타협점입니다. 만약 그렇지 않다면 직접 릴레이를 운영하십시오.
직접 릴레이를 운영하려면 config.yaml 내의 derp.server 아래에 enabled: true를 설정하십시오. 기본 제공되는 ipv4: 198.51.100.1 및 ipv6: 2001:db8::1을 릴레이의 공인 주소로 교체하거나, 두 설정을 모두 제거하여 DNS를 사용하도록 한 뒤 headscale을 재시작하고 sudo ufw allow 3478/udp를 사용하여 STUN(session traversal utilities for NAT) 포트를 개방하십시오. 해당 주소 설정은 DNS보다 우선하므로, 자리 표시자(placeholder)를 그대로 두면 클라이언트가 도달할 수 없는 주소로 전송됩니다. 설정 파일에는 요구 사항이 명시되어 있습니다. DERP는 TLS를 필요로 하므로 server_url는 반드시 https를 사용해야 합니다. derp.urls 목록을 비우면 Tailscale의 릴레이가 맵에서 제거됩니다. 그렇게 하기 전에 작동하는 내장 릴레이나 사용자 정의 릴레이를 먼저 확보하십시오. Headscale은 릴레이 맵이 비어 있으면 시작을 거부하며, 도달할 수 없는 릴레이만 포함된 맵을 사용할 경우 직접 연결이 불가능한 노드들은 통신할 수 없게 됩니다.
클라이언트에서 tailscale netcheck을 실행하면 알고 있는 각 릴레이 지역까지의 지연 시간을 출력하며, tailscale status은 활성 피어 연결에 대해 주소와 함께 direct를 표시하거나 지역 코드와 함께 relay을 표시합니다. relay 상태에 머물러 있는 피어는 NAT 및 방화벽 규칙을 포함하여 직접 경로를 점검해야 합니다. direct 상태임에도 여전히 속도가 느린 피어는 다른 문제이며, 이 경우 일반적인 해결책은 터널 자체가 아닌 MTU 설정입니다.
노드가 오프라인으로 표시되는 이유는 무엇입니까?
프록시가 업그레이드를 차단하고 있습니다. 이 경우 /health은 200을 반환하지만, 새로운 등록이 실패하거나 기존 노드가 재연결되지 않는 현상이 나타납니다. 제어 연결은 Upgrade: tailscale-control-protocol를 포함하는 POST 요청이며, 이를 전달하지 않는 프록시는 노드 상태를 보고하는 채널과 등록 과정을 모두 차단합니다. nginx 설정을 위의 업그레이드 관련 설정과 비교하거나, 프록시 문제를 배제하기 위해 Caddy로 전환하십시오.
공용 제어 주소가 변경되었습니다. 노드는 --login-server에 설정된 주소로 계속 연결을 시도합니다. server_url을 변경해도 클라이언트 설정은 업데이트되지 않습니다. 이전 주소로 서버에 도달할 수 없는 경우, 각 노드에서 sudo tailscale up --login-server https://headscale.example.com --force-reauth을 실행하십시오.
클라이언트가 실행 중이지 않습니다. 노드에서 sudo systemctl is-active tailscaled 및 sudo journalctl -u tailscaled -n 50 --no-pager를 확인하십시오. 도메인을 해석할 수 없거나 도메인에 도달할 수 없는 클라이언트는 해당 위치에 재시도 로그를 기록합니다.
키가 만료되었습니다. 다음 섹션에서 다룹니다.
테스트하는 동안 서버 측을 모니터링하려면 VPS에서 sudo journalctl -u headscale -f을 실행하고 클라이언트에서 tailscaled을 재시작하십시오. 클라이언트 저널도 확인하십시오. headscale에 요청이 도달했다고 해서 기본 로그 수준에서 반드시 로그가 생성되는 것은 아니므로, 서버 로그에 아무것도 나타나지 않는다고 해서 DNS, 방화벽 또는 프록시 오류가 없다고 단정할 수는 없습니다.
키 만료와 몇 주 뒤 작동이 중단되는 노드
두 가지 별도의 만료 기한이 존재하며, 이를 혼동하면 시간을 낭비하게 됩니다.
Preauth 키는 설계상 빠르게 만료됩니다. 기본값은 1시간, 1회 사용입니다. 만약 tailscale up가 키를 거부한다면, 클라이언트 설정을 수정하지 말고 서버에서 새로 생성하십시오.
노드 키는 수명이 긴 쪽입니다. config.yaml의 node 섹션에서 expiry: 0를 설정하며, 0은 기본 만료 기한이 없음을 의미합니다. 단, 명시적인 클라이언트 만료 기한이나 OIDC 만료 기한은 여전히 적용될 수 있습니다. 태그가 지정된 노드는 자동 만료에서 제외되지만, 관리자가 명시적으로 만료시킬 수는 있습니다. 태그가 없는 등록에 대해 180일 기본값을 설정하려면 expiry: 180d을 지정하십시오. 하지만 클라이언트가 요청한 만료 기한이 우선하며, OIDC는 기본값을 재정의할 수 있습니다. 이 설정을 변경해도 기존 등록에는 소급 적용되지 않습니다. 만료 기한이 있는 노드는 해당 기한 전에 sudo tailscale up --login-server https://headscale.example.com --force-reauth을 사용하여 재인증을 받아야 합니다. 아무도 재인증하지 않는 헤드리스 서버는 등록이 만료되면 네트워크에서 제외됩니다.
노트북을 분실했을 때는 수동으로 처리하십시오. sudo headscale nodes list로 ID를 확인한 다음, sudo headscale nodes expire -i 3으로 해당 노드를 로그아웃시키고, sudo headscale nodes delete -i 3로 네트워크에서 완전히 제거하십시오.
백업 및 업그레이드
/var/lib/headscale와 /etc/headscale은 서버 전체를 구성합니다. SQLite는 실행 중에 쓰기 작업이 진행 중일 수 있으며, 부하가 걸린 상태에서 복사한 데이터베이스는 일관성이 깨질 수 있으므로 파일을 복사하기 전에 서비스를 중지하십시오.
sudo systemctl stop headscale
sudo tar czf /root/headscale-state.tgz -C /var/lib headscale
sudo tar czf /root/headscale-config.tgz -C /etc headscale
sudo systemctl start headscale
sudo chmod 600 /root/headscale-*.tgz두 파일을 서버 외부로 이동하십시오. 이 파일들에는 개인 키와 모든 등록 정보가 포함되어 있으므로 서버 자체와 동일한 수준의 주의를 기울여 관리해야 합니다. VPS에서 restic 백업하기 문서에서는 이를 암호화하여 주기적으로 수행하는 방법을 다룹니다.
업그레이드는 설치 과정을 반복합니다. 새로운 .deb와 sudo apt install ./headscale.deb를 다운로드한 뒤, 서비스를 재시작하고 is-active 및 /health 검사를 다시 실행하십시오. 0.29 버전부터는 업그레이드 경로가 엄격하게 제한됩니다. 마이너 버전을 건너뛰거나 이전 마이너 버전으로 다운그레이드하는 것은 차단됩니다. 한 번에 한 마이너 버전씩 이동하고, 각 단계마다 백업을 수행하십시오. 또한 해당 버전의 릴리스 노트를 먼저 읽어보아야 합니다. 동일한 릴리스에서 ACL 정책 동작이 변경되거나 여러 설정 키가 이동했을 수 있기 때문입니다. 0.29.6 버전에는 인증서 검증 및 의존성에 대한 보안 업데이트가 포함되어 있으므로, 기존 0.29 설치 환경은 이 패치 릴리스를 적용해야 합니다.
FAQ
.deb를 설치한 직후에도 headscale이 이미 실행 중입니까?
예. 패키지는 설치되는 즉시 서비스를 시작하며, 기본 제공되는 /etc/headscale/config.yaml에서 loopback만 수신하고 server_url을 http://127.0.0.1:8080로 설정합니다. 새로 설치하면 서비스가 활성화되지는 않으므로 sudo systemctl enable headscale을 실행하기 전까지 부팅 시 시작되지 않습니다. 기본 제공 주소는 클라이언트가 사용할 수 없으므로 server_url, listen_addr, base_domain을 수정한 다음 sudo systemctl restart headscale을 실행하고 sudo systemctl is-active headscale으로 확인합니다. 반드시 restart해야 합니다. headscale은 시작할 때만 파일을 읽기 때문입니다. 이미 실행 중인 서비스에서 enable --now을 실행하면 부팅 시 활성화되지만 서비스를 다시 시작하지는 않으므로 기존 값이 계속 적용됩니다. 다시 시작한 뒤 실패한다면 sudo journalctl -u headscale -n 50 --no-pager이 문제를 보여 줍니다. 이 단계에서는 headscale이 포트에 바인딩하기 전에 파일 전체를 구문 분석하므로 원인은 거의 항상 YAML 오류입니다.
컴퓨터에 일반 Tailscale client도 설치해야 합니까?
예. Headscale은 control server만 대체합니다. 모든 node는 Tailscale의 공식 client를 실행하고, sudo tailscale up --login-server https://headscale.example.com로 해당 client가 server를 사용하도록 지정합니다. 이 flag는 standard client에 포함되어 있으므로 patch하거나 다시 빌드할 필요가 없습니다.
내 traffic이 headscale server를 통과합니까?
대개 그렇지 않습니다. Headscale은 network를 조정하고 key와 address를 배포하며, data path는 node 간의 WireGuard 직접 연결입니다. 두 node가 서로 직접 연결되지 않아 DERP relay로 fallback할 때만 traffic이 우회합니다. 기본 제공 configuration에서는 이러한 relay가 Tailscale의 public relay입니다. node에서 tailscale status을 실행하면 특정 peer가 direct인지 또는 relay에 있는지 확인할 수 있습니다.
node를 등록한 뒤에도 offline 상태로 남는 이유는 무엇입니까?
headscale nodes list에 표시되지만 online 상태가 되지 않는 node는 reverse proxy를 포함해 control connection을 확인해야 합니다. 이 connection은 Upgrade: tailscale-control-protocol header를 포함한 POST로 전송되는 HTTP upgrade입니다. nginx는 proxy_set_header을 사용해 Upgrade 및 Connection header를 명시적으로 전달하지 않으면 이를 삭제하며, 위 configuration은 map $http_upgrade $connection_upgrade block을 통해 해당 header를 전달합니다. 이러한 header를 삭제하면 새 node도 등록할 수 없습니다. Caddy는 추가 configuration 없이 이를 전달하므로 proxy가 원인인지 빠르게 확인할 수 있습니다.
headscale에 domain name과 TLS가 필요합니까?
이 setup에서는 필요합니다. domain name과 HTTPS를 사용해야 합니다. Certificate는 bare IP address에도 적용할 수 있지만, configuration file에는 DERP에 TLS가 필요하다고 명시되어 있습니다. Domain과 Caddy를 함께 사용하면 약 5분 안에 설정할 수 있으며, 자동으로 갱신되는 HTTPS endpoint를 제공합니다. Plain HTTP도 사용할 수 있지만, headscale documentation은 production 환경에서 port 443의 HTTPS를 강력히 권장합니다. 또한 특정 상황에서 Tailscale client가 port 443의 HTTPS를 전제로 한다고 설명합니다.