[시놀로지 나스] 올인원 도커 컨테이너 관리 도구 - Dockhand 설치 가이드

Dockhand는 최근 셀프호스팅 커뮤니티에서 Portainer의 강력한 대안으로 떠오르는 Docker 관리 도구다. 직관적인 웹 인터페이스에서 Docker Compose 스택과 컨테이너를 손쉽게 제어할 수 있으며, 기존 Compose 파일을 그대로 연동하여 파일 기반으로 운영할 수 있다.
단순한 배포/관리를 넘어 이미지 자동 업데이트, 취약점 스캔, 실시간 로그 조회, 알림 설정 등 Docker 운영 시 필요한 기능들을 폭넓게 지원한다. 사실상 Portainer(컨테이너 관리) + Dozzle(로그) + Beszel(모니터링) + PruneMate(리소스 정리) 핵심 기능을 하나로 합쳐 놓은 올인원(All-in-One) 솔루션에 가깝다.
비슷한 역할을 하는 Arcane, Dockge, Komodo 등도 도입 후보로 고려했으나, 파편화된 관리 도구들을 대시보드에서 통합 처리할 수 있고, Compose 파일 중심의 운영 방식도 유지할 수 있는 Dockhand를 선택했다.
- Arcane: 깔끔한 UI. 개발 초기 단계라 전반적인 관리 기능은 부족한 편.
- Dockge: Compose 관리에 특화되었으나, 업데이트/스캔/모니터링 기능은 제한적.
- Komodo: 기능 풍부. 단일 Docker 환경을 관리하기에는 다소 무거운 편.
이번 글에서는 시놀로지 NAS에 Dockhand 설치 방법, 기존 Docker 스택 불러오는 방법, 추천 설정까지 단계별로 정리해 봤다.
들어가기에 앞서
- DSM에 Container Manager가 설치되어 있어야 한다(패키지 센터에서 설치 가능).
/volume1/docker/프로젝트명을 Docker 프로젝트의 기본 작업 경로로 사용한다.- 보안을 위해 Docker 소켓을 직접 연결하는 대신, Socket Proxy를 통해 필요한 Docker API만 허용한다.
설치 방법
⓪ NAS SSH 접속 (SSH 접속 가이드는 이전 포스팅 참고)
ssh <DSM 사용자명>@<NAS IP>
# 예: ssh user@192.168.1.2
① 폴더 생성 및 권한 변경
# Dockhand 프로젝트 폴더와 데이터 폴더 생성
sudo mkdir -p /volume1/docker/dockhand/data
# DSM 관리 계정이 프로젝트를 관리하도록 설정
sudo chown -R $USER:users /volume1/docker/dockhand
# 프로젝트 설정과 데이터 접근 제한
sudo chmod 700 /volume1/docker/dockhand
sudo chmod 700 /volume1/docker/dockhand/data
cd /volume1/docker/dockhand
② .env 파일 생성 (아래 명령어 실행)
cat > .env <<EOF
PUID=$(id -u)
PGID=$(id -g)
DOCKER_API_VERSION=$(sudo docker version --format '{{.Server.APIVersion}}')
EOF
③ compose.yaml 파일 생성 (아래 명령어 실행)
cat <<'EOF' > compose.yaml
# 모든 컨테이너에 공통으로 적용할 로그 제한 설정
x-logging: &default-logging
driver: json-file
options:
# 로그 파일 1개의 최대 크기
max-size: "10m"
# 컨테이너당 최대 약 30MB 보관
max-file: "3"
services:
dockhand-socket-proxy:
image: ghcr.io/tecnativa/docker-socket-proxy:latest
container_name: dockhand-socket-proxy
# 직접 정지한 경우를 제외하고 재부팅/오류 상황에서 자동 재시작
restart: unless-stopped
logging: *default-logging
environment:
# Docker 레지스트리 인증 관련 API 허용
AUTH: "1"
# Dockhand 핵심 기능에 필요한 Docker API만 허용
CONTAINERS: "1"
IMAGES: "1"
NETWORKS: "1"
VOLUMES: "1"
EVENTS: "1" # 컨테이너 시작·종료 등 Docker 이벤트 수신
POST: "1" # Docker API 쓰기 요청 허용(생성·수정·시작·정지 등)
# 대시보드 호스트 정보 및 디스크 사용량 조회
INFO: "1"
SYSTEM: "1"
# 컨테이너 시작·정지·재시작 권한
ALLOW_START: "1"
ALLOW_STOP: "1"
ALLOW_RESTARTS: "1"
# Dockhand 웹 터미널 사용 시 활성화
# 사용하지 않는다면 "0" 권장
EXEC: "0"
volumes:
# 실제 Docker 소켓은 proxy에만 읽기 전용으로 전달
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- socket-proxy
labels:
# proxy 자신을 업데이트하면 Dockhand 연결이 끊기므로 자동 업데이트 금지
dockhand.update: "false"
# 내부 proxy의 시작·정지 이벤트는 알림 대상에서 제외
dockhand.notify: "false"
security_opt:
# 컨테이너 내부 프로세스가 실행 중 추가 권한을 획득하지 못하도록 제한
- no-new-privileges:true
dockhand:
image: fnsys/dockhand:latest
container_name: dockhand
restart: unless-stopped
logging: *default-logging
depends_on:
- dockhand-socket-proxy
environment:
# Dockhand는 시작 시 권한을 정리한 뒤 지정 사용자로 권한을 낮춰 실행
PUID: "${PUID}"
PGID: "${PGID}"
# Dockhand 설정, SQLite DB, 내부 스택 파일 등 저장 경로
# 기본값은 컨테이너 내부의 /app/data
DATA_DIR: "/volume1/docker/dockhand/data"
# Docker 소켓에 직접 접근하지 않고 proxy 경유
DOCKER_HOST: "tcp://dockhand-socket-proxy:2375"
# 시놀로지 구형 Docker Engine과 Compose CLI 호환성 확보
DOCKER_API_VERSION: "${DOCKER_API_VERSION}"
# 대시보드가 지나치게 느릴 때만 활성화
# SKIP_DF_COLLECTION: "true"
ports:
# NAS IP의 3000번 포트로 Dockhand 웹 UI 접속 허용
- "3000:3000"
volumes:
# Dockhand가 상대 경로를 사용하는 Compose 스택을 처리할 수 있도록
# NAS의 /volume1/docker 폴더를 컨테이너 내부에도 동일한 경로로 연결
- /volume1/docker:/volume1/docker
networks:
- socket-proxy
labels:
# Dockhand 자체는 예약 자동 업데이트 대상에서 제외
dockhand.update: "false"
security_opt:
- no-new-privileges:true
networks:
socket-proxy:
# Docker 소켓 proxy 통신 전용 네트워크
# Trivy 취약점 DB 다운로드를 위해 외부 통신은 허용
driver: bridge
EOF
④ 파일 권한 변경
# 현재 사용자만 읽기/수정 가능
sudo chmod 600 \
/volume1/docker/dockhand/.env \
/volume1/docker/dockhand/compose.yaml
⑤ 설정 검증 및 실행
cd /volume1/docker/dockhand
# YAML 문법과 환경 변수 치환 결과 확인
sudo docker compose config
# 이미지 다운로드
sudo docker compose pull
# 컨테이너 실행
sudo docker compose up -d
# 실행 상태 확인
sudo docker compose ps
# 초기 로그 확인
sudo docker compose logs --tail=100 dockhand
sudo docker compose logs --tail=100 dockhand-socket-proxy
💡 Dockhand는 실시간 업데이트와 터미널 기능에 WebSocket을 사용한다. 리버스 프록시를 통해 접속하는 경우, DSM 역방향 프록시 규칙 > 사용자 지정 머리글(헤더) > 생성 버튼을 클릭해서 WebSocket 관련 헤더를 추가해야 한다.

