집에 WireGuard VPN 서버 만들기 — wg-easy + Docker + Tailscale
해외 가족에게 한국 IP를 — wg-easy로 집에 WireGuard VPN 서버 세우기. CGNAT 확인부터 DDNS 선택지(공유기 내장/DuckDNS/Cloudflare), 관리 UI 보안, 밟았던 함정까지.
해외에 있는 가족이 티빙 같은 한국 전용 서비스를 못 봐서, 집에 WireGuard VPN 서버를 세웠다. 가족 폰에서 VPN을 켜면 우리집 IP로 인터넷을 쓰게 되는 구조다. 이 글은 같은 걸 만들고 싶은 사람을 위한 전체 과정 정리다. 서버 관리는 wg-easy 웹 UI로 한다.

0. 준비물
- 집에서 24시간 도는 리눅스 머신 — 미니PC, 라즈베리파이, NAS 등. Docker만 돌면 된다
- 공유기 관리자 페이지 접근 권한 — 포트포워딩 설정에 필요
- 진짜 공인 IP — 제일 먼저 확인할 것. 아래 참고
⚠️ 시작 전에 CGNAT인지 확인하자. 공유기 관리 페이지의 WAN(외부) IP와 ipinfo.io에서 보이는 IP가 다르면 통신사가 사설 IP를 준 것(CGNAT)이고, 이 경우 포트포워딩이 불가능해서 이 글의 방식이 안 통한다. (그땐 Tailscale 같은 중계형 VPN을 써야 한다.) 두 IP가 같으면 통과.
1. wg-easy 설치
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:15
container_name: wg-easy
environment:
- PORT=51821
- HOST=0.0.0.0
- INSECURE=true # 관리 UI를 HTTP로 열 때 필요 (아래 보안 절 참고)
- INIT_ENABLED=true # 웹 온보딩 생략, 아래 계정으로 자동 설정
- INIT_USERNAME=admin
- INIT_PASSWORD=${ADMIN_PASSWORD} # 12자 이상!
- INIT_HOST=vpn.example.com # 가족이 접속할 주소 (2절)
- INIT_PORT=51820
- INIT_DNS=1.1.1.1,8.8.8.8
volumes:
- etc_wireguard:/etc/wireguard
- /lib/modules:/lib/modules:ro
ports:
- "0.0.0.0:51820:51820/udp" # WireGuard: 인터넷 공개 (유일하게 여는 문)
- "127.0.0.1:51821:51821/tcp" # 관리 UI: 일단 로컬 전용으로 (3절에서 선택)
cap_add: [NET_ADMIN, SYS_MODULE]
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
restart: unless-stopped
volumes:
etc_wireguard:
docker compose up -d 하면 끝. 관리 UI에 접속하면 이런 로그인 화면이 나온다.

