본문으로 건너뛰기
UPS와 NUT을 이용한 홈서버 자동 종료 시스템

# 홈서버 정전 대비 완벽 가이드: NUT으로 UPS 하나에 여러 장비 자동 종료하기

Table of Contents

“RAID는 백업이 아니다”라는 말은 많이 들어봤을 겁니다. 그런데 “UPS가 있어도 제대로 설정 안 하면 의미 없다”는 건 알고 계셨나요?

UPS를 사서 홈서버에 연결해두면 끝이라고 생각하기 쉽습니다. 하지만 실제로 정전이 발생하면, USB로 직접 연결된 장비 하나만 종료되고 나머지 서버들은 배터리가 바닥날 때까지 돌다가 강제 종료됩니다. NAS에서 파일 쓰기 중이었다면? ZFS 풀이 스크럽 중이었다면? 운이 나쁘면 데이터가 날아갑니다.

Network UPS Tools(NUT)은 이 문제를 해결합니다. UPS 하나에 USB로 연결된 “마스터” 장비가 정전 상태를 네트워크로 브로드캐스트하면, “슬레이브” 장비들이 순차적으로 안전하게 종료됩니다.

왜 NUT이 필요한가

일반적인 UPS 사용 시나리오를 생각해봅시다.

┌─────────────────────────────────────────────────────────────┐
│                    일반적인 UPS 연결                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   UPS ──USB──→ 시놀로지 NAS (자동 종료 O)                    │
│    │                                                        │
│    ├─전원──→ Docker 서버 (강제 종료 X)                       │
│    │                                                        │
│    ├─전원──→ Proxmox 호스트 (강제 종료 X)                    │
│    │                                                        │
│    └─전원──→ 공유기 (강제 종료 X)                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

USB 케이블은 하나입니다. 하지만 전원은 여러 장비에 공급됩니다. 정전이 되면 USB로 연결된 장비만 “지금 배터리로 돌아가고 있어”라는 사실을 알 수 있습니다. 나머지 장비들은 아무것도 모른 채 동작하다가 배터리가 떨어지면 그대로 꺼집니다.

NUT은 이 구조를 바꿉니다.

┌─────────────────────────────────────────────────────────────┐
│                    NUT을 사용한 UPS 연결                     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   UPS ──USB──→ NUT 서버 (Master)                            │
│    │              │                                         │
│    │              ├─TCP 3493──→ 시놀로지 (Slave) → 종료     │
│    │              │                                         │
│    ├─전원         ├─TCP 3493──→ Docker 호스트 (Slave) → 종료│
│    │              │                                         │
│    └─전원         └─TCP 3493──→ Proxmox (Slave) → 종료      │
│                                                             │
│   * 모든 장비가 정전 상태를 인지하고 순차 종료               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

마스터가 정전을 감지하면 슬레이브들에게 알리고, 배터리가 임계치 이하로 떨어지면 모든 장비가 안전하게 종료됩니다.

구성 요소 이해하기

NUT은 세 가지 핵심 컴포넌트로 구성됩니다.

1. Driver (nut-driver)

UPS와 직접 통신하는 레이어입니다. USB HID, SNMP, 시리얼 등 다양한 프로토콜을 지원합니다. 대부분의 가정용 UPS는 usbhid-ups 드라이버를 사용합니다.

2. Server (upsd)

UPS 상태 정보를 네트워크로 제공하는 데몬입니다. 기본 포트는 3493입니다. 클라이언트들이 이 서버에 연결해서 배터리 잔량, 충전 상태, 부하량 등을 조회합니다.

3. Monitor (upsmon)

UPS 상태를 모니터링하고 종료 명령을 실행하는 클라이언트입니다. 마스터 모드와 슬레이브 모드가 있습니다.

  • Master: UPS에 직접 연결된 장비. 모든 슬레이브가 종료된 후 마지막에 종료하고 UPS에 전원 차단 명령을 보냅니다.
  • Slave: 네트워크로 UPS 상태를 받아오는 장비. 배터리가 임계치 이하면 먼저 종료됩니다.

Docker로 NUT 서버 구축하기

가장 깔끔한 방법은 Docker로 NUT 서버를 운영하는 것입니다. 라즈베리파이나 항상 켜져 있는 저전력 장비에서 실행하면 됩니다.

사전 준비

  1. UPS를 USB로 연결할 장비 (라즈베리파이, 미니PC 등)
  2. Docker와 Docker Compose 설치
  3. UPS 모델 확인 (대부분 usbhid-ups 드라이버 사용)

UPS 연결 확인