주요 설정
계정
Dockhand 접속 > Settings > Authentication에서 계정을 설정한다. Dockhand 무료 버전에선 로그인 가능한 모든 계정은 사실상 관리자 권한을 가진다. 권한을 세부적으로 나누려면 Enterprise 버전이 필요하다.

- Add user 버튼 > 유저 생성
- Authentication 옵션 ON 설정
- 다시 로그인
왼쪽 하단 프로필 클릭 > Enable MFA 버튼을 눌러서 2단계 인증을 활성화할 수 있다.

Environments
도커 정보를 불러오려면 Dockhand가 Docker Engine에 접근할 수 있도록 Environment를 추가해야 한다. 보안을 위해 Docker 소켓을 직접 마운트하지 않고 앞서 생성한 dockhand-socket-proxy를 통해 연결한다.

- Name: 환경 이름 (예:
Synology DS218+) - Connection type:
Direct connection - Host:
dockhand-socket-proxy - Port:
2375 - Protocol:
HTTP - Public IP: NAS 내부 IP (예:
192.168.1.2)
Compose 스택 불러오기
Docker Compose로 이미 실행 중인 기존 스택은 Untracked(미추적) 상태로 표시된다. Adopt 기능으로 기존 Compose 파일을 연결해 주면 Dockhand에서 수정/배포할 수 있는 Internal 스택으로 전환할 수 있다.