2. 가족이 접속할 주소 정하기 (DDNS)
가정용 인터넷은 IP가 수시로 바뀐다. IP를 그대로 알려주면 바뀔 때마다 가족 전원의 설정을 다시 해줘야 하므로, 도메인 주소를 만들어 자동 갱신(DDNS)하는 게 정석이다. 상황별 선택지:
A. 공유기 내장 DDNS (제일 간단)
ipTIME이면 관리 페이지에서 xxx.iptime.org 무료 DDNS를 몇 클릭으로 만들 수 있다. 통신사 공유기도 비슷한 기능이 있는 경우가 많다. 도메인 살 필요도, 컨테이너 추가도 없다. 대부분의 사람에게 이걸 추천.
B. DuckDNS (공유기에 기능이 없다면)
duckdns.org에서 무료로 xxx.duckdns.org를 만들고, 갱신 컨테이너 하나를 같이 돌리면 된다.
C. Cloudflare (자기 도메인이 있다면 — 내가 쓴 방식)
cloudflare-ddns:
image: favonia/cloudflare-ddns:1
environment:
- DOMAINS=vpn.example.com
- PROXIED=false # 반드시 false (DNS 전용)
- UPDATE_CRON=@every 5m
env_file: [.env] # CLOUDFLARE_API_TOKEN (Zone.DNS Edit 권한만 부여)
⚠️ Cloudflare를 쓴다면 프록시(주황 구름)를 반드시 꺼야 한다. WireGuard는 HTTP가 아니라서 Cloudflare 프록시를 통과하지 못한다.
어느 방식이든, 여기서 만든 주소를 compose의 INIT_HOST에 넣는다.
3. 관리 UI는 절대 인터넷에 열지 말 것
관리 UI(51821)에는 모든 접속자의 키가 들어 있다. 여기가 뚫리면 끝이므로 인터넷에 노출하면 안 된다. 선택지:
- 로컬 전용(기본 추천): 위 compose처럼
127.0.0.1:51821로 바인딩하고, 관리할 때만ssh -L 51821:localhost:51821 서버로 터널 뚫어서localhost:51821접속 - Tailscale(내 방식): 서버에 Tailscale을 깔고
"[Tailscale IP]:51821:51821"로 바인딩. 내 기기에서만 UI가 열리고, SSH 터널을 매번 안 만들어도 된다
어느 쪽이든 설정 후 ss -tulnp로 51821이 0.0.0.0에 물려 있지 않은지 꼭 확인하자.
4. 공유기 포트포워딩
공유기 관리 페이지에서 규칙 하나만 추가한다:
- 외부 포트
51820→ 내부 IP(서버)51820, 프로토콜 UDP
인터넷에 열리는 건 이 51820/UDP 하나뿐이다. WireGuard는 올바른 키가 없는 패킷에는 응답 자체를 하지 않아서(스텔스) 포트 스캔에도 잡히지 않는다. 이 용도로 전 세계가 쓰는 표준 프로토콜이니 과하게 걱정하지 않아도 된다.
5. 가족 등록 — QR 하나면 끝
관리 UI에서 New로 가족마다 클라이언트를 만들고 QR 버튼을 누르면 코드가 뜬다. 가족은 폰에 WireGuard 공식 앱을 깔고 QR을 스캔한 뒤 토글만 켜면 끝. 로그인도 계정도 없다.

💡 클라이언트 이름은 영문으로 짓는 걸 추천한다(mom-phone 등). 한글 이름은 설정파일 다운로드 시 파일명이 1.conf처럼 번호로 바뀐다.
6. 밟았던 함정들
비밀번호는 12자 이상
wg-easy v15는 관리자 비밀번호 최소 12자를 강제한다. 짧게 만들면 계정은 생성되는데 로그인 화면 검증에 막혀 영영 못 들어간다.
온라인 포트 체커의 "Closed"는 무시
포트포워딩 후 체커로 51820을 검사하면 Closed가 나온다. 정상이다 — 체커 대부분은 TCP만 검사하는데 WireGuard는 UDP이고, 애초에 키 없는 요청엔 무응답이다. 진짜 검증은 폰을 LTE로 전환해 실제 연결해보는 것뿐이다.
집 안에서 테스트하면 안 될 수 있음
집 WiFi에서 자기 집 공인 IP로 되돌아 들어오는 헤어핀 NAT를 지원하지 않는 공유기가 많다. 집에서 안 된다고 서버를 의심하지 말고 셀룰러로 테스트하자.
브라우저로 VPN 주소를 열면 공유기 페이지가 나온다
VPN 주소를 브라우저에 치면 공유기 관리 페이지가 떠서 놀랄 수 있다. 브라우저는 80 포트로 가고 그 포트는 공유기가 물고 있어서다. WireGuard(51820/UDP)와는 무관하고, 오히려 DNS가 집을 잘 가리킨다는 증거다. 단, 집 밖에서도 그 페이지가 열리면 공유기 원격관리가 노출된 것이니 꺼두자.
7. 운영
연결 상태는 서버에서 docker exec wg-easy wg show로 확인한다. 마지막 핸드셰이크 시각과 송수신량이 보인다. 문제가 생기면 그 사람 클라이언트만 UI에서 삭제·비활성화하면 된다.
완성 후 폰을 LTE로 전환해 연결하니 IP 조회에서 집 IP가 나왔다. 해외 가족에게는 QR 캡처만 보내면 된다. 만든 지 하루 만에 가족 등록까지 끝났다.