5 min read

Headscale로 셀프호스팅 VPN 만들기 — Tailscale 없이 내 네트워크 구축

Tailscale 컨트롤 서버를 셀프호스팅하고, 해외 VPS를 Exit Node로 연결해 유료 VPN을 대체한 경험을 공유합니다.

Tailscale은 좋은데, 개인 서버에서 더 좋은 방법이 있네?

Tailscale을 써본 분이라면 아실 겁니다. 설치 한 번이면 어디서든 집 네트워크에 접근할 수 있죠. 그런데 몇 가지 아쉬운 점이 있었습니다.

  • 무료 플랜은 기기 수 제한이 있습니다.
  • 컨트롤 서버가 Tailscale 클라우드에 있어서, 서비스가 죽으면 내 VPN도 죽습니다.
  • 내 네트워크 정보가 외부 서버에 저장됩니다.

Headscale은 Tailscale의 컨트롤 서버를 셀프호스팅할 수 있게 만든 오픈소스 프로젝트입니다. Tailscale 클라이언트를 그대로 쓰면서, 컨트롤 플레인만 내 서버에서 돌립니다.


구성 요소

컴포넌트 역할
Headscale 컨트롤 서버 (Tailscale 대체)
Headplane 웹 관리 UI
Tailscale Router 서브넷 라우터 (홈 네트워크 광고)
Tailscale 클라이언트 각 기기에 설치

Docker Compose

services:
  headscale:
    image: headscale/headscale:0.28.0
    container_name: headscale-server
    command: serve
    ports:
      - "8089:8080"
      - "3478:3478/udp"
    volumes:
      - "./headscale-data:/var/lib/headscale"
      - "./headscale-config:/etc/headscale"
    healthcheck:
      test: ["CMD", "headscale", "version"]
      interval: 30s

  headplane:
    image: ghcr.io/tale/headplane:0.6.2
    container_name: headscale-headplane
    depends_on:
      - headscale
    ports:
      - "3009:3000"
    volumes:
      - "./config.yaml:/etc/headplane/config.yaml"
      - "./headscale-config/config.yaml:/etc/headscale/config.yaml"
      - "./headscale-config/acl.hujson:/etc/headscale/acl.hujson"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

  tailscale-router:
    image: tailscale/tailscale:v1.96.5
    container_name: tailscale-router
    hostname: subnet-router
    network_mode: host
    cap_add:
      - NET_ADMIN
      - NET_RAW
    volumes:
      - ./tailscale-state:/var/lib/tailscale
    environment:
      TS_AUTHKEY: ${TS_AUTHKEY}
      TS_EXTRA_ARGS: "--advertise-routes=192.168.0.0/23 --login-server=http://127.0.0.1:8089 --accept-dns=false"
      TS_STATE_DIR: /var/lib/tailscale

Headscale 설정

# headscale-config/config.yaml
server_url: https://headscale.example.com
listen_addr: 0.0.0.0:8080

prefixes:
  v4: 100.64.0.0/10
  v6: fd7a:115c:a1e0::/48

dns:
  magic_dns: true
  base_domain: ts.example.com
  nameservers:
    global:
      - 192.168.1.100  # 내부 DNS (AdGuard 등)
      - 1.1.1.1
  override_local_dns: true

database:
  type: sqlite
  sqlite:
    path: /var/lib/headscale/db.sqlite

핵심 설정:

  • server_url: 외부에서 접근 가능한 Headscale 주소. 리버스 프록시 뒤에 놓으면 됩니다.
  • prefixes: Tailscale 네트워크에서 사용할 IP 대역. 기본값 그대로 쓰면 됩니다.
  • magic_dns: 기기명.ts.example.com으로 접근 가능하게 해줍니다.

ACL (접근 제어)

{
  "groups": {
    "group:admin": ["myuser@"]
  },
  "tagOwners": {
    "tag:server": ["group:admin"],
    "tag:vpn": ["group:admin"]
  },
  "acls": [
    {
      "action": "accept",
      "src": ["group:admin"],
      "dst": ["*:*"]
    },
    {
      "action": "accept",
      "src": ["tag:server", "tag:vpn"],
      "dst": ["*:*"]
    }
  ]
}