- 왼쪽 메뉴 > Stacks
- 우측 상단 Adopt 버튼 클릭
- Docker 프로젝트가 포함된 상위 폴더에서 Scan this folder 클릭
- 가져올 스택 선택 후 Adopt stack(s) 버튼 클릭
취약점 자동 분석
Settings > Environments > 연필 아이콘(Edit Environment) > Security에서 취약점 자동 스캔을 설정할 수 있다. 스캐너는 Trivy, Grype 두 종류를 지원한다. Trivy는 속도/범용성이 좋고, Grype는 좀 더 꼼꼼하게 탐지하지만 상대적으로 느린 편이다. 스캐너마다 사용하는 취약점 DB와 매칭 방식이 달라서 결과가 다를 수 있다. 개인 홈서버는 Trivy 하나만 켜 놔도 무난하다.

- Enable scanning:
ON
컨테이너 이미지 취약점 스캔 활성화. - Scanner:
Trivy
사용할 취약점 스캐너 선택(Trivy, Grype 둘 다 선택 가능).
스캐너 버전은 공급망 공격* 방지를 위해 기본적으로 고정되어 있다. Settings > General > Vulnerability scanners 섹션에서 스캐너 버전을 변경할 수 있다. 버전은 각 스캐너의 Docker Hub(Trivy, Grype) 태그 페이지를 확인한 후 입력하면 된다.
공급망 공격: 프로그램이 의존하는 업데이트/라이브러리/이미지/배포 경로를 오염시켜 공격하는 방식
예를 들어 시놀로지 NAS DS218+ 모델은 Intel 계열 CPU이므로 선택한 태그의 OS/ARCH 항목에 linux/amd64가 포함되어 있어야 한다. 이미지는 이미지명:태그 형식으로 입력한다. digest를 사용하면 해시값 기준으로 특정 이미지 버전을 더 엄격하게 고정할 수 있다. digest는 이미지명@sha256:... 형식으로 입력한다.

컨테이너 자동 업데이트
Settings > Environments > 연필 아이콘(Edit Environment) > Updates에서 전체 컨테이너의 업데이트 확인 주기, 자동 업데이트 여부, 취약점 기준, 불필요한 이미지 정리 정책 등을 설정할 수 있다.

- Enable scheduled update check:
ON
컨테이너 이미지 업데이트 여부 정기적으로 확인. - Schedule:
Daily / 05:30(새벽 시간대 추천)
업데이트 확인 시점 설정(Daily, Weekly, Cron 표현식 지원). - Automatically update containers:
ON
새 이미지 발견 시 컨테이너 자동 업데이트/재배포. - Vulnerability criteria:
Critical only
취약점 검사 결과에 따른 업데이트 허용/차단 기준.

- Enable automatic image pruning:
ON
사용하지 않는 이미지 자동 정리. - Prune mode: Dangling images Only:
Weekly / 06:00 / Sunday
태그가 없고(<none>) 더 이상 참조가 없는 이미지만 정리. - Timezone:
Asia/Seoul
스케줄 실행 기준 시간대.
전체 컨테이너를 대상으로 자동 업데이트하는 대신, 각 컨테이너의 편집(Edit) 화면에서 개별적으로 적용할 수도 있다. 중요한 서비스는 컨테이너별로 업데이트 여부를 설정하는 편이 안전하다.

