AdGuard Home으로 시작한 홈서버에 비밀번호 관리, Git 저장소와 상태 모니터링 서비스를 차례로 추가했다. 각 서비스의 데이터 보관 방식과 외부 접근 경로를 검토한 뒤 셀프호스팅 대상으로 선택했다.

공통점은 두 가지다. 전부 Docker 컨테이너로 띄운다. 그리고 바깥에서 오는 요청은 모두 같은 경로를 거쳐 안으로 들어온다. 그 경로를 따라가며 하나씩 적어둔다.

홈서버 셀프호스팅 대시보드

Homepage에 등록한 셀프호스팅 서비스 대시보드.

인터넷에서 집까지: Cloudflare Tunnel

외부 접근에는 Cloudflare Tunnel을 사용한다. 공유기에 서비스별 포트포워딩을 설정하지 않고 cloudflared가 ingress 규칙에 따라 도메인 요청을 내부 서비스로 전달한다.

ingress:
  - hostname: git.example.dev
    service: http://internal-host:3001
  - service: http_status:404

새 서비스를 바깥으로 뺄 때는 이 파일에 두 줄을 더하고 컨테이너만 다시 띄우면 된다.

도메인을 가르는 자리: Nginx Proxy Manager

터널을 통과한 요청은 Nginx Proxy Manager가 호스트 이름에 따라 각 컨테이너로 전달한다. Let's Encrypt 인증서의 발급과 갱신도 NPM에서 관리한다. 직접 작성한 nginx 설정에서 인증서 갱신을 누락한 경험이 있어 인증서 작업을 한 경로로 통합했다.

바닥에 깔린 DNS: AdGuard Home

홈서버에서 가장 먼저 구성한 서비스는 AdGuard Home이었다. 공유기의 DHCP 설정에서 DNS 주소를 AdGuard로 지정해 휴대전화와 TV를 포함한 내부 기기에 공통 적용했다. 내부 와일드카드 DNS도 이곳에서 처리해 새 서브도메인을 추가할 때 개별 레코드를 반복해서 등록하지 않도록 구성했다.

프록시 뒤에 사는 앱들

매일 쓰는 두 가지가 프록시 뒤에 있다.

Vaultwarden — Bitwarden 클라이언트와 호환되는 비공식 서버 구현이다. 컨테이너 하나로 운영하며 데이터 볼륨 백업, 관리자 경로 제한과 외부 접근 모니터링을 함께 적용했다.

Gitea — GitHub 레포를 mirror 모드로 가져와 내 서버에도 둔다. 한 번 걸어두면 주기적으로 동기화된다. 외부 노출 면에서 SSH는 끄고 HTTPS와 토큰만 쓰도록 막아뒀다.

죽었는지 지켜보는 눈: Uptime Kuma + ntfy

서비스가 늘면 하나가 죽어도 모르고 지나가기 쉽다. Uptime Kuma가 각 서비스를 주기적으로 확인하고, 죽으면 ntfy로 폰에 푸시를 보낸다. ntfy는 토픽 하나에 POST만 하면 폰 앱에 알림이 나타나는 구조다. 스크립트에서 curl 한 줄로도 알림을 보낼 수 있다. 백업 완료나 디스크 사용량 초과 같은 자작 알림을 붙이기에 좋다.

남은 작업

처음부터 많은 서비스를 운영할 계획은 아니었지만, 하나씩 추가하다 보니 일상에서 사용하는 여러 기능을 집의 서버에서 처리하게 됐다. 일부 구독 서비스를 대체했고 데이터를 직접 관리할 수 있게 됐다. 아직 보완할 부분은 백업이다. 현재 cron과 스크립트로 수행하는 백업을 복구 검증까지 포함한 절차로 개선할 계획이다.


참고