먼저 UPS가 시스템에서 인식되는지 확인합니다.

# USB 장치 목록에서 UPS 찾기
lsusb | grep -i ups

# 또는 제조사 이름으로 검색
lsusb | grep -iE "(apc|cyberpower|eaton|tripplite)"

출력 예시:

Bus 001 Device 003: ID 051d:0002 American Power Conversion Uninterruptible Power Supply

이 정보에서 Vendor ID(051d)와 Product ID(0002)를 기억해둡니다.

Docker Compose 설정

# docker-compose.yml
version: '3.8'

services:
  nut-upsd:
    image: instantlinux/nut-upsd:latest
    container_name: nut-upsd
    restart: unless-stopped
    ports:
      - "3493:3493"
    environment:
      - TZ=Asia/Seoul
      - API_USER=upsmon
      - API_PASSWORD=your_secure_password_here  # 반드시 변경하세요
      - DRIVER=usbhid-ups
      - GROUP=nut
      - NAME=ups
      - POLLINTERVAL=15
      - PORT=auto
      - SERIAL=
      - SERVER=master
      - VENDORID=051d  # lsusb에서 확인한 값
      - DESCRIPTION=APC Back-UPS 750
    devices:
      - /dev/bus/usb:/dev/bus/usb
    privileged: true  # USB 접근에 필요
    volumes:
      - nut-config:/etc/nut
    healthcheck:
      test: ["CMD", "upsc", "ups@localhost"]
      interval: 30s
      timeout: 10s
      retries: 3

volumes:
  nut-config:

중요한 환경 변수들:

변수설명
API_USER클라이언트 인증 사용자명
API_PASSWORD클라이언트 인증 비밀번호
DRIVERUPS 드라이버 (대부분 usbhid-ups)
NAMEUPS 이름 (시놀로지는 ups 고정 필요)
VENDORIDUSB Vendor ID (선택사항, 자동 감지 가능)
SERVERmaster 또는 slave

컨테이너 실행 및 확인

# 컨테이너 시작
docker compose up -d

# 로그 확인
docker logs -f nut-upsd

# UPS 상태 확인
docker exec nut-upsd upsc ups@localhost

정상적으로 연결되었다면 다음과 같은 출력이 나옵니다:

battery.charge: 100
battery.charge.low: 10
battery.charge.warning: 50
battery.runtime: 1800
battery.voltage: 27.1
device.mfr: American Power Conversion
device.model: Back-UPS RS 750G
device.type: ups
driver.name: usbhid-ups
input.voltage: 230.0
output.voltage: 230.0
ups.load: 15
ups.status: OL
ups.test.result: No test initiated

ups.status: OL은 Online(전원 정상)을 의미합니다. 전원이 끊기면 OB(On Battery)로 바뀝니다.

슬레이브 장비 설정하기

시놀로지 NAS

시놀로지는 NUT 클라이언트가 기본 내장되어 있습니다. 하지만 중요한 제약이 있습니다: UPS 이름이 반드시 ups여야 합니다. 다른 이름은 인식하지 못합니다.

  1. 제어판 → 하드웨어 및 전원 → UPS 이동
  2. 네트워크 UPS 서버 선택
  3. 설정 입력:
    • 네트워크 UPS 서버 IP: NUT 서버 IP (예: 192.168.1.100)
    • UPS가 대기모드로 전환되기 전 시간: 180초 (3분)
  4. 적용

시놀로지는 NUT 서버에 인증 없이 연결합니다. 보안이 걱정된다면 NUT 서버에서 시놀로지 IP만 허용하도록 설정할 수 있습니다.

Unraid

Unraid는 NUT 플러그인을 제공합니다.

  1. AppsNUT 검색 → NUT - Network UPS Tools 설치
  2. Settings → UPS Settings 이동
  3. 설정:
    • UPS Mode: Slave
    • IP Address: NUT 서버 IP
    • Port: 3493
    • UPS Name: ups
    • User: upsmon
    • Password: 설정한 비밀번호
  4. ApplyStart

Docker 호스트 (우분투/데비안)

Docker가 아닌 호스트 시스템에서 직접 NUT 클라이언트를 실행해야 합니다.

# NUT 클라이언트 설치
sudo apt update
sudo apt install -y nut-client

# upsmon 설정
sudo nano /etc/nut/upsmon.conf

/etc/nut/upsmon.conf 내용:

# NUT 서버 연결 설정
# 형식: MONITOR <ups>@<host> <powervalue> <username> <password> <type>
MONITOR ups@192.168.1.100 1 upsmon your_secure_password slave