General 설정
Settings > General에서 표시 방식, 언어/지역 등 기본 환경을 설정할 수 있다. 터미널/에디터 폰트는 가독성이 좋은 JetBrains Mono를 추천한다. JetBrains Mono는 일반적인 고정폭 폰트보다 소문자 높이(x-height)가 더 크게 설계되어 작은 크기에서도 선명하게 보이는 장점이 있다.
Appearance

- Show stopped containers:
ON
중지 컨테이너 표시 여부. - Highlight available updates:
ON
업데이트 항목 강조 여부. - Compact port display:
ON
포트 정보를 간결하게 표시할지 여부. - Show exposed ports:
OFF
exposed 포트 표시 여부. published 포트와 혼동을 방지하기 위해 OFF로 설정. - Time format:
24h
시간 표시 형식. 24h로 설정하면 오전/오후 없이 24시간 형식으로 표시된다. - Date format:
YYYY-MM-DD
날짜 표시 형식.
Logs & files

- Log buffer size:
2,000 lines
로그 화면에 유지할 최대 줄 수. 컨테이너 로그는 시간이 지날수록 빠르게 쌓인다. 일반적인 상태 확인/오류 추적 용도라면 2,000줄 정도가 적당하다. - Format log timestamps:
ON
로그 타임스탬프를 읽기 쉬운 형식으로 변환할지 여부. ON으로 설정하면 타임스탬프를 사람이 읽기 편한 형식으로 변환한다. 예:2026-06-15T01:25:56Z→2026-06-15 10:25:56 - Download format:
tar.gz
다운로드 시 사용할 압축 형식.tar는 압축하지 않고 파일/폴더를 하나로 묶는 방식. 처리 속도가 빠르고 구조 보존에 유리하지만 파일 크기는 거의 줄어들지 않는다.tar.gz는tar로 묶은 뒤 gzip으로 압축한 형식으로 용량을 줄일 수 있어 보관/이동에 유리하다.
System jobs
메트릭 수집 방식/주기, 각종 정리 스케줄을 설정하는 섹션. 정리 작업은 03:00, 03:30, 04:00처럼 사용량이 적은 새벽 시간대에 시간차를 두고 실행하는게 좋다.

- Activity event collection mode:
Stream
활동 이벤트 수집 방식.Stream은 실시간,Poll은 일정 주기로 이벤트를 조회한다.Stream방식은 리소스를 조금 더 사용하지만 컨테이너 상태 변화를 빠르게 확인할 수 있다. - Metrics collection interval:
60s
CPU, 메모리, 네트워크 등 컨테이너 메트릭 수집 주기. 주기가 짧을수록 상태를 정밀하게 파악할 수 있지만 모니터링 시스템 부하가 커지므로 기본값인 60초 정도가 적당하다. - Schedule execution cleanup:
ON|30 days / Daily / 03:00
예약 작업 실행 기록 정리 설정. 보관 기간이 지난 실행 기록을 자동으로 정리한다. - Container event cleanup:
ON|30 days / Daily / 03:30
컨테이너 이벤트 기록 정리 설정. 보관 기간이 지난 이벤트 기록을 자동으로 정리한다. - Scanner cache cleanup:
ON|Weekly / 04:00 / Sunday
스캐너 캐시 정리 설정. 오래된 이미지 취약점 스캐너 캐시를 자동으로 정리한다.
텔레그램 알림
Dockhand는 이메일/웹훅 알림 기능도 제공한다(Settings > Notifications). 이메일 알림은 별도의 메일 서버 구성이 필요하므로, 웹훅을 통해 텔레그램(Telegram)으로 알림을 받는 방식이 가장 간편하다.

