목차
- 홈랩에서 Docker Compose가 정말 빛나는 순간
- Docker Compose란 뭔가요? — 쉽게 말해서
- 2026년 Docker Compose 최신 버전 현황
- 홈랩 환경 준비 — 제 실제 서버 스펙 공개
- Docker Engine 설치 확인
- 실전 구현 — 홈랩 멀티컨테이너 스택 구성하기
- 1단계: 디렉터리 구조 잡기
- 2단계: 환경변수 파일(.env) 설정
- 3단계: 메인 compose.yaml 작성
- 4단계: Traefik 설정 파일 작성
- 5단계: 스택 실행
- ⚠️ 삽질 모음 — 제가 겪은 트러블슈팅
- 문제 1: "network proxy not found" 에러
- 문제 2: Nextcloud 설치 후 "신뢰할 수 없는 도메인" 에러
- 문제 3: 컨테이너 시작 순서 문제 (depends_on과 healthcheck)
- 문제 4: 볼륨 권한 문제
- 2026년 Docker Compose 신기능 — Watch 모드와 Profiles
- Profiles — 환경별 선택적 실행
- Watch 모드 — 개발 환경에서 파일 변경 자동 감지
- 결과 확인 — 홈랩 모니터링 대시보드
- 서비스 접속 URL 정리
- 자주 묻는 질문 (FAQ)
- Q. Docker Compose와 Kubernetes(쿠버네티스) 중 홈랩엔 뭐가 낫나요?
- Q. compose.yaml 파일을 여러 개로 나눌 수 있나요?
- Q. 서버 재부팅 시 자동으로 컨테이너가 시작되나요?
- 마무리 — 그래서 뭘 배웠냐고요?
홈랩에서 Docker Compose가 정말 빛나는 순간
혹시 이런 경험 있으신가요? 홈서버에 이것저것 서비스를 올리다 보니 어느 순간 컨테이너가 10개, 20개를 넘어서고, docker run 명령어를 매번 손으로 치고 있는 자신을 발견하는 그 순간. 저도 그랬거든요. 3년 전쯤 홈랩을 본격적으로 운영하기 시작했을 때, Plex, Jellyfin, Nextcloud, Pi-hole까지 올리고 나니까 진짜 관리가 엉망이 됐었습니다.
그때 Docker Compose를 제대로 파고들기 시작했는데, 2026년 현재 버전은 제가 처음 쓰던 것과 꽤 많이 달라졌더라고요. Docker Compose 2026 최신 버전에서는 홈랩 멀티컨테이너 구성이 훨씬 더 직관적이고 강력해졌습니다. 이 글에서는 제가 직접 홈서버에 적용하면서 겪은 삽질과 노하우를 솔직하게 공유해 드릴게요.
▲ 홈랩 환경에서 Docker Compose로 구성한 멀티컨테이너 전체 아키텍처 — Reverse Proxy부터 미디어 서버, 모니터링 스택까지 한눈에 보이는 구성도입니다.
Docker Compose란 뭔가요? — 쉽게 말해서
기술적인 정의는 잠깐 접어두고요. 쉽게 말해서 Docker Compose는 여러 개의 컨테이너를 하나의 설정 파일로 한꺼번에 관리하는 도구입니다. 컨테이너 오케스트레이션의 가장 기본적인 형태라고 보시면 돼요.
예를 들어 WordPress를 올린다고 하면, 웹 서버 컨테이너 하나, MySQL 데이터베이스 컨테이너 하나, 이렇게 최소 두 개가 필요하잖아요. 이걸 docker run으로 두 번 치고, 네트워크 연결하고, 볼륨 마운트하고... 매번 이 과정을 반복하는 건 진짜 고통입니다.
Docker Compose를 쓰면 docker compose up -d 명령어 하나로 끝나거든요. 그게 핵심입니다.
2026년 Docker Compose 최신 버전 현황
예전에는 docker-compose(하이픈 있음)와 docker compose(하이픈 없음) 두 가지가 공존했었는데, 지금은 완전히 정리됐습니다. 2026년 현재는 Docker Compose V2가 표준이고, Docker Engine에 플러그인 형태로 내장되어 있어요. 별도 설치가 필요 없습니다.
| 구분 | 구버전 (V1) | 현재 (V2, 2026) |
|---|---|---|
| 명령어 | docker-compose up |
docker compose up |
| 설치 방식 | 별도 Python 패키지 | Docker Engine 내장 플러그인 |
| 성능 | 상대적으로 느림 | Go 언어 기반, 빠름 |
| 설정 파일 | version 필드 필수 | version 필드 불필요 (deprecated) |
| Watch 모드 | 미지원 | ✅ 지원 (개발 환경에 유용) |
💡 팁: 2026년에는 compose.yaml이 권장 파일명입니다. 물론 docker-compose.yml도 여전히 인식하지만, 새로 만드신다면 compose.yaml로 쓰는 게 좋아요.
홈랩 환경 준비 — 제 실제 서버 스펙 공개
글을 읽으시는 분들이 어떤 환경에서 따라 하실지 모르니까, 제 홈랩 환경을 먼저 공개할게요. 비싼 장비 없어도 됩니다. 저도 중고 미니PC로 시작했거든요.
- 하드웨어: Intel N100 미니PC, RAM 16GB, SSD 256GB + HDD 4TB
- OS: Ubuntu Server 24.04 LTS
- Docker: Docker Engine 27.x (2026년 최신)
- 네트워크: 공유기 포트포워딩 + Cloudflare Tunnel
Docker Engine 설치 확인
먼저 Docker가 제대로 설치되어 있는지 확인해 봅시다.
# Docker 버전 확인
docker --version
# Docker Engine 27.x.x 이상이면 OK
# Compose 플러그인 확인
docker compose version
# Docker Compose version v2.x.x 이상이면 OK
# Docker 서비스 자동 시작 설정
sudo systemctl enable docker
sudo systemctl start docker
저는 처음에 docker compose version을 쳤는데 명령어를 못 찾는다는 에러가 났었어요. 알고 보니 Docker Desktop이 아닌 Docker Engine만 설치했을 때 Compose 플러그인이 빠져있는 경우가 있더라고요. 그럴 땐 아래 명령어로 추가 설치하시면 됩니다.
sudo apt-get install docker-compose-plugin
실전 구현 — 홈랩 멀티컨테이너 스택 구성하기
자, 이제 본론입니다. 제가 홈랩에서 실제로 운영 중인 Docker Compose 스택을 기반으로 설명드릴게요. 구성은 이렇습니다.
- Traefik(트래픽, Reverse Proxy): 외부 트래픽을 각 서비스로 라우팅
- Jellyfin: 미디어 서버
- Nextcloud: 개인 클라우드 스토리지
- Portainer: 컨테이너 관리 웹 UI
- Prometheus + Grafana: 모니터링 스택
1단계: 디렉터리 구조 잡기
홈랩에서 제일 처음 배운 교훈이 있어요. 디렉터리 구조를 처음부터 잘 잡아야 나중에 고생을 안 한다는 거예요. 저는 처음에 막 아무 데나 올렸다가 나중에 다 갈아엎었습니다 ㅎㅎ
mkdir -p ~/homelab
cd ~/homelab
# 서비스별 디렉터리 생성
mkdir -p traefik/config
mkdir -p jellyfin/config
mkdir -p nextcloud/{config,data}
mkdir -p portainer/data
mkdir -p monitoring/{prometheus,grafana}
# 공통 환경변수 파일 생성
touch .env
2단계: 환경변수 파일(.env) 설정
비밀번호나 도메인 같은 민감한 정보는 절대 compose.yaml에 직접 쓰면 안 됩니다. 이건 진짜 중요한 포인트예요! .env 파일에 분리하고, Git에 올릴 때는 반드시 .gitignore에 추가하세요.
# ~/homelab/.env
# 도메인 설정
DOMAIN=homelab.example.com
# Traefik 설정
ACME_EMAIL=your@email.com
# Nextcloud 설정
NC_DB_PASSWORD=super_secure_password_here
NC_ADMIN_USER=admin
NC_ADMIN_PASSWORD=another_secure_password
# Grafana 설정
GF_ADMIN_PASSWORD=grafana_password
# 타임존
TZ=Asia/Seoul
3단계: 메인 compose.yaml 작성
드디어 핵심입니다. 아래가 제가 실제로 쓰는 Docker Compose 구성 파일이에요. 처음 보면 좀 길어 보이지만, 섹션별로 나눠서 설명드릴게요.
▲ compose.yaml 파일에서 각 서비스(Traefik, Jellyfin, Nextcloud, Monitoring)가 내부 네트워크로 연결되고 Traefik을 통해 외부에 노출되는 구조를 보여주는 다이어그램입니다.
# ~/homelab/compose.yaml
# Docker Compose 2026 최신 형식 (version 필드 생략)
services:
# ================================
# Traefik — Reverse Proxy (역방향 프록시)
# ================================
traefik:
image: traefik:v3.2
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
ports:
- "80:80"
- "443:443"
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik/config/traefik.yml:/traefik.yml:ro
- ./traefik/config/acme.json:/acme.json
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN}`)"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
- "traefik.http.services.traefik.loadbalancer.server.port=8080"
# ================================
# Jellyfin — 미디어 서버
# ================================
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
restart: unless-stopped
environment:
- TZ=${TZ}
volumes:
- ./jellyfin/config:/config
- /mnt/hdd/media:/media:ro # HDD 미디어 폴더 마운트
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.jellyfin.rule=Host(`media.${DOMAIN}`)"
- "traefik.http.routers.jellyfin.entrypoints=websecure"
- "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
- "traefik.http.services.jellyfin.loadbalancer.server.port=8096"
devices:
- /dev/dri:/dev/dri # Intel 하드웨어 트랜스코딩 활성화
# ================================
# Nextcloud — 개인 클라우드
# ================================
nextcloud-db:
image: mariadb:11
container_name: nextcloud-db
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=${NC_DB_PASSWORD}
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=${NC_DB_PASSWORD}
volumes:
- ./nextcloud/db:/var/lib/mysql
networks:
- nextcloud-internal
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
nextcloud:
image: nextcloud:29
container_name: nextcloud
restart: unless-stopped
depends_on:
nextcloud-db:
condition: service_healthy
environment:
- MYSQL_HOST=nextcloud-db
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=${NC_DB_PASSWORD}
- NEXTCLOUD_ADMIN_USER=${NC_ADMIN_USER}
- NEXTCLOUD_ADMIN_PASSWORD=${NC_ADMIN_PASSWORD}
- TZ=${TZ}
volumes:
- ./nextcloud/config:/var/www/html
- ./nextcloud/data:/var/www/html/data
networks:
- proxy
- nextcloud-internal
labels:
- "traefik.enable=true"
- "traefik.http.routers.nextcloud.rule=Host(`cloud.${DOMAIN}`)"
- "traefik.http.routers.nextcloud.entrypoints=websecure"
- "traefik.http.routers.nextcloud.tls.certresolver=letsencrypt"
- "traefik.http.services.nextcloud.loadbalancer.server.port=80"
# ================================
# Portainer — 컨테이너 관리 UI
# ================================
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./portainer/data:/data
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.portainer.rule=Host(`portainer.${DOMAIN}`)"
- "traefik.http.routers.portainer.entrypoints=websecure"
- "traefik.http.routers.portainer.tls.certresolver=letsencrypt"
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
# ================================
# Prometheus — 메트릭 수집
# ================================
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
volumes:
- ./monitoring/prometheus:/etc/prometheus
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
networks:
- monitoring
- proxy
# ================================
# Grafana — 모니터링 대시보드
# ================================
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GF_ADMIN_PASSWORD}
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- ./monitoring/grafana:/var/lib/grafana
depends_on:
- prometheus
networks:
- monitoring
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.grafana.rule=Host(`monitor.${DOMAIN}`)"
- "traefik.http.routers.grafana.entrypoints=websecure"
- "traefik.http.routers.grafana.tls.certresolver=letsencrypt"
- "traefik.http.services.grafana.loadbalancer.server.port=3000"
# ================================
# 네트워크 정의
# ================================
networks:
proxy:
name: proxy
driver: bridge
nextcloud-internal:
name: nextcloud-internal
driver: bridge
internal: true # 외부 접근 차단
monitoring:
name: monitoring
driver: bridge
# ================================
# 볼륨 정의
# ================================
volumes:
prometheus_data:
driver: local
4단계: Traefik 설정 파일 작성
Traefik은 설정이 살짝 까다롭습니다. 저도 처음엔 이게 뭔가 싶었는데, 한 번 이해하고 나면 진짜 편해요.
# ~/homelab/traefik/config/traefik.yml
api:
dashboard: true
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
http:
tls:
certResolver: letsencrypt
certificatesResolvers:
letsencrypt:
acme:
email: "${ACME_EMAIL}"
storage: /acme.json
httpChallenge:
entryPoint: web
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: proxy
log:
level: INFO
# acme.json 파일 권한 설정 (이거 빠뜨리면 Traefik이 시작 안 됩니다!)
touch ~/homelab/traefik/config/acme.json
chmod 600 ~/homelab/traefik/config/acme.json
5단계: 스택 실행
드디어 실행할 차례입니다!
cd ~/homelab
# 백그라운드로 전체 스택 시작
docker compose up -d
# 로그 확인 (실시간)
docker compose logs -f
# 특정 서비스 로그만 보기
docker compose logs -f traefik
# 실행 중인 컨테이너 상태 확인
docker compose ps
⚠️ 삽질 모음 — 제가 겪은 트러블슈팅
이 섹션이 사실 제일 중요할 수도 있어요. 저도 처음에 온갖 에러를 다 만났거든요. 미리 알고 계시면 몇 시간은 절약하실 수 있을 겁니다.
문제 1: "network proxy not found" 에러
여러 개의 compose.yaml 파일을 쓸 때 자주 생기는 문제입니다. 특히 Traefik을 별도 파일로 분리했을 때요.
# 해결방법: 네트워크를 external로 선언하고 미리 생성
docker network create proxy
# 또는 compose.yaml에서 external 설정
networks:
proxy:
external: true # 이미 존재하는 네트워크를 사용
문제 2: Nextcloud 설치 후 "신뢰할 수 없는 도메인" 에러
Nextcloud를 처음 올리면 이 에러 꼭 만납니다. 도메인을 신뢰 목록에 추가해야 해요.
# Nextcloud 컨테이너 내부에서 실행
docker exec -u www-data nextcloud php occ config:system:set trusted_domains 1 --value=cloud.homelab.example.com
문제 3: 컨테이너 시작 순서 문제 (depends_on과 healthcheck)
이거 진짜 헷갈리는 부분인데요. depends_on은 컨테이너가 시작되는 것만 기다리고, 서비스가 준비되는 것은 기다리지 않습니다. MySQL이 완전히 초기화되기 전에 Nextcloud가 연결을 시도해서 에러가 나는 경우가 많아요. 2026년 방식은 healthcheck를 활용하는 거거든요.
# healthcheck를 통한 의존성 관리 (위 compose.yaml 코드 참조)
nextcloud-db:
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
nextcloud:
depends_on:
nextcloud-db:
condition: service_healthy # healthy 상태가 될 때까지 대기
문제 4: 볼륨 권한 문제
컨테이너 안에서 파일을 쓰지 못한다는 에러가 나면 대부분 권한 문제입니다.
# 호스트에서 볼륨 디렉터리 권한 조정
sudo chown -R 1000:1000 ~/homelab/grafana
# 또는 컨테이너 내부 UID에 맞게 조정 (Grafana는 472)
sudo chown -R 472:472 ~/homelab/monitoring/grafana
2026년 Docker Compose 신기능 — Watch 모드와 Profiles
최신 버전에서 추가된 기능 중에 홈랩에서 진짜 유용하게 쓰는 것들 소개해 드릴게요.
Profiles — 환경별 선택적 실행
개발 환경에서만 쓰는 서비스가 있잖아요. profiles를 쓰면 상황에 따라 특정 서비스만 선택적으로 올릴 수 있어요.
services:
# 항상 실행
traefik:
image: traefik:v3.2
# profiles 없으면 기본 실행
# debug 프로파일일 때만 실행
phpmyadmin:
image: phpmyadmin:latest
profiles:
- debug
# ...
# 기본 실행 (phpmyadmin 제외)
docker compose up -d
# debug 프로파일 포함해서 실행
docker compose --profile debug up -d
Watch 모드 — 개발 환경에서 파일 변경 자동 감지
홈랩에서 직접 개발하시는 분들한테 유용한 기능이에요. 파일이 변경되면 자동으로 컨테이너를 업데이트해 줍니다.
services:
myapp:
image: node:20
develop:
watch:
- action: sync
path: ./src
target: /app/src
- action: rebuild
path: package.json
# Watch 모드로 실행
docker compose watch
결과 확인 — 홈랩 모니터링 대시보드
모든 서비스가 올라가고 나면 이런 것들을 확인하실 수 있어요. 처음에 Grafana 대시보드가 뜨는 순간 드디어 됐다! 하는 느낌이 오거든요. 그 뿌듯함은 직접 경험해 보셔야 알아요.
# 전체 서비스 상태 한눈에 보기
docker compose ps
# 출력 예시:
# NAME IMAGE STATUS PORTS
# traefik traefik:v3.2 Up 2 hours 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp
# jellyfin jellyfin/jellyfin Up 2 hours
# nextcloud nextcloud:29 Up 2 hours
# nextcloud-db mariadb:11 Up 2 hours
# portainer portainer/portainer-ce Up 2 hours
# prometheus prom/prometheus Up 2 hours
# grafana grafana/grafana Up 2 hours
# 리소스 사용량 실시간 모니터링
docker stats
# 전체 스택 재시작
docker compose restart
# 이미지 업데이트 후 재배포
docker compose pull && docker compose up -d
▲ Grafana 대시보드에서 각 컨테이너의 CPU, 메모리, 네트워크 사용량을 실시간으로 모니터링하는 화면입니다. Prometheus와 연동하면 이런 대시보드를 손쉽게 구성할 수 있습니다.
서비스 접속 URL 정리
| 서비스 | 접속 URL | 용도 |
|---|---|---|
| Traefik Dashboard | https://traefik.homelab.example.com | 라우팅 현황 확인 |
| Jellyfin | https://media.homelab.example.com | 미디어 스트리밍 |
| Nextcloud | https://cloud.homelab.example.com | 파일 관리 |
| Portainer | https://portainer.homelab.example.com | 컨테이너 관리 |
| Grafana | https://monitor.homelab.example.com | 모니터링 대시보드 |
자주 묻는 질문 (FAQ)
Q. Docker Compose와 Kubernetes(쿠버네티스) 중 홈랩엔 뭐가 낫나요?
솔직히 말씀드리면, 홈랩 규모에서는 Docker Compose가 훨씬 현실적입니다. Kubernetes는 학습 곡선이 가파르고, 리소스 오버헤드도 상당해요. 컨테이너 오케스트레이션을 배우는 목적이라면 Docker Compose를 완전히 익히신 다음에 K3s(경량 쿠버네티스)로 넘어가시는 걸 추천드려요. 저도 그 순서로 왔거든요.
Q. compose.yaml 파일을 여러 개로 나눌 수 있나요?
네, 가능합니다! -f 옵션으로 여러 파일을 합쳐서 실행할 수 있어요.
docker compose -f compose.yaml -f compose.monitoring.yaml up -d
Q. 서버 재부팅 시 자동으로 컨테이너가 시작되나요?
restart: unless-stopped 옵션을 넣으면 됩니다. Docker 서비스가 자동 시작(systemctl enable docker)으로 설정되어 있어야 해요.
▲ Docker Compose 홈랩 구성 전체 흐름 요약 인포그래픽 — 설치부터 서비스 배포, 모니터링까지 단계별 로드맵을 한눈에 정리한 이미지입니다.
마무리 — 그래서 뭘 배웠냐고요?
13년 동안 인프라 엔지니어로 일하면서 느끼는 건데, 결국 좋은 도구는 복잡한 것을 단순하게 만들어 줄 때 진가가 드러나더라고요. Docker Compose 2026 버전이 딱 그런 도구입니다. 홈랩 멀티컨테이너 구성이 이렇게 파일 하나로 관리되는 게 처음엔 신기했는데, 지금은 없으면 못 살 것 같아요.
오늘 다룬 내용을 정리하면:
- ✅ Docker Compose V2는 Docker Engine에 내장, 별도 설치 불필요
- ✅
version필드는 2026년에 deprecated, 생략하세요 - ✅ .env 파일로 민감 정보 분리는 필수
- ✅
depends_on과healthcheck를 함께 써야 제대로 된 의존성 관리 - ✅ 네트워크 분리로 서비스 간 격리 (internal 네트워크 활용)
- ✅ Profiles로 환경별 선택적 실행 가능
다음 글에서는 이 구성을 바탕으로 자동 업데이트(Watchtower)와 백업 자동화를 적용하는 방법을 다룰 예정이에요. 홈서버 운영에서 자동화는 정말 게임 체인저거든요. 이전 글에서 홈랩 초기 세팅 방법도 다뤘으니 참고하시면 좋을 것 같습니다.
궁금한 점이나 삽질 경험 있으시면 댓글로 편하게 남겨주세요. 저도 아직 배우는 중이라, 같이 이야기 나누는 게 좋더라고요. 🎉
'IT > HomeLabs' 카테고리의 다른 글
| [HomeLabs] WireGuard VPN으로 홈랩 네트워크 보안 강화하기 완벽 가이드 (0) | 2026.04.13 |
|---|---|
| [HomeLabs] UPS 홈서버 전원 관리: 정전 대비 및 데이터 보호 완벽 가이드 (0) | 2026.04.13 |
| [HomeLabs] Home Assistant 음성 제어 2026.4: 로컬 AI 홈 오토메이션 완벽 가이드 (0) | 2026.04.09 |
| [HomeLabs] Home Assistant를 텔레그램으로 제어하기: Gemini AI 자연어 스마트홈 봇 만들기 (0) | 2026.04.05 |
| [HomeLabs] 저전력 미니PC로 나만의 홈서버 구축하기: 전력소모 줄이기 (0) | 2026.04.05 |
| [HomeLabs] Proxmox에 Home Assistant 올리기 ② — Mushroom Cards 대시보드 만들기 (0) | 2026.04.04 |