내가 Hetzner에서 쓰는 Docker Compose와 하드닝 스크립트를 포함한 완전 셀프‑호스팅 스택 (전부 공개)
출처: Dev.to
원래 Reddit의 r/selfhosted에 올린 글이며, dev.to 커뮤니티와 공유합니다.
2년 넘게 Hetzner CX32(4 vCPU, 8 GB RAM, 월 €15) 한 대에서 셀프‑호스팅 환경을 운영한 뒤, 설정을 정리해 재사용 가능한 형태로 만들었습니다. 현재 18개 이상의 컨테이너를 구동 중이며, 여유 RAM은 약 1.7 GB 정도입니다. 처음 시작하거나 최적화를 고민하는 분들에게 도움이 되길 바라며 전체 설정을 공유합니다.
실행 중인 서비스
├── Reverse Proxy: Caddy (자동 HTTPS, 매우 간단한 설정)
├── Monitoring: Uptime Kuma + Prometheus + Grafana
├── Analytics: Matomo (셀프‑호스팅, Google 없음)
├── Passwords: Vaultwarden
├── Notes: Hedgedoc
├── Files: Nextcloud
├── Media: Jellyfin
├── Git: Gitea + Drone CI
├── DNS: AdGuard Home
├── Automation: n8n
├── Backup: Restic → Hetzner Storage Box
└── DSGVO Scanner: Custom (아래에서 자세히 설명)
Docker‑Compose 설정 (프로파일 사용)
# docker-compose.yml (simplified)
version: "3.8"
services:
caddy:
image: caddy:2-alpine
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
profiles: ["core"]
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
restart: unless-stopped
volumes:
- uptime-kuma:/app/data
profiles: ["monitoring"]
matomo:
image: matomo:latest
container_name: matomo
restart: unless-stopped
depends_on:
- matomo-db
environment:
- MATOMO_DATABASE_HOST=matomo-db
volumes:
- matomo:/var/www/html
profiles: ["analytics"]
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
environment:
- ADMIN_TOKEN=${VAULTWARDEN_ADMIN_TOKEN}
- SMTP_HOST=${SMTP_HOST}
volumes:
- vaultwarden:/data
profiles: ["security"]
volumes:
caddy_data:
caddy_config:
uptime-kuma:
matomo:
vaultwarden:
- 모든 서비스 시작:
docker compose --profile core --profile monitoring --profile security up -d - 핵심 서비스만 시작:
docker compose --profile core up -d
새 Hetzner 서버에 적용하는 스크립트
#!/bin/bash
# server-harden.sh — 새 Debian/Ubuntu에 root 권한으로 실행
set -euo pipefail
# 1. SSH 하드닝
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
cat > /etc/ssh/sshd_config.d/hardening.conf <<EOF
PermitRootLogin prohibit-password
PasswordAuthentication no
PubkeyAuthentication yes
X11Forwarding no
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
EOF
systemctl restart sshd
# 2. 방화벽
apt install -y ufw
ufw default deny incoming
ufw default allow outgoing
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 22/tcp
ufw --force enable
# 3. Fail2ban
apt install -y fail2ban
systemctl enable --now fail2ban
# 4. 자동 업데이트
apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
# 5. Docker 설치
curl -fsSL https://get.docker.com | sh
systemctl enable docker
# 6. 모니터링 에이전트
apt install -y prometheus-node-exporter
systemctl enable --now prometheus-node-exporter
echo "✅ 서버 하드닝 완료. 재부팅을 권장합니다."
Caddyfile (자동 HTTPS 적용)
{
email admin@yourdomain.de
}
grafana.yourdomain.de {
reverse_proxy grafana:3000
}
uptime.yourdomain.de {
reverse_proxy uptime-kuma:3001
}
bitwarden.yourdomain.de {
reverse_proxy vaultwarden:80
}
matomo.yourdomain.de {
reverse_proxy matomo:80
}
*.yourdomain.de {
@nc host cloud.yourdomain.de
handle @nc {
reverse_proxy nextcloud:80
}
}
백업 스크립트 (매일 새벽 3시 cron 실행)
#!/bin/bash
# backup.sh — 매일 3시 cron 으로 실행
export RESTIC_REPOSITORY=/mnt/storagebox/backups
export RESTIC_PASSWORD_FILE=/root/.restic-password
# 일관된 백업을 위해 서비스 일시 정지
docker compose -f /opt/selfhosted/docker-compose.yml stop matomo nextcloud
# Docker 볼륨 + 설정 백업
restic backup /var/lib/docker/volumes /opt/selfhosted
# 서비스 재시작
docker compose -f /opt/selfhosted/docker-compose.yml start matomo nextcloud
# 오래된 백업 정리 (일일 7개, 주간 4개, 월간 3개 보관)
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 3 --prune
DSGVO(독일 개인정보보호법) 스캐너
독일 서버를 사용하고 있기 때문에 셀프‑호스팅 서비스도 DSGVO에 맞춰야 합니다. 개인 프로젝트라도 사용자 데이터를 수집한다면 적용 대상이 됩니다. 나는 다음과 같은 항목을 검사하는 스캐너를 직접 만들었습니다.
- 외부 리소스 로드 여부 (EU 외부의 Google Fonts, CDN 등)
- 쿠키 동의 상태
- SSL/TLS 설정
- Impressum / Datenschutzerklärung 누락 여부
무료로 nevik.de/guard/에서 실행할 수 있으니, 설정을 확인하고 싶다면 이용해 보세요. 아직도 Google Fonts를 외부에서 로드하는 셀프‑호스팅 서비스가 많아, 독일에서는 €500‑2000 규모의 경고(Abmahnung) 위험이 있다는 점에 놀랐습니다.
2년간 튜닝한 결과
- 가동 시간: 99.94 % (예정된 재부팅 2회)
- RAM 사용량: 5.8 GB / 7.6 GB
- 디스크 사용량: 109 GB / 150 GB
- 월 비용: ~€15 (Hetzner) + €3.50 (스토리지 박스 백업)
- 관리 시간: ~2 시간/월 (업데이트)
- 총 월 비용: €18.50 (SaaS 구독이라면 $200 +/월 수준)
주요 교훈
- Caddy부터 시작 → SSL 인증서 관리에 들어가는 시간을 크게 절감
- Docker 프로파일을 처음부터 사용 → 개별 서비스 테스트가 훨씬 쉬워짐
- Restic을 바로 설정 → Nextcloud 데이터를 한 번 잃어본 경험이 있음
- Uptime Kuma를 초기에 도입 → 2분이면 설정 완료, 디버깅 시간 절감
궁금한 점이 있으면 언제든 질문해 주세요. 전체 스크립트·설정·단계별 가이드를 포함한 더 완전한 패키지도 준비해 두었습니다. 필요하면 DM 주세요, 링크를 공유해 드리겠습니다.