Apprise는 통일된 URL 규칙으로 다양한 알림 서비스를 연동할 수 있는 라이브러리다. 각 알림 서비스는 저마다 다른 API 스펙을 갖고 있기 때문에 연동 작업에 번거로움이 따른다. Apprise를 사용하면 service://credential/target 형태의 단일 URL 규칙으로 140개 이상의 서비스를 일관되게 연동할 수 있다.
웹훅 알림은 아래와 같은 Apprise URL 형식으로 입력해야 한다. 텔레그램 봇 토큰은 @BotFather에서 봇 생성 후 확인할 수 있고(기존 봇 사용 가능), Chat ID는 @userinfobot 봇에서 확인할 수 있다.
형식: tgram://<Bot Token>/<Chat ID>
예시: tgram://123456789:ABCDEF_xxxxxxxxxxxxxxxxx/123456789
텔레그램 외에도 슬랙(Slack), 디스코드(Discord), Pushbullet, Gotify 등 다양한 앱으로 알림을 보낼 수 있다. 알림 항목 세부 설정은 등록한 Environment 편집 화면 > Notification 탭에서 할 수 있다.
번외: Dockhand 볼륨 매핑 이해하기
DSM Container Manager는 NAS 호스트에 저장된 프로젝트를 직접 관리하므로 Compose 파일의 상대 경로를 별다른 설정 없이 처리할 수 있다. 반면 Dockhand는 컨테이너 내부에서 격리되어 실행된다. NAS에 저장된 기존 Compose 스택을 Dockhand로 불러와서 관리하려면, NAS의 프로젝트 폴더를 Dockhand 컨테이너 내부에서도 읽을 수 있도록 연결(볼륨 매핑)해야 한다.
이때 NAS 호스트와 Dockhand 컨테이너 내부의 경로를 동일하게 맞추는 것이 가장 안전하다.
volumes:
# NAS 실제 경로 : Dockhand 컨테이너 내부 경로
# NAS의 /volume1/docker 폴더가 Dockhand 내부에서도 같은 경로로 보이게 한다.
- /volume1/docker:/volume1/docker
위처럼 NAS의 실제 폴더를 매핑하면 Dockhand 컨테이너 내부에서도 동일한 경로를 통해 접근할 수 있다. 얼핏 보면 복사본 같지만 NAS의 원본 폴더를 Dockhand 내부에서 직접 보고 있는 상태다.

