Pangolin + Traefik으로 홈서버 리버스 프록시 구축하기
셀프호스팅의 첫 번째 관문
홈서버에 서비스를 하나둘 올리다 보면 금방 마주치는 문제가 있습니다.
- Ghost 블로그는 3060 포트, Komga는 25600 포트, Authentik은 7080 포트...
- 외부에서 접근하려면
IP:포트로 들어가야 하는데, 이걸 도메인으로 연결하고 싶습니다. - HTTPS도 붙이고 싶고, 인증도 걸고 싶습니다.
이걸 해결하는 게 리버스 프록시입니다. Nginx Proxy Manager, Caddy 등 선택지가 많지만, 저는 Pangolin + Traefik 조합을 선택했습니다.
왜 Pangolin 을 쓰려고 했을까 보면
| 방식 | 장점 | 단점 |
|---|---|---|
| Nginx Proxy Manager | GUI 쉬움 | 설정 자유도 낮음, 자동화 어려움 |
| Caddy | 설정 간결 | 복잡한 미들웨어 체인 한계 |
| Traefik 단독 | 강력한 자동화 | 설정 파일이 복잡 |
| Pangolin + Traefik | 웹 UI + Traefik 파워 | 초기 세팅 복잡 |
Pangolin은 Traefik 위에 올라가는 관리 레이어입니다. 도메인 추가, SSL 인증서, 미들웨어 설정을 웹 UI에서 할 수 있으면서도 Traefik의 유연함을 그대로 쓸 수 있죠.
거기에 Gerbil(WireGuard 터널)이 붙어서, 외부에서 홈서버로 들어오는 트래픽을 안전하게 터널링합니다. 공유기 포트포워딩 없이도 외부 접근이 가능해집니다.
흐름을 살펴볼까요
인터넷 → Gerbil(:80/:443) → Traefik → 각 서비스
│
┌─────────────────────────┼──────────────────┐
│ │ │
authentik-server ghost(:3060) rxresume(:3020)
headscale-server livecap(:3030) sftpgo(:8080)
핵심 구성 요소:
- Pangolin: 도메인·라우팅 관리 API
- Gerbil: WireGuard 터널 + 포트 80/443 수신
- Traefik: 실제 리버스 프록시 (Gerbil 네트워크 공유)
- CrowdSec: 분산 위협 탐지 (IP 차단)
- Autoheal: unhealthy 컨테이너 자동 재시작
Docker Compose 구성
services:
pangolin:
image: fosrl/pangolin:1.18.1
container_name: pangolin-server
restart: unless-stopped
volumes:
- ./config:/app/config
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
interval: "10s"
timeout: "10s"
retries: 15
gerbil:
image: fosrl/gerbil:1.4.0
container_name: pangolin-gerbil
depends_on:
pangolin:
condition: service_healthy
command:
- --reachableAt=http://gerbil:3004
- --generateAndSaveKeyTo=/var/config/key
- --remoteConfig=http://pangolin:3001/api/v1/
cap_add:
- NET_ADMIN
- SYS_MODULE
ports:
- 80:80
- 443:443
- 51820:51820/udp
traefik:
image: traefik:v3.6.14
container_name: pangolin-traefik
network_mode: service:gerbil # Gerbil과 네트워크 공유
depends_on:
pangolin:
condition: service_healthy
gerbil:
condition: service_healthy
command:
- --configFile=/etc/traefik/traefik_config.yml
volumes:
- ./config/traefik:/etc/traefik:ro
- ./config/letsencrypt:/letsencrypt
env_file:
- .env
labels:
- "autoheal=true"
핵심 포인트:
- 시작 순서: Pangolin(healthy) → Gerbil(healthy) → Traefik.
depends_on으로 강제합니다. - network_mode: service:gerbil: Traefik이 Gerbil의 네트워크를 공유합니다. 포트는 Gerbil에서만 열면 됩니다.
- autoheal 라벨: Traefik이 unhealthy 상태가 되면 자동으로 재시작됩니다.
SSL 인증서: DNS Challenge
Let's Encrypt 인증서를 자동 발급받습니다. HTTP Challenge 대신 DNS Challenge를 쓰면 와일드카드 인증서도 가능합니다.
# traefik_config.yml
certificatesResolvers:
letsencrypt:
acme:
dnsChallenge:
provider: route53 # AWS Route53 사용
email: your@email.com
storage: "/letsencrypt/acme.json"
Route53, Cloudflare, 등 DNS 프로바이더에 맞는 환경변수를 .env에 넣으면 됩니다.
보안: CrowdSec + Autoheal
crowdsec:
image: crowdsecurity/crowdsec:v1.7.7
environment:
- COLLECTIONS=crowdsecurity/linux
- BOUNCER_KEY_firewall=${CROWDSEC_BOUNCER_KEY}
volumes:
- ./config/traefik/logs:/var/log/traefik:ro
autoheal:
image: willfarrell/autoheal:latest
environment:
- AUTOHEAL_CONTAINER_LABEL=autoheal
- AUTOHEAL_INTERVAL=15
volumes:
- /var/run/docker.sock:/var/run/docker.sock
network_mode: none
- CrowdSec: Traefik 로그를 읽어서 악성 IP를 자동 차단합니다. Traefik bouncer 플러그인과 연동하면 요청 단계에서 바로 블록됩니다.
- Autoheal:
autoheal=true라벨이 붙은 컨테이너가 unhealthy 상태가 되면 15초 간격으로 체크 후 재시작합니다.
서비스 연결하기
새 서비스를 추가할 때는:
- 해당 서비스의 Docker 네트워크에
pangolin을 external로 추가 - Pangolin 웹 UI에서 도메인 → 서비스 매핑 설정
# 예: Ghost 블로그
services:
ghost:
image: ghost:5
ports:
- "3060:2368"
networks:
- ghost
- pangolin # 이것만 추가하면 됨
networks:
pangolin:
external: true
Pangolin UI에서 blog.example.com → http://ghost:2368 매핑하면 끝입니다.
초기에 운영하면서 겪은 문제도...
Traefik이 간헐적으로 죽는 현상
Gerbil이 재시작되면 Traefik도 네트워크가 끊기면서 unhealthy 상태가 됩니다. autoheal 컨테이너를 추가해서 자동 복구되도록 했습니다.
서비스 추가 시 네트워크 연결 누락
pangolin 네트워크를 빼먹으면 Traefik에서 서비스에 접근을 못합니다. 502 에러가 나면 가장 먼저 네트워크 연결을 확인하는게 좋습니다.
마무리
Pangolin + Traefik 조합은 초기 세팅이 좀 복잡하지만, 한 번 잡아놓으면 서비스 추가가 매우 편합니다. 도메인 하나 추가하는 데 1분이면 충분하죠.
홈서버에 서비스가 5개 이상이라면 리버스 프록시는 필수입니다. 그 중에서도 자동화와 확장성을 원한다면 이 조합을 추천합니다.