# 종료 명령
SHUTDOWNCMD "/sbin/shutdown -h +0"

# 종료 전 실행할 명령 (선택사항)
NOTIFYCMD /usr/sbin/upssched

# 알림 설정
NOTIFYFLAG ONLINE SYSLOG
NOTIFYFLAG ONBATT SYSLOG+WALL
NOTIFYFLAG LOWBATT SYSLOG+WALL
NOTIFYFLAG FSD SYSLOG+WALL
NOTIFYFLAG SHUTDOWN SYSLOG+WALL

# 폴링 간격 (초)
POLLFREQ 5
POLLFREQALERT 5

# 배터리 부족 시 종료까지 대기 시간
FINALDELAY 5

NUT 모드 설정:

sudo nano /etc/nut/nut.conf
MODE=netclient

서비스 시작:

sudo systemctl enable nut-client
sudo systemctl start nut-client

# 상태 확인
upsc ups@192.168.1.100

Proxmox VE

Proxmox도 내부적으로 Debian이므로 같은 방식으로 설정합니다.

# 쉘에서 실행
apt update
apt install -y nut-client

# 설정 파일은 위 우분투와 동일

다만 Proxmox에서는 VM들의 안전한 종료도 고려해야 합니다. /etc/nut/upssched.conf를 사용해서 정전 시 VM들을 먼저 종료시키는 스크립트를 실행할 수 있습니다.

고급 설정: 알림 및 자동화

Discord/Slack 알림

정전 발생 시 알림을 받으려면 upssched를 설정합니다.

/etc/nut/upssched.conf:

CMDSCRIPT /usr/local/bin/ups-notify.sh
PIPEFN /var/run/nut/upssched/upssched.pipe
LOCKFN /var/run/nut/upssched/upssched.lock

# 배터리 모드 전환 시 즉시 알림
AT ONBATT * EXECUTE onbatt
AT ONLINE * EXECUTE online
AT LOWBATT * EXECUTE lowbatt
AT FSD * EXECUTE fsd

# 30초 이상 배터리 모드면 추가 알림
AT ONBATT * START-TIMER onbatt-warning 30
AT ONLINE * CANCEL-TIMER onbatt-warning

/usr/local/bin/ups-notify.sh:

#!/bin/bash

WEBHOOK_URL="https://discord.com/api/webhooks/your-webhook-url"

case $1 in
    onbatt)
        MSG="⚡ 정전 발생! UPS 배터리 모드로 전환되었습니다."
        ;;
    online)
        MSG="✅ 전원 복구! 정상 운영 중입니다."
        ;;
    lowbatt)
        MSG="🔴 배터리 부족! 곧 시스템이 종료됩니다."
        ;;
    fsd)
        MSG="🛑 강제 종료 시작. 모든 시스템을 종료합니다."
        ;;
    onbatt-warning)
        MSG="⚠️ 30초 이상 배터리 모드 지속 중..."
        ;;
esac

curl -H "Content-Type: application/json" \
     -d "{\"content\": \"$MSG\"}" \
     "$WEBHOOK_URL"

Docker 컨테이너 안전 종료

Docker 호스트가 종료되기 전에 컨테이너들을 graceful하게 종료시키려면 종료 스크립트를 수정합니다.

/etc/nut/upssched-cmd:

#!/bin/bash

case $1 in
    onbatt)
        logger -t upssched "UPS on battery"
        # Discord 알림
        /usr/local/bin/ups-notify.sh onbatt
        ;;
    lowbatt|fsd)
        logger -t upssched "Low battery - initiating shutdown"
        # 컨테이너 종료 (30초 타임아웃)
        docker stop -t 30 $(docker ps -q) 2>/dev/null
        # 시스템 종료
        /sbin/shutdown -h now "UPS battery critical"
        ;;
esac

테스트: 실제 정전 시뮬레이션

설정이 완료되었다면 반드시 테스트해야 합니다. 테스트 없는 백업은 백업이 아닙니다.

1. 연결 테스트

각 슬레이브에서 NUT 서버에 연결되는지 확인합니다.

# 모든 슬레이브에서 실행
upsc ups@<NUT서버IP>

2. 정전 시뮬레이션

방법 A: UPS 전원 케이블 분리

가장 확실한 방법입니다. 하지만 위험하므로 중요한 작업이 없을 때 진행하세요.

  1. 모든 장비가 NUT 서버에 연결되어 있는지 확인
  2. UPS의 전원 케이블을 콘센트에서 분리
  3. ups.statusOB(On Battery)로 바뀌는지 확인
  4. 슬레이브들이 정전을 감지하는지 로그 확인
  5. 배터리가 battery.charge.low 이하로 떨어지기 전에 전원 복구