admin 그룹은 모든 곳에 접근 가능하고, 서버/VPN 태그가 붙은 노드끼리도 자유롭게 통신합니다.


서브넷 라우터: 홈 네트워크 전체 접근

tailscale-router192.168.0.0/23을 광고합니다. 이렇게 하면 VPN에 연결된 기기에서 홈 네트워크의 모든 장비에 접근할 수 있습니다.

TS_EXTRA_ARGS: "--advertise-routes=192.168.0.0/23 --login-server=http://127.0.0.1:8089"
  • --advertise-routes: 이 서브넷을 VPN 네트워크에 광고
  • --login-server: 로컬 Headscale 서버 주소 (같은 호스트니까 localhost)

Headplane UI에서 해당 라우트를 승인하면 활성화됩니다.


클라이언트 연결

각 기기에서 Tailscale 클라이언트를 설치하고, login server만 바꿔주면 됩니다.

# macOS / Linux
tailscale up --login-server=https://headscale.example.com

# iOS / Android
# Tailscale 앱 설정에서 "Custom control server" 입력

실제 사용 예시

제 네트워크 구성입니다:

노드 Tailscale IP 역할
홈서버 라우터 100.64.0.11 서브넷 라우터 + Exit Node
해외 VPS 100.64.0.23 Exit Node (해외 IP)
노트북 100.64.0.35 클라이언트
스마트폰 100.64.0.42 클라이언트

카페에서 노트북으로 192.168.1.100:3060에 바로 접근할 수 있고, 해외 IP가 필요하면 VPS를 Exit Node로 쓰면 됩니다.


Exit Node 활용: 해외 VPS로 VPN 대체하기

유료 VPN 서비스를 쓰는 대신, 해외 클라우드에 무료 VPS를 하나 띄워서 Exit Node로 쓰고 있습니다. Oracle Cloud 프리 티어로 일본 리전에 인스턴스를 만들고, Tailscale 클라이언트를 설치한 뒤 Headscale에 연결했습니다.

이렇게 하면:

  • 지역 제한 우회: 일본 IP로 인터넷 접속 (일본 전용 서비스 이용)
  • 홈서버 Exit Node: 해외에서 한국 IP가 필요할 때 집을 통해 나감
  • 비용 0원: Oracle Cloud 프리 티어는 평생 무료

VPS에서 할 일은 Tailscale 설치 + Exit Node 광고뿐입니다.

# 해외 VPS에서
tailscale up --advertise-exit-node --login-server=https://headscale.example.com

Headplane UI에서 Exit Node를 승인하면, 클라이언트에서 바로 사용할 수 있습니다.

# 클라이언트에서 (일본 IP로 나가고 싶을 때)
tailscale up --exit-node=100.64.0.23

기존에 쓰던 유료 VPN을 완전히 대체했습니다. 속도도 더 빠르고, 내 트래픽이 제3자 VPN 업체를 거치지 않으니 프라이버시도 낫습니다.


Tailscale vs Headscale 비교

Tailscale (클라우드) Headscale (셀프호스팅)
설정 난이도 매우 쉬움 중간
기기 수 제한 무료 3대 (2025 기준) 무제한
컨트롤 서버 Tailscale 클라우드 내 서버
데이터 주권 외부 완전 내부
안정성 높음 내 서버 의존
관리 UI 엔터프라이즈급 Headplane

소수 기기만 쓴다면 Tailscale 클라우드가 편합니다. 하지만 기기가 많거나, 데이터를 외부에 두기 싫거나, 인프라를 직접 통제하고 싶다면 Headscale이 좋은 선택입니다.


마무리

Headscale + Tailscale 클라이언트 조합은 "WireGuard의 성능 + Tailscale의 편의성 + 셀프호스팅의 자유"를 동시에 얻을 수 있는 방법입니다.

한 번 세팅해두면 어디서든 집 네트워크에 접근할 수 있고, Exit Node로 VPN 용도로도 쓸 수 있습니다. Docker Compose 하나면 끝이니 홈서버 운영자라면 꼭 시도해보시길 추천합니다.


참고 자료