Dynu & Caddy
Dynu와 Caddy를 조합하여 안정적인 DNS-01 챌린지 환경을 구축하는 단계별 가이드입니다.
DuckDNS에서 겪으셨던 DNS 조회 오류(SERVFAIL)를 방지하기 위해, Caddy가 직접 Dynu API를 통해 인증을 수행하도록 설정하는 것이 핵심입니다.
1단계: Dynu 도메인 및 API 키 준비
- 도메인 생성: Dynu Control Panel에서 원하는 호스트네임(예:
jace-dev.dynu.com)을 만듭니다. - API 키 발급: API Credentials 메뉴로 가서 API Key를 복사해둡니다. (이 키가 있어야 Caddy가 자동으로 DNS 설정을 변경할 수 있습니다.)
2단계: Dynu 플러그인이 포함된 Caddy 빌드
기본 Caddy 바이너리에는 Dynu 지원 기능이 없습니다. xcaddy를 사용해 플러그인을 넣어 빌드해야 합니다.
# xcaddy 설치 (Go가 설치되어 있어야 합니다)
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
# Dynu 플러그인을 포함하여 Caddy 빌드(~/go/bin/xcaddy)
xcaddy build --with github.com/caddy-dns/dynu빌드가 완료되면 현재 폴더에 caddy 실행 파일이 생깁니다.
3단계: Caddyfile 작성
환경 변수를 활용해 로컬, 테스트, 배포 환경을 하나로 관리하는 구조입니다.
{
# 전역 설정: 이메일은 Let's Encrypt 알림용
email your-email@example.com
}
# Snippet: Dynu DNS 인증 설정
(dynu_auth) {
tls {
dns dynu {
api_token {$DYNU_API_KEY}
}
}
}
# 메인 사이트 설정
{$SITE_ADDR:localhost} {
import dynu_auth
# 리버스 프록시 (개발 중인 포트에 맞춰 수정)
reverse_proxy localhost:3000
log {
output file ./access.log
}
}4단계: 환경별 실행 방법
🏁 로컬 개발 (localhost)
DNS 인증이 필요 없으므로 그냥 실행합니다.
./caddy run🚀 호스트 테스트 및 배포 (Dynu)
발급받은 API 키와 도메인을 환경 변수로 넘겨줍니다. 80번 포트가 막혀있어도 DNS-01 방식으로 인증서가 발급됩니다.
export DYNU_API_KEY="여러분의_API_키"
SITE_ADDR="jace-dev.dynu.com" ./caddy run💡 왜 이 방식이 더 좋은가요?
- 안정성:
http-01(80포트) 대신dns-01방식을 쓰기 때문에, 어제 겪으신 DuckDNS의 네임서버 불안정이나 통신사 포트 차단 문제에서 자유롭습니다. - 보안: 서버의 80번 포트를 외부로 개방할 필요가 전혀 없습니다.
- 와일드카드 지원: 나중에
*.jace-dev.dynu.com같은 와일드카드 인증서가 필요해져도 설정 변경 없이 바로 사용 가능합니다.
caddy-dynu
에러 메시지 module not registered: dns.providers.dynu는 현재 실행 중인 caddy 바이너리에 Dynu 플러그인이 포함되어 있지 않다는 뜻입니다.
Jace님이 이전에 xcaddy로 빌드하셨더라도, 실행할 때 시스템에 미리 설치되어 있던 기본 caddy를 실행했거나 빌드된 파일을 제대로 참조하지 못했을 가능성이 큽니다.
1. 현재 실행 파일 확인하기
먼저, 현재 사용 중인 caddy가 어떤 모듈을 가지고 있는지 확인해 보세요.
./caddy list-modules | grep dynu만약 아무것도 출력되지 않는다면, 그 파일은 Dynu 기능이 없는 일반 버전입니다.
2. 해결 방법: 다시 빌드하고 정확한 파일 실행
xcaddy로 새로 만든 바이너리를 확실하게 사용해야 합니다.
1단계: 다시 빌드 (출력 파일명 지정)
혼동을 피하기 위해 이름을 caddy-dynu로 지정해서 빌드해 보겠습니다.
xcaddy build --with github.com/caddy-dns/dynu --output caddy-dynu2단계: 모듈 포함 여부 재확인
./caddy-dynu list-modules | grep dynu여기서 dns.providers.dynu가 리스트에 나오면 성공입니다.
3단계: 새 파일로 실행
기존 caddy 대신 방금 만든 caddy-dynu를 실행하세요.
export DYNU_API_KEY="여러분의_키"
./caddy-dynu run --config Caddyfile3. 왜 이런 일이 생기나요? (추측)
- PATH 문제: 터미널에서 그냥
caddy라고 치면/usr/bin/caddy나/usr/local/bin/caddy에 있는 순정 버전이 우선 실행될 수 있습니다. - 파일 덮어쓰기 실패:
xcaddy build를 할 때 기존에caddy가 실행 중이었다면 파일이 교체되지 않았을 수 있습니다.
💡 팁
새로 만든 caddy-dynu 파일을 아예 시스템 경로로 옮겨두면 앞으로 caddy라고만 쳐도 플러그인 버전이 실행됩니다.
sudo mv ./caddy-dynu /usr/local/bin/caddy
caddy list-modules | grep dynu # 확인이제 다시 실행해 보시면 tls 블록의 import dynu_auth 부분이 에러 없이 정상적으로 파싱될 것입니다!
서브도메인 사용
로그를 분석해 보니, 서브도메인을 사용할 수 없는 것이 아니라 서브도메인 인증 과정에서 "이전 기록"이 방해를 주고 있는 상태입니다.
1. 현상 분석
로그의 핵심 문구는 이겁니다:
Incorrect TXT record "ly8r..." found at _acme-challenge.docs.jace1.mywire.org
- 성공:
jace1.mywire.org(루트)는 인증서 발급에 성공했습니다. - 실패:
docs.jace1.mywire.org(서브)는 Let's Encrypt가 확인하러 갔을 때, **현재 Caddy가 심은 값이 아닌 예전 값(또는 다른 값)**이 발견되어 거절당했습니다.
2. 왜 이런 일이 생기나요?
-
DNS 전파 지연 (DNS Propagation): Caddy가 Dynu API를 통해 새로운 TXT 레코드를 심었지만, Let's Encrypt 서버가 조회했을 때 Dynu의 네임서버(
ns1.dynu.com등)들이 아직 이전 값을 들고 있거나 업데이트된 값을 반환하지 않은 경우입니다. -
중복 레코드: Dynu 관리 페이지에
_acme-challenge.docs.jace1이라는 이름의 TXT 레코드가 이미 수동으로 입력되어 있거나, 이전 시도에서 지워지지 않고 남아있을 수 있습니다.
3. 해결 방법
방법 A: Dynu 관리 페이지에서 직접 정리 (가장 확실)
- Dynu 제어판의 DNS Records 메뉴로 들어갑니다.
TXT타입의 레코드 중 이름에_acme-challenge가 포함된 것들을 모두 삭제하세요.- 깨끗한 상태에서 Caddy를 다시 실행합니다.
방법 B: Caddy의 전파 대기 시간 늘리기
Dynu의 네임서버 업데이트가 느릴 수 있으므로, Caddy가 인증을 요청하기 전에 DNS가 퍼질 때까지 더 오래 기다리도록 설정할 수 있습니다. (다만, caddy-dns/dynu 플러그인 버전에 따라 옵션 지원 여부가 다를 수 있습니다.)
방법 C: 와일드카드 인증서 사용 (추천)
매번 서브도메인마다 개별 인증을 하는 대신, *.jace1.mywire.org로 하나만 받는 방식입니다.
# Caddyfile 수정 제안
*.jace1.mywire.org, jace1.mywire.org {
import dynu_auth
# 도메인에 따라 프록시 분기
@docs host docs.jace1.mywire.org
handle @docs {
reverse_proxy localhost:4009
}
handle {
reverse_proxy localhost:4000
}
}💡 결론
서브도메인 당연히 사용 가능합니다! 지금 에러는 단지 "예전 TXT 레코드가 아직 DNS 상에 남아 있어서" 발생하는 일시적인 충돌입니다.
- Dynu 사이트에서 TXT 레코드를 다 지우고 다시 해보시거나,
- 5~10분 정도 뒤에 다시 실행해 보세요. (자동으로 해결되는 경우가 많습니다.)