셀프호스팅 서비스를 하나둘 늘려가다 보면 서비스가 5개, 10개를 넘는 순간 “이 서비스는 어떻게 설치했더라?”를 기억하지 못하는 상황이 와요. Docker Compose는 이 문제를 해결하는 표준적인 방법으로, 각 서비스의 설정을 하나의 YAML 파일로 코드화해두면 서버를 재설치하거나 다른 하드웨어로 옮길 때도 명령어 한 줄로 동일한 환경을 재현할 수 있어요. 그렇다면 Docker Compose로 홈서버 스택을 구성하는 법을 실제로 어떻게 시작하면 될까요?
왜 개별 docker run 대신 Compose인가
docker run 명령으로 컨테이너를 하나씩 실행하는 것도 가능하지만, 옵션이 많아질수록(포트 매핑, 볼륨, 환경변수, 네트워크 설정) 명령어가 길어지고 관리가 어려워져요. Docker Compose는 이 모든 설정을 파일로 선언해두고, docker compose up -d 한 번으로 전체 스택을 실행해요. 무엇보다 이 YAML 파일 자체를 Git으로 버전 관리할 수 있다는 점이 큰 장점이에요.
홈서버에서 흔히 쓰는 디렉터리 구조
서비스별로 폴더를 분리해 각자의 docker-compose.yml과 .env를 따로 두는 구조가 홈랩 커뮤니티에서 가장 흔히 쓰여요.
homelab/
├── nextcloud/
│ ├── docker-compose.yml
│ └── .env
├── jellyfin/
│ ├── docker-compose.yml
│ └── .env
├── pihole/
│ └── docker-compose.yml
└── proxy/
└── docker-compose.yml
서비스마다 별도의 폴더와 Compose 파일을 두면, 한 서비스를 업데이트하거나 재시작할 때 다른 서비스에 영향을 주지 않고 독립적으로 관리할 수 있어요. 위 예시의 Pi-hole처럼 이미 별도로 다룬 서비스도 같은 방식으로 이 스택에 그대로 편입시킬 수 있어요.
네트워크 설계: 리버스 프록시와 내부 네트워크
여러 서비스를 외부에 노출하려면 각 서비스마다 다른 포트를 열어두는 것보다, 하나의 리버스 프록시(Nginx Proxy Manager, Traefik 등)가 80/443 포트만 열고 내부적으로 각 서비스의 컨테이너로 트래픽을 전달하는 구조가 안전하고 관리하기 쉬워요. 이를 위해 Docker의 커스텀 네트워크를 활용해, 프록시 컨테이너와 각 서비스 컨테이너가 같은 내부 네트워크에 속하도록 구성해요.
networks:
proxy_net:
external: true
각 서비스의 Compose 파일에서 이 외부 네트워크를 참조하도록 설정하면, 프록시가 컨테이너 이름으로 각 서비스에 접근할 수 있어요.
환경변수와 비밀 정보 관리
비밀번호나 API 키를 Compose 파일에 직접 하드코딩하는 대신, .env 파일에 분리해서 관리하는 게 기본이에요. .env 파일은 반드시 .gitignore에 추가해 실수로 저장소에 커밋되지 않도록 해야 해요. 여러 서비스에서 공통으로 쓰는 환경변수는 최상위 .env 파일 하나로 관리하고 각 Compose 파일에서 참조하는 방식으로 중복을 줄일 수 있어요.
업데이트 전략
docker compose pull && docker compose up -d 명령으로 최신 이미지를 받아 컨테이너를 재생성할 수 있어요. 다만 프로덕션처럼 다루어야 하는 서비스(비밀번호 관리자, 파일 저장소 등)는 업데이트 전 반드시 데이터 백업을 먼저 수행하고, 가능하다면 Watchtower 같은 자동 업데이트 도구보다는 변경 사항을 확인한 뒤 수동으로 업데이트하는 게 안전해요.
리소스 제한 설정하기
여러 컨테이너를 하나의 서버에서 동시에 운영하면, 특정 서비스가 리소스를 독점해 다른 서비스에 영향을 줄 수 있어요. Compose 파일의 deploy.resources 설정으로 각 컨테이너의 CPU/메모리 사용량 상한을 지정해두면, 하나의 서비스가 폭주하더라도 전체 시스템이 함께 다운되는 걸 방지할 수 있어요.
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
결론
Docker Compose는 셀프호스팅 홈서버를 “그때그때 명령어로 설치한 것들의 모음”에서 “코드로 관리되는 재현 가능한 인프라”로 바꿔주는 핵심 도구예요. 서비스가 늘어날수록 이 구조화의 가치는 기하급수적으로 커지기 때문에, 처음 홈서버를 구축할 때부터 Compose 기반으로 습관을 들이는 걸 강력히 추천해요. 컨테이너가 많아져 명령줄로 관리하기 번거로워졌다면 Portainer 같은 웹 GUI를 앞단에 두는 것도 좋은 다음 단계예요.
자주 묻는 질문
Q1. docker run 대신 꼭 Docker Compose를 써야 하나요?
필수는 아니지만, 옵션이 많아질수록 명령어가 길어져 관리가 어려워지므로 설정을 YAML 파일로 코드화하고 Git으로 버전 관리할 수 있는 Compose가 실질적으로 훨씬 편해요.
Q2. 서비스마다 폴더를 따로 두는 이유가 뭔가요?
한 서비스를 업데이트하거나 재시작할 때 다른 서비스에 영향을 주지 않고 독립적으로 관리하기 위해서예요.
Q3. 비밀번호나 API 키는 Compose 파일에 바로 적어도 되나요?
안 돼요. `.env` 파일로 분리하고 `.gitignore`에 추가해 저장소에 실수로 커밋되지 않도록 해야 해요.
Q4. Watchtower로 자동 업데이트를 걸어두는 게 안전한가요?
비밀번호 관리자나 파일 저장소처럼 데이터가 중요한 서비스는 자동 업데이트보다 변경 사항을 확인한 뒤 수동으로 업데이트하고, 업데이트 전 반드시 데이터를 백업하는 게 안전해요.
Q5. 컨테이너 하나가 리소스를 독점하면 어떻게 막나요?
Compose 파일의 `deploy.resources`로 각 컨테이너의 CPU/메모리 사용량 상한을 지정해두면, 특정 서비스가 폭주해도 전체 시스템이 함께 다운되는 것을 막을 수 있어요.
댓글
첫 번째 댓글을 남겨보세요!
Mealie로 레시피와 장보기 목록 셀프호스팅하기
여기저기 저장해둔 요리 레시피가 흩어져 사라지는 문제를, Mealie로 직접 운영하는 레시피 관리 서버와 자동 장보기 목록 기능으로 해결하는 방법을 소개합니다.
MealieRestic vs Duplicati: 홈서버 백업 도구 비교
명령줄 기반의 가벼운 Restic과 웹 UI를 갖춘 Duplicati, 홈서버 데이터를 클라우드나 NAS로 안전하게 백업할 때 두 도구가 어떻게 다르고 어떤 상황에 맞는지 비교합니다.
ResticWireGuard로 직접 VPN 서버 구축하기
커널에 통합될 만큼 가볍고 빠른 WireGuard 프로토콜의 구조와, 집 서버에 직접 VPN을 구축해 외부에서 홈네트워크 전체에 안전하게 접속하는 방법을 정리합니다.
WireGuard