방법 B: 소프트웨어 테스트

실제 종료 없이 이벤트만 테스트합니다.

# NUT 서버에서 실행
upsmon -c fsd

이 명령은 “Forced ShutDown” 이벤트를 발생시킵니다. 하지만 실제로 장비들이 종료되므로 주의하세요.

3. 종료 순서 확인

정상적인 종료 순서:

  1. 정전 발생 → ONBATT 이벤트
  2. 배터리 임계치 도달 → LOWBATT 이벤트
  3. 마스터가 FSD 브로드캐스트
  4. 슬레이브들 순차 종료
  5. 마스터 종료
  6. 마스터가 UPS에 전원 차단 명령 전송

주의사항과 팁

공유기도 UPS에 연결하세요

NUT은 네트워크 기반입니다. 정전 시 공유기가 먼저 꺼지면 슬레이브들은 정전 사실조차 모릅니다. 공유기도 반드시 UPS에 연결하고, 가능하면 공유기보다 다른 장비들이 먼저 종료되도록 설정하세요.

시놀로지 UPS 이름 제약

시놀로지 DSM은 NUT 서버의 UPS 이름이 반드시 ups여야 합니다. 이 이름은 하드코딩되어 있어서 변경할 수 없습니다. NUT 서버 설정에서 NAME=ups로 지정해야 시놀로지가 인식합니다.

배터리 교체 주기

UPS 배터리는 보통 3-5년 수명입니다. NUT에서 배터리 상태를 주기적으로 모니터링하고, battery.charge가 최대치에서 급격히 떨어지면 교체를 고려하세요.

# 배터리 상태 확인
upsc ups@localhost battery.charge
upsc ups@localhost battery.runtime

적절한 종료 대기 시간

배터리 용량, 연결된 장비의 소비 전력, 종료에 필요한 시간을 고려해서 battery.charge.low 값을 설정하세요.

예를 들어:

  • 배터리 런타임이 10분
  • 모든 장비 종료에 3분 필요
  • 여유 시간 2분

이 경우 배터리 잔량 50% (5분) 정도에서 종료를 시작하는 게 안전합니다.

실제 구성 예시

제가 운영 중인 홈랩 구성입니다.

APC Back-UPS Pro 1500

     ├─USB─→ 라즈베리파이 4 (NUT Master)
     │           └─ Docker: nut-upsd

     ├─전원─→ 시놀로지 DS920+ (NUT Slave)
     │           └─ 사진, 문서 백업

     ├─전원─→ 미니PC (NUT Slave)
     │           └─ Proxmox VE + Docker

     ├─전원─→ 공유기 (UniFi UDM)

     └─전원─→ 스위치 (8포트 PoE)

정전 시 동작 순서:

  1. 라즈베리파이가 정전 감지 후 네트워크로 브로드캐스트
  2. 1분 후에도 전원 미복구 시 Discord 알림 발송
  3. 배터리 30% 도달 시 종료 프로세스 시작
  4. 시놀로지 → Proxmox VM들 → Proxmox 호스트 → 라즈베리파이 순서로 종료
  5. 라즈베리파이가 UPS에 전원 차단 명령

이 구성으로 2년간 운영하면서 3번의 정전이 있었고, 모두 데이터 손실 없이 안전하게 종료되었습니다.

마무리

홈서버 운영에서 UPS는 필수입니다. 하지만 UPS만으로는 충분하지 않습니다. NUT을 설정해서 모든 장비가 정전을 인지하고 안전하게 종료될 수 있도록 해야 진정한 보호가 됩니다.

설정이 조금 복잡해 보일 수 있지만, 한 번 구축해두면 수년간 신경 쓸 일이 없습니다. 그리고 실제로 정전이 발생했을 때, 데이터가 무사한 것을 확인하는 순간 이 모든 노력이 가치 있었다는 걸 알게 됩니다.

다음에 정전이 와도, 당신의 데이터는 안전할 겁니다.

이 글 공유하기:
My avatar

글을 마치며

이 글이 도움이 되었기를 바랍니다. 궁금한 점이나 의견이 있다면 댓글로 남겨주세요.

더 많은 기술 인사이트와 개발 경험을 공유하고 있으니, 다른 포스트도 확인해보세요.

유럽살며 여행하며 코딩하는 노마드의 여정을 함께 나누며, 함께 성장하는 개발자 커뮤니티를 만들어가요! 🚀


관련 포스트