n8n 성능 최적화: 시놀로지 NAS에서 워커 노드 포함하여 구축하기
시놀로지 NAS의 Docker 환경에서 n8n과 워커(Worker)를 함께 설치하여, 무거운 워크플로우도 안정적으로 처리할 수 있는 자동화 시스템 구축 방법을 단계별로 소개합니다.
Synology Docker install n8n with worker
- 서론(intro)
- 주제: 시놀로지 NAS에 Docker Compose로 MinIO(S3 저장소)와 n8n(자동화 툴)을 구축하며 겪은 시행착오 기록.
- 핵심:
1. Nginx Proxy Manager(NPM) 세팅
2. n8n 구축 (Portainer이용)
- 목표: 안정적인 Self-hosted 환경 구축을 위한 docker-compose.yml 및 NPM 설정값 공유.
- 환경 구성 (Prerequisites)
- H/W: Synology NAS with Portainer(stack)
- Directory Structure:
/volume1/docker/n8n/data
/volume1/docker/n8n/postgresql
/volume1/docker/n8n/ redis
- 핵심 전략: .env 파일을 활용한 환경변수 분리 (보안 및 유지보수 용이성).
- NPM (Reverse Proxy) 설정
n8n은 실행 로그를 실시간으로 보기 위해 Websocket이 필수임. NPM 설정에서 이를 누락하면 워크플로우 실행 중 화면이 멈춤.
- Websockets Support: ON (필수)
- Block Common Exploits: OFF (Webhook 데이터 오탐지 방지)
- Advanced Tab:
proxy_buffering off;
proxy_cache off;
client_max_body_size 0;
- YML 세팅 값
services:
n8n:
image: 'docker.n8n.io/n8nio/n8n:1.119.2'
user: 0:0
ports:
- 5678:5678
environment:
- SERVICE_URL_N8N_5678
- 'N8N_EDITOR_BASE_URL=${SERVICE_URL_N8N}'
- 'WEBHOOK_URL=${SERVICE_URL_N8N}'
- 'N8N_HOST=${SERVICE_URL_N8N}'
- 'GENERIC_TIMEZONE=${GENERIC_TIMEZONE:-Europe/Berlin}'
- 'TZ=${TZ:-Europe/Berlin}'
- DB_TYPE=postgresdb
- 'DB_POSTGRESDB_DATABASE=${POSTGRES_DB:-n8n}'
- DB_POSTGRESDB_HOST=postgresql
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_USER=$SERVICE_USER_POSTGRES
- DB_POSTGRESDB_SCHEMA=public
- DB_POSTGRESDB_PASSWORD=$SERVICE_PASSWORD_POSTGRES
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_HEALTH_CHECK_ACTIVE=true
- 'N8N_ENCRYPTION_KEY=${SERVICE_PASSWORD_ENCRYPTION}'
- N8N_RUNNERS_ENABLED=true
- OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true
- 'N8N_BLOCK_ENV_ACCESS_IN_NODE=${N8N_BLOCK_ENV_ACCESS_IN_NODE:-true}'
- 'N8N_GIT_NODE_DISABLE_BARE_REPOS=${N8N_GIT_NODE_DISABLE_BARE_REPOS:-true}'
- 'N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=${N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS:-true}'
- 'N8N_PROXY_HOPS=${N8N_PROXY_HOPS:-1}'
volumes:
- '/volume1/docker/n8n/data:/home/node/.n8n'
depends_on:
postgresql:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test:
- CMD-SHELL
- 'wget -qO- http://127.0.0.1:5678/'
interval: 5s
timeout: 20s
retries: 10
n8n-worker:
image: 'docker.n8n.io/n8nio/n8n:1.119.2'
user: 0:0
command: worker
environment:
- 'GENERIC_TIMEZONE=${GENERIC_TIMEZONE:-Europe/Berlin}'
- 'TZ=${TZ:-Europe/Berlin}'
- DB_TYPE=postgresdb
- 'DB_POSTGRESDB_DATABASE=${POSTGRES_DB:-n8n}'
- DB_POSTGRESDB_HOST=postgresql
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_USER=$SERVICE_USER_POSTGRES
- DB_POSTGRESDB_SCHEMA=public
- DB_POSTGRESDB_PASSWORD=$SERVICE_PASSWORD_POSTGRES
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_HEALTH_CHECK_ACTIVE=true
- 'N8N_ENCRYPTION_KEY=${SERVICE_PASSWORD_ENCRYPTION}'
- N8N_RUNNERS_ENABLED=true
- 'N8N_BLOCK_ENV_ACCESS_IN_NODE=${N8N_BLOCK_ENV_ACCESS_IN_NODE:-true}'
- 'N8N_GIT_NODE_DISABLE_BARE_REPOS=${N8N_GIT_NODE_DISABLE_BARE_REPOS:-true}'
- 'N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=${N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS:-true}'
- 'N8N_PROXY_HOPS=${N8N_PROXY_HOPS:-1}'
volumes:
- '/volume1/docker/n8n/data:/home/node/.n8n'
healthcheck:
test:
- CMD-SHELL
- 'wget -qO- http://127.0.0.1:5678/healthz'
interval: 5s
timeout: 20s
retries: 10
depends_on:
n8n:
condition: service_healthy
postgresql:
condition: service_healthy
redis:
condition: service_healthy
postgresql:
image: 'postgres:16-alpine'
volumes:
- '/volume1/docker/n8n/postgresql:/var/lib/postgresql/data'
environment:
- POSTGRES_USER=$SERVICE_USER_POSTGRES
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
- 'POSTGRES_DB=${POSTGRES_DB:-n8n}'
healthcheck:
test:
- CMD-SHELL
- 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}'
interval: 5s
timeout: 20s
retries: 10
redis:
image: 'redis:6-alpine'
user: 0:0
volumes:
- '/volume1/docker/n8n/redis:/data'
healthcheck:
test:
- CMD
- redis-cli
- ping
interval: 5s
timeout: 5s
retries: 10
.env 세팅 값
SERVICE_FQDN_N8N=n8n.도메인
SERVICE_FQDN_N8N_5678=n8n.도메인:5678
SERVICE_PASSWORD_ENCRYPTION=32token(다른거)
SERVICE_PASSWORD_POSTGRES=32token(다른거)
SERVICE_URL_N8N=https://n8n.도메인
SERVICE_URL_N8N_5678=https://n8n.도메인:5678
SERVICE_USER_POSTGRES=16token(아니면 너가 원하는거)
DB_POSTGRESDB_PASSWORD=$SERVICE_PASSWORD_POSTGRES
DB_POSTGRESDB_USER=$SERVICE_USER_POSTGRES
GENERIC_TIMEZONE=Asia/Seoul
N8N_BLOCK_ENV_ACCESS_IN_NODE=true
N8N_EDITOR_BASE_URL=${SERVICE_URL_N8N}
N8N_ENCRYPTION_KEY=${SERVICE_PASSWORD_ENCRYPTION}
N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
N8N_GIT_NODE_DISABLE_BARE_REPOS=true
N8N_HOST=${SERVICE_URL_N8N}
N8N_PROXY_HOPS=1
POSTGRES_DB=n8n
POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
POSTGRES_USER=$SERVICE_USER_POSTGRES
TZ=Asia/Seoul
WEBHOOK_URL=${SERVICE_URL_N8N}
token값 생성은 링크에서 쉽게 가능하다.
https://1password.com/password-generator
- 결론 및 요약 (Conclusion)
시놀로지 NAS에 n8n을 구축하는 과정은 단순한 컨테이너 실행 그 이상이었습니다. 특히 성능과 안정성을 위해 queue 모드(Redis 연동)를 선택하면서 마주친 권한 문제는, 많은 사용자가 "그냥 포기하고 클라우드 쓸까?"를 고민하게 만드는 진입 장벽입니다.
이번 과정을 통해 얻은 핵심 노하우를 정리하며 글을 마칩니다.
- Redis 권한은 root로 제압한다: 시놀로지 Docker 환경에서 Redis가 시작하자마자 exit code 1로 죽는다면, 복잡한 리눅스 권한 설정(CHMOD)보다 docker-compose.yml 내 user: root 한 줄이 가장 확실하고 빠른 해결책입니다.
- 환경변수의 나비효과: SERVICE_URL 같은 핵심 변수의 오타나 매핑 실수는, 컨테이너는 멀쩡히 켜져 있는데 Webhook만 작동하지 않는 답답한 상황을 만듭니다. .env 파일과 yml 파일 간의 변수 매핑을 두 번, 세 번 확인해야 합니다.
- NPM은 단순 터널이 아니다: n8n의 꽃인 '실시간 실행 로그'를 보려면 Nginx Proxy Manager에서 Websockets Support 켜기와 Buffering off 설정이 선택이 아닌 필수입니다.
이제 멈추지 않는 안정적인 자동화 서버가 준비되었습니다. 단순 반복 작업에서 벗어나, 나만의 창의적인 워크플로우를 마음껏 설계해 보시길 바랍니다.