경로를 동일하게 설정하는 이유
NAS에 다음과 같은 n8n 프로젝트가 있다고 가정해 보자.
/volume1/docker/n8n/
├── compose.yml
└── data/
compose.yml 파일에는 보통 아래와 같이 상대 경로를 지정한다.
volumes:
# 호스트 데이터 폴더 : n8n 컨테이너 내부 경로
- ./data:/home/node/.n8n
Dockhand는 현재 관리 중인 스택의 compose.yml 파일이 있는 폴더(작업 디렉터리)를 기준으로 ./data 경로를 계산한다. NAS 폴더와 Dockhand 컨테이너 내부 경로를 동일하게 연결했다면, Dockhand가 인식하는 작업 디렉터리는 NAS의 실제 폴더 경로를 그대로 가리킨다.
- Dockhand가 인식한
compose.yml위치:/volume1/docker/n8n/compose.yml - Dockhand가 계산한
./data실제 경로:/volume1/docker/n8n/data
Dockhand는 계산된 /volume1/docker/n8n/data 경로를 Docker Engine에 전달한다. Docker Engine은 NAS 호스트에서 해당 폴더를 찾아 n8n 컨테이너 내부의 /home/node/.n8n 경로에 연결한다. 이를 bind mount라고 부른다.
경로를 다르게 지정했을 때 문제
반대로 NAS 호스트 경로와 Dockhand 컨테이너 내부 경로를 다르게 지정했다고 가정해 보자.
volumes:
# NAS Docker 프로젝트 폴더 : Dockhand 컨테이너 내부 경로
# NAS의 /volume1/docker 폴더가 Dockhand 내부에서는 /stacks로 보인다.
# 따라서 n8n 프로젝트의 작업 디렉터리도 /stacks/n8n으로 인식된다.
- /volume1/docker:/stacks
이 경우 NAS의 /volume1/docker 폴더는 Dockhand 컨테이너 내부에서 /stacks라는 경로로 인식한다. 이 상태에서 스택을 불러오면 Dockhand는 다음과 같이 경로를 계산한다.
- Dockhand가 인식한
compose.yml위치:/stacks/n8n/compose.yml - Dockhand가 계산한
./data실제 경로:/stacks/n8n/data
실제로 볼륨을 연결하는 주체는 Dockhand 컨테이너가 아니라 NAS 호스트의 Docker Engine이다. Docker Engine은 Dockhand가 전달한 /stacks/n8n/data 경로를 NAS 호스트 기준으로 찾으려고 시도한다.
그러나 NAS 호스트에서 실제 데이터는 /volume1/docker/n8n/data에 있다. 결과적으로 Docker Engine이 호스트에서 해당 경로를 찾지 못해 컨테이너 실행 오류가 발생하거나 엉뚱한 위치에 빈 폴더가 생성된다.
기존 Compose 파일의 상대 경로를 수정하지 않고 안정적으로 관리하려면, NAS 호스트와 Dockhand 컨테이너 내부의 프로젝트 경로를 동일하게 맞추는 편이 가장 단순하다.
바인드 마운트 vs 네임드 볼륨
이처럼 호스트의 실제 폴더 경로를 직접 지정하여 컨테이너 내부 경로와 연결하는 방식을 바인드 마운트(Bind Mount)라고 부른다. 반대로 호스트의 실제 저장 경로를 직접 지정하지 않고 Docker에 저장 공간 관리를 맡기는 방식을 네임드 볼륨(Named Volume)이라고 부른다.
# 바인드 마운트 방식
volumes:
# NAS 실제 폴더 : 컨테이너 내부 경로
- /volume1/docker/n8n/data:/home/node/.n8n
# 네임드 볼륨 방식
services:
n8n:
volumes:
# Docker가 관리하는 볼륨 이름 : 컨테이너 내부 경로
- n8n-data:/home/node/.n8n
volumes:
n8n-data:
네임드 볼륨은 설정이 간편하지만 Docker 전용 경로에 저장되므로, 파일을 직접 확인하거나 수정하고 백업하기에는 상대적으로 불편하다. 이러한 이유로 시놀로지 NAS에서는 폴더 구조를 명확하게 관리할 수 있는 바인드 마운트를 선호하는 경우가 많다.
'⌚️ Productivity' 카테고리의 다른 글
| [시놀로지 나스] Cloudflare Tunnel QUIC UDP 버퍼 경고 해결방법 (0) | 2026.06.12 |
|---|---|
| [시놀로지 나스] Split DNS 구성하기 - 로컬에서 Tailscale 없이 도메인 접속 (0) | 2026.06.10 |
| [시놀로지 나스] Tailscale 전용 NAS에서 파일/폴더 외부 공유하기 (0) | 2026.06.04 |
| [시놀로지 나스] n8n 셀프 호스팅 가이드 - PostgreSQL + Cloudflare Tunnel (0) | 2026.05.30 |
| [macOS] 예쁘고 직관적인 라디얼 스타일 앱 전환기 - Orbit (0) | 2026.04.26 |
댓글
이 글 공유하기
다른 글
-
[시놀로지 나스] Cloudflare Tunnel QUIC UDP 버퍼 경고 해결방법
[시놀로지 나스] Cloudflare Tunnel QUIC UDP 버퍼 경고 해결방법
2026.06.12 -
[시놀로지 나스] Split DNS 구성하기 - 로컬에서 Tailscale 없이 도메인 접속
[시놀로지 나스] Split DNS 구성하기 - 로컬에서 Tailscale 없이 도메인 접속
2026.06.10 -
[시놀로지 나스] Tailscale 전용 NAS에서 파일/폴더 외부 공유하기
[시놀로지 나스] Tailscale 전용 NAS에서 파일/폴더 외부 공유하기
2026.06.04 -
[시놀로지 나스] n8n 셀프 호스팅 가이드 - PostgreSQL + Cloudflare Tunnel
[시놀로지 나스] n8n 셀프 호스팅 가이드 - PostgreSQL + Cloudflare Tunnel
2026.05.30