Docker Compose로 n8n 프로덕션 환경 구축하기 (Traefik + PostgreSQL + Redis)
Traefik + PostgreSQL + Redis + Worker 구성의 프로덕션급 n8n 환경을 Docker Compose로 구축하는 전 과정을 정리했습니다.
Docker Compose로 n8n 프로덕션 환경 구축하기 (Traefik + PostgreSQL + Redis)
n8n은 Zapier나 Make(구 Integromat)와 유사한 오픈소스 워크플로우 자동화 도구입니다. 직접 서버에 호스팅하여 데이터를 외부로 보내지 않고 사용할 수 있고, 400개 이상의 서비스 연동과 커스텀 코드(JavaScript/Python) 실행을 지원합니다. 이 가이드에서는 단순한 단일 컨테이너 구성이 아닌, 도메인 + TLS + PostgreSQL + Redis + Worker를 갖춘 프로덕션급 환경을 Docker Compose로 구축하는 방법을 정리합니다.
아키텍처 개요
이 가이드에서 구축할 스택은 다음과 같습니다.
[브라우저]
│ HTTPS
▼
[Traefik] ← Let's Encrypt TLS 자동 발급/갱신
│
▼
[n8n Master] ── 워크플로우 실행 요청 ──▶ [Redis Queue]
│
▼
[n8n Worker]
│
┌───────────────┘
▼
[PostgreSQL] ← 워크플로우/실행 이력 저장
각 컴포넌트의 역할은 다음과 같습니다.
- Traefik: 리버스 프록시. Let's Encrypt를 통해 HTTPS 인증서를 자동으로 발급·갱신합니다.
- PostgreSQL: 프로덕션 데이터베이스. n8n 기본 SQLite 대신 사용하여 안정성과 성능을 확보합니다.
- Redis: 워크플로우 실행 Queue. Master 노드가 실행 요청을 Queue에 쌓으면 Worker가 처리합니다.
- n8n Master: UI 제공 및 워크플로우 관리. 실행 요청을 Redis Queue에 전달합니다.
- n8n Worker: Queue에서 작업을 가져와 실제 워크플로우를 실행합니다. 수평 확장이 가능합니다.
사전 요구사항
- Docker 및 Docker Compose 설치
- 외부에서 접근 가능한 서버 (80, 443 포트 오픈)
- 도메인 및 DNS A 레코드 설정 (
n8n.your-domain.com→ 서버 IP)
# Docker 버전 확인
docker --version
# Docker Compose 버전 확인
docker compose version
# Timezone 설정 (서울)
sudo timedatectl set-timezone Asia/Seoul
timedatectl
1. 프로젝트 디렉토리 및 Docker Network 생성
mkdir ~/n8n && cd ~/n8n
docker network create n8n
프로젝트 최종 디렉토리 구조는 다음과 같습니다.
~/n8n/
├── docker-compose.yaml
├── .env
├── traefik/
│ └── traefik.yml
├── acme/
│ └── acme.json # Let's Encrypt 인증서 저장
└── logs/ # Traefik 로그
2. Traefik 설정
Traefik은 Docker 라벨 기반으로 라우팅을 자동으로 감지합니다. Let's Encrypt HTTP Challenge 방식으로 TLS 인증서를 발급합니다.
mkdir ~/n8n/traefik ~/n8n/acme ~/n8n/logs
vi ~/n8n/traefik/traefik.yml
global:
checkNewVersion: false
sendAnonymousUsage: false
api:
dashboard: true
insecure: false
log:
level: INFO
format: json
filePath: /logs/traefik.log
accessLog:
filePath: /logs/access.log
entryPoints:
web:
address: ":80"
http:
redirections:
entrypoint:
to: websecure
scheme: https
websecure:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: n8n
watch: true
certificatesResolvers:
letsencrypt:
acme:
email: <your-email@example.com> # Let's Encrypt 알림 수신용 이메일
storage: /acme/acme.json
httpChallenge:
entryPoint: web
Let's Encrypt 인증서 저장 파일을 생성합니다. 권한은 반드시 600이어야 합니다.
touch ~/n8n/acme/acme.json
chmod 600 ~/n8n/acme/acme.json
3. .env 파일 작성
민감한 정보는 .env 파일에서 관리합니다.
vi ~/n8n/.env
# 도메인 설정
N8N_HOST=n8n.<your-domain.com>
N8N_PROTOCOL=https
TRAEFIK_HOST=traefik.<your-domain.com>
# n8n 암호화 키 (임의의 긴 문자열로 변경)
N8N_ENCRYPTION_KEY=change-this-to-a-random-secret-key
# 데이터베이스 비밀번호
DB_POSTGRESDB_PASSWORD=change-this-db-password
# Redis 비밀번호
REDIS_PASSWORD=change-this-redis-password
# 타임존
GENERIC_TIMEZONE=Asia/Seoul
# n8n 컨테이너 실행 사용자 (권한 문제 방지)
UID=1000
GID=1000
N8N_ENCRYPTION_KEY는 n8n이 Credential(API 키, 패스워드 등)을 암호화할 때 사용합니다. 분실하면 저장된 모든 Credential을 복구할 수 없으니 반드시 안전하게 보관하세요.
4. docker-compose.yaml 작성
모든 서비스를 하나의 파일로 정의합니다.
vi ~/n8n/docker-compose.yaml
services:
# ── Reverse Proxy ──────────────────────────────────────
traefik:
image: traefik:v3.0
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik/traefik.yml:/etc/traefik/traefik.yml:ro
- ./acme:/acme
- ./logs:/logs
networks:
- n8n
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`${TRAEFIK_HOST}`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.tls=true"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
# ── Database ───────────────────────────────────────────
postgres:
image: postgres:16
container_name: postgres
restart: always
environment:
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=${DB_POSTGRESDB_PASSWORD}
- POSTGRES_DB=n8n
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- n8n
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n"]
interval: 10s
timeout: 5s
retries: 5
# ── Queue (Redis) ──────────────────────────────────────
redis:
image: redis:7-alpine
container_name: redis
restart: always
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis-data:/data
networks:
- n8n
healthcheck:
test: ["CMD", "redis-cli", "--no-auth-warning", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 5
# ── n8n Master ─────────────────────────────────────────
n8n:
image: docker.n8n.io/n8nio/n8n
container_name: n8n
user: "${UID}:${GID}"
restart: always
volumes:
- ~/.n8n:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
environment:
# 외부 접근 URL
- N8N_HOST=${N8N_HOST}
- N8N_PORT=5678
- N8N_PROTOCOL=${N8N_PROTOCOL}
- N8N_WEBHOOK_URL=https://${N8N_HOST}
- NODE_ENV=production
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
# 불필요한 텔레메트리 비활성화
- N8N_DIAGNOSTICS_ENABLED=false
- N8N_VERSION_NOTIFICATIONS_ENABLED=false
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
# PostgreSQL
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=${DB_POSTGRESDB_PASSWORD}
# Redis Queue
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_BULL_REDIS_PORT=6379
- QUEUE_BULL_REDIS_PASSWORD=${REDIS_PASSWORD}
networks:
- n8n
labels:
- "traefik.enable=true"
- "traefik.http.routers.n8n.rule=Host(`${N8N_HOST}`)"
- "traefik.http.routers.n8n.tls=true"
- "traefik.http.routers.n8n.tls.certresolver=letsencrypt"
- "traefik.http.services.n8n.loadbalancer.server.port=5678"
# ── n8n Worker ─────────────────────────────────────────
n8n-worker:
image: docker.n8n.io/n8nio/n8n
container_name: n8n-worker
user: "${UID}:${GID}"
restart: always
command: worker
volumes:
- ~/.n8n:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
environment:
- NODE_ENV=production
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
# PostgreSQL
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=${DB_POSTGRESDB_PASSWORD}
# Redis Queue
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_BULL_REDIS_PORT=6379
- QUEUE_BULL_REDIS_PASSWORD=${REDIS_PASSWORD}
networks:
- n8n
networks:
n8n:
external: true
volumes:
postgres-data:
redis-data:
주요 설정 포인트:
N8N_PORT=5678: n8n이 컨테이너 내부에서 리스닝하는 포트입니다. 외부에서는 Traefik이 443으로 받아 5678로 포워딩합니다.traefik.http.services.n8n.loadbalancer.server.port=5678: Traefik이 n8n 컨테이너의 5678 포트로 트래픽을 전달하도록 지정합니다.user: "${UID}:${GID}": n8n 컨테이너가 root로 실행되면~/.n8n디렉토리가 root 소유로 생성되어 권한 문제가 발생합니다. 현재 사용자 UID/GID를 지정하여 방지합니다.depends_on+healthcheck: PostgreSQL과 Redis가 완전히 준비된 후에 n8n이 시작되도록 합니다. healthcheck 없이depends_on만 쓰면 컨테이너가 뜨는 시점(준비 완료 전)에 n8n이 연결을 시도해 실패할 수 있습니다.EXECUTIONS_MODE=queue: Worker 모드 활성화 설정입니다. 이 값이 있어야 Master가 Redis Queue에 작업을 넣고, Worker가 꺼내서 실행하는 구조로 동작합니다.
5. 실행
cd ~/n8n
docker compose up -d
컨테이너 상태를 확인합니다.
docker compose ps
정상 실행 시 아래와 같이 출력됩니다.
NAME IMAGE STATUS
traefik traefik:v3.0 Up
postgres postgres:16 Up (healthy)
redis redis:7-alpine Up (healthy)
n8n docker.n8n.io/n8nio/n8n Up
n8n-worker docker.n8n.io/n8nio/n8n Up
n8n 로그를 확인합니다.
docker compose logs -f n8n
브라우저에서 https://n8n.<your-domain.com> 으로 접속하면 초기 계정 설정 화면이 나타납니다.
6. 백업
주기적인 백업을 위한 스크립트를 작성합니다.
vi ~/n8n/backup-n8n.sh
#!/bin/bash
BACKUP_PATH="/backup/n8n"
BACKUP_NAME="n8n-backup-$(date +%Y%m%d-%H%M%S)"
N8N_PATH=~/n8n
mkdir -p $BACKUP_PATH
cd $N8N_PATH
# PostgreSQL 덤프
docker compose exec -T postgres pg_dump -U n8n n8n > $BACKUP_PATH/$BACKUP_NAME.sql
# n8n 설정 및 워크플로우 백업
tar -czf $BACKUP_PATH/$BACKUP_NAME-data.tar.gz -C ~ .n8n
# 30일 이상 된 백업 정리
find $BACKUP_PATH -name "n8n-backup-*" -type f -mtime +30 -delete
echo "Backup completed: $BACKUP_PATH/$BACKUP_NAME"
chmod +x ~/n8n/backup-n8n.sh
매일 자정 자동 백업을 위한 cron 설정입니다.
(crontab -l ; echo "0 0 * * * ~/n8n/backup-n8n.sh >> ~/n8n/backup.log 2>&1") | crontab -
7. 업데이트
n8n 이미지를 최신 버전으로 업데이트할 때는 백업 후 이미지를 교체합니다.
cd ~/n8n
# 백업 먼저 실행
./backup-n8n.sh
# 최신 이미지 Pull
docker compose pull
# 재시작
docker compose down
docker compose up -d
# 로그 확인
docker compose logs -f n8n
마치며
이 가이드에서 구축한 n8n 스택의 핵심 포인트는 세 가지입니다.
- Traefik: 별도 Certbot 없이 Let's Encrypt 인증서를 자동으로 관리해 줍니다. 컨테이너 추가 시 라벨만 붙이면 자동으로 라우팅을 잡아줘서 확장이 편리합니다.
- Queue 모드(Redis + Worker): 워크플로우가 많아지면 단일 n8n 인스턴스가 병목이 됩니다. Worker를 분리하면
n8n-worker서비스만 scale out하여 처리량을 높일 수 있습니다. - PostgreSQL + healthcheck: SQLite는 동시 쓰기에 약하고 파일 기반이라 백업이 번거롭습니다. PostgreSQL을 쓰면 표준 DB 백업 방식을 그대로 적용할 수 있고, healthcheck로 기동 순서를 보장해 초기화 타이밍 문제를 예방합니다.
n8n은 사용자가 직접 커스텀 코드를 실행할 수 있어 Zapier 같은 SaaS 대비 훨씬 유연합니다. 한번 셋업해 두면 반복 업무를 자동화하는 데 강력한 도구가 됩니다.