내가 Hetzner에서 쓰는 Docker Compose와 하드닝 스크립트를 포함한 완전 셀프‑호스팅 스택 (전부 공개)

발행: (2026년 6월 11일 PM 07:41 GMT+9)
6 분 소요
원문: Dev.to

출처: 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 주세요, 링크를 공유해 드리겠습니다.

0 조회
Back to Blog

관련 글

더 보기 »