n8n 일반 모드 vs 큐 모드 — 세팅·장단점, 그리고 큐모드가 S3 를 쓰는 이유
Summary
n8n 을 셀프호스팅하다 보면 어느 순간 “실행이 한 프로세스에 다 몰려서 무거운 워크플로우 하나가 전체를 막는” 상황을 만나요. 이걸 푸는 게 큐 모드(queue mode) 입니다. 메인 인스턴스는 UI·API·트리거만 맡고, 실제 실행은 별도 워커(worker) 들이 Redis 큐에서 꺼내 처리하는 구조에요.
이 글에서는 기본인 일반 모드(regular) 와 큐 모드 를 세팅 방법부터 장단점까지 비교하고, 큐모드에서 자주 막히는 지점인 바이너리 데이터와 S3 이야기를 정리합니다. 그리고 S3 없이도 큐모드를 굴리는 방법까지 같이 다뤄볼게요.
💡 이 글에서 다루는 것
- 일반 모드 / 큐 모드의 구조 차이 (한 그림으로)
- 각각의 세팅 방법 (env 변수, 워커·웹훅 프로세서, docker-compose 예시)
- 두 모드의 장단점 비교표
- 큐모드가 바이너리 데이터에 S3 를 요구하는 진짜 이유
- S3 없이
database모드로 구축하는 법과 그 트레이드오프
1. 한 그림으로 — 일반 모드 vs 큐 모드
먼저 두 모드가 어떻게 다른지 구조부터 잡고 갈게요.
일반 모드 는 n8n 프로세스 하나가 전부 다 합니다. UI 를 띄우고, API 를 받고, 스케줄/웹훅 트리거를 감시하고, 워크플로우 실행까지 같은 프로세스 안에서 처리해요.
[ n8n (main) ] ← UI · API · 트리거 · 실행을 전부 한 프로세스에서
│
[ PostgreSQL ]
큐 모드 는 역할을 쪼갭니다. 메인은 “받기”만 하고, 실행은 Redis 큐를 거쳐 워커들에게 분산돼요.
[ Redis (Bull 큐) ]
▲ │
│ enqueue │ dequeue
[ n8n (main) ] [ worker 1 ]
UI·API·트리거 [ worker 2 ]
│ [ worker N ]
└──────┬────────┘
[ PostgreSQL (공유) ]
핵심은 메인과 워커가 서로 다른 프로세스(보통 다른 컨테이너, 때로는 다른 머신)라는 점이에요. 이 “분리” 가 큐모드의 모든 장점과 단점의 출발점입니다. 나중에 S3 이야기도 전부 여기서 나와요.
2. 일반(regular) 모드 세팅
사실 일반 모드는 “세팅” 이랄 게 거의 없어요. n8n 의 기본값이 일반 모드라서, 아무 설정 없이 띄우면 그대로 일반 모드입니다.
docker run -it --rm \
--name n8n \
-p 5678:5678 \
-e DB_TYPE=postgresdb \
-e DB_POSTGRESDB_HOST=postgres \
-e DB_POSTGRESDB_DATABASE=n8n \
-e DB_POSTGRESDB_USER=n8n \
-e DB_POSTGRESDB_PASSWORD=<DB_PASSWORD> \
-e N8N_ENCRYPTION_KEY=<N8N_ENCRYPTION_KEY> \
docker.n8n.io/n8nio/n8n
명시적으로 모드를 박고 싶다면 이렇게 둡니다.
-e EXECUTIONS_MODE=regular
✅ 소규모 팀, 워크플로우 수십 개, 동시 실행이 몇 개 수준이면 일반 모드로 충분합니다. 운영 부담이 가장 작아요. 굳이 Redis 를 얹을 이유가 없어요.
3. 큐(queue) 모드 세팅
큐 모드는 최소 세 덩어리가 필요해요.
- Redis — 실행 작업을 담아두는 큐(내부적으로 Bull 사용)
- 공유 PostgreSQL — 메인·워커가 같은 DB 를 봐야 함 (PostgreSQL 13+ 권장)
- 메인 1개 + 워커 N개 — 같은 암호화 키, 같은 Redis, 같은 DB 를 공유
여기서 가장 자주 깜빡하는 게 N8N_ENCRYPTION_KEY 를 모든 인스턴스에서 동일하게 맞추는 거예요. 이게 어긋나면 워커가 자격증명을 복호화하지 못해서 실행이 줄줄이 실패합니다.
3-1. 공통 env (메인·워커·웹훅 전부)
export EXECUTIONS_MODE=queue
export N8N_ENCRYPTION_KEY=<N8N_ENCRYPTION_KEY> # 모든 인스턴스 동일
# Redis 연결
export QUEUE_BULL_REDIS_HOST=redis
export QUEUE_BULL_REDIS_PORT=6379
export QUEUE_BULL_REDIS_PASSWORD=<REDIS_PASSWORD>
export QUEUE_BULL_REDIS_DB=0
# 공유 PostgreSQL
export DB_TYPE=postgresdb
export DB_POSTGRESDB_HOST=postgres
export DB_POSTGRESDB_DATABASE=n8n
export DB_POSTGRESDB_USER=n8n
export DB_POSTGRESDB_PASSWORD=<DB_PASSWORD>
위 블록은 메인이든 워커든 똑같이 들어갑니다. 차이는 “어떤 명령으로 띄우느냐” 뿐이에요.
3-2. 메인 인스턴스
메인은 평소처럼 기동하면 됩니다. UI·API·트리거를 담당해요.
n8n start
웹훅 트래픽이 많아서 웹훅 처리까지 따로 떼고 싶으면, 메인에서 프로덕션 웹훅 처리를 끄고 별도 웹훅 프로세서로 넘길 수 있어요.
export N8N_DISABLE_PRODUCTION_MAIN_PROCESS=true
3-3. 워커
실제 실행을 담당하는 주인공이에요. n8n worker 로 띄웁니다.
n8n worker --concurrency=10
--concurrency 는 워커 하나가 동시에 처리할 실행 개수예요. 지정하지 않으면 기본값은 10 입니다. 워커는 그냥 컨테이너를 더 띄우면 수평으로 늘어나요. 부하가 커지면 워커 수를 늘리는 게 큐모드의 기본 스케일링 방법입니다.
워커에 헬스체크 엔드포인트를 열어두면 오케스트레이터(K8s 등)가 살아있는지 확인할 수 있어요.
export QUEUE_HEALTH_CHECK_ACTIVE=true
3-4. 웹훅 프로세서 (선택)
웹훅 수신을 전담하는 프로세스도 따로 둘 수 있어요. 메인에서 N8N_DISABLE_PRODUCTION_MAIN_PROCESS=true 를 켰다면 이 프로세스가 웹훅을 받습니다.
n8n webhook
3-5. docker-compose 로 묶기
위 조각들을 한 파일로 모으면 이런 모양이 됩니다. 바이너리 데이터 모드는 일단 S3 없이 가는 database 로 둘게요 (이유는 6장에서 설명).
x-n8n-common: &n8n-common
image: docker.n8n.io/n8nio/n8n
environment:
- EXECUTIONS_MODE=queue
- N8N_ENCRYPTION_KEY=<N8N_ENCRYPTION_KEY>
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_BULL_REDIS_PORT=6379
- QUEUE_BULL_REDIS_PASSWORD=<REDIS_PASSWORD>
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=<DB_PASSWORD>
- N8N_DEFAULT_BINARY_DATA_MODE=database
depends_on:
- postgres
- redis
services:
postgres:
image: postgres:16
environment:
- POSTGRES_DB=n8n
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=<DB_PASSWORD>
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7
command: ["redis-server", "--requirepass", "<REDIS_PASSWORD>"]
n8n-main:
<<: *n8n-common
command: start
ports:
- "5678:5678"
n8n-worker:
<<: *n8n-common
command: worker --concurrency=10
# docker compose up --scale n8n-worker=3 으로 워커를 늘릴 수 있어요.
volumes:
pgdata:
⚠️ 위
&n8n-common의 env 가 메인·워커에 그대로 상속돼요. 그래서 암호화 키·Redis·DB 가 자동으로 동일하게 맞춰집니다. 이 공유가 큐모드의 전제 조건이에요.
4. 장단점 비교
두 모드를 한 표로 정리하면 다음과 같아요.
| 항목 | 일반 모드 (regular) | 큐 모드 (queue) |
|---|---|---|
| 구성 요소 | n8n + DB | n8n 메인 + 워커 N + Redis + 공유 DB |
| 스케일링 | 수직(스펙 업) 위주 | 수평(워커 추가)으로 확장 |
| 동시 실행 처리 | 한 프로세스가 다 떠안음 | 워커들로 분산 |
| 장애 격리 | 무거운 실행 하나가 전체를 막을 수 있음 | 워커가 죽어도 메인·다른 워커는 살아있음 |
| 운영 난이도 | 낮음 (얹을 게 없음) | 높음 (Redis·워커·키 동기화 관리) |
| 바이너리 데이터 | 로컬 파일시스템 그대로 OK | filesystem 모드 불가 → S3 또는 database 필요 |
| HA(다중 메인) | 불가 | 가능하지만 Enterprise 전용 |
| 추천 규모 | 소~중 | 중~대, 동시 실행 많음 |
큐모드의 장점 을 한 줄로 줄이면 “실행을 수평으로 늘려서, 무거운 워크플로우가 다른 워크플로우를 막지 않게 만드는 것” 이에요. 트래픽이 튀어도 워커만 더 붙이면 됩니다.
단점 은 운영 복잡도예요. Redis 가 새 의존성으로 들어오고, 암호화 키·Redis·DB 를 모든 인스턴스에서 똑같이 맞춰야 하고, 뒤에서 설명할 바이너리 데이터 처리도 따로 신경 써야 해요. 그래서 “동시 실행이 별로 없는데 그냥 멋있어 보여서” 큐모드를 켜는 건 비추천드립니다.
5. 왜 큐모드는 S3 를 (사실상) 써야 하나
여기가 이 글의 핵심이에요. 큐모드를 켜자마자 사람들이 가장 많이 밟는 지점이 바로 바이너리 데이터 입니다.
n8n 에서 “바이너리 데이터” 는 워크플로우가 다루는 파일이에요. HTTP 로 받은 이미지, 업로드된 PDF, 변환 중인 엑셀 같은 것들이죠. n8n 은 이 파일을 실행 데이터(JSON)와 분리해서 어딘가에 저장해 둡니다.
이걸 저장하는 방식이 바이너리 데이터 모드 이고, 세 가지가 있어요.
| 모드 | 저장 위치 | 비고 |
|---|---|---|
default (메모리/database) |
메모리 → DB 에 함께 저장 | 큐모드에서 권장되는 기본 |
filesystem |
실행한 프로세스의 로컬 디스크 | 큐모드에서 지원 안 됨 |
s3 |
S3 호환 외부 스토리지 | 큐모드 대규모에 적합, Enterprise 라이선스 |
문제는 filesystem 모드예요. 이 모드는 워크플로우를 실행한 그 프로세스의 로컬 디스크 에 파일을 씁니다. 일반 모드에서는 메인 하나가 다 처리하니 아무 문제가 없어요. 자기가 쓰고 자기가 읽으니까요.
그런데 큐모드에서는 상황이 달라집니다. 1장에서 본 것처럼 워커들은 서로 다른 프로세스(다른 컨테이너/머신) 예요.
worker-1이 파일을 받아서 자기 로컬 디스크에 씀- 다음 단계 실행이
worker-2로 가거나, 메인이 그 파일을 결과로 보여주려 함 - 그런데
worker-2와 메인은worker-1의 로컬 디스크를 들여다볼 수 없음 → 파일을 못 찾음
그래서 n8n 은 아예 큐모드 + filesystem 조합을 지원하지 않는다 고 못 박아 뒀어요. 공식 문서 표현 그대로:
n8n doesn’t support queue mode with binary data storage in filesystem.
결국 큐모드에서 바이너리 파일을 다루려면, 메인과 모든 워커가 똑같이 접근할 수 있는 공유 저장소 가 필요해요. 그 답으로 가장 깔끔한 게 S3 입니다. 워커가 어디에 떠 있든 같은 버킷에 쓰고 같은 버킷에서 읽으니까요. 게다가 디스크 용량 걱정 없이 무한히 늘어나죠.
S3 외부 스토리지 세팅은 이렇게 들어갑니다.
export N8N_AVAILABLE_BINARY_DATA_MODES=filesystem,s3
export N8N_DEFAULT_BINARY_DATA_MODE=s3
export N8N_EXTERNAL_STORAGE_S3_HOST=s3.ap-northeast-2.amazonaws.com
export N8N_EXTERNAL_STORAGE_S3_BUCKET_NAME=my-n8n-binary
export N8N_EXTERNAL_STORAGE_S3_BUCKET_REGION=ap-northeast-2
export N8N_EXTERNAL_STORAGE_S3_ACCESS_KEY=<AWS_ACCESS_KEY_ID>
export N8N_EXTERNAL_STORAGE_S3_ACCESS_SECRET=<AWS_SECRET_ACCESS_KEY>
🚨 S3 외부 스토리지는 Enterprise 라이선스 기능 이에요. 라이선스 키가 만료되면 S3 버킷에서 읽기는 되지만 쓰기는 막힙니다. 엔터프라이즈를 쓰신다니 이 부분은 그대로 활용하시면 돼요.
정리하면 — 큐모드가 S3 를 “요구” 하는 게 아니라, 큐모드는 프로세스가 분리돼 있어서 로컬 디스크를 공유 못 하고, 그래서 파일을 다루려면 공유 저장소가 필수인데, 그 공유 저장소로 가장 자연스러운 선택지가 S3 인 거예요.
6. S3 없이 큐모드를 구축하는 방법
그럼 S3 가 없으면 큐모드를 못 쓰냐? 그건 아니에요. 핵심은 “공유 저장소” 였고, 메인과 워커가 이미 공유하는 저장소가 하나 더 있죠 — PostgreSQL 입니다.
바이너리 데이터 모드를 database 로 두면, 파일을 로컬 디스크가 아니라 공유 DB 에 함께 저장 해요. 모든 워커와 메인이 같은 Postgres 를 보니까, 누가 쓰든 누구나 읽을 수 있습니다. S3 도, Enterprise 라이선스도 필요 없어요.
export N8N_DEFAULT_BINARY_DATA_MODE=database
공식 문서도 “큐모드를 쓰면 이 값을 database 로 바꾸라” 고 안내하고 있어요. 그래서 위 5장 docker-compose 예시에서도 기본값으로 database 를 박아둔 거예요.
다만 트레이드오프가 분명해요.
⚠️
database모드의 한계
- 파일이 전부 DB 로 들어가서 DB 용량이 빠르게 불어납니다. 큰 파일을 자주 다루면 더 심해요.
- DB I/O·메모리 부담이 커져서, 대용량 파일에서는 S3 대비 느리고 무겁습니다.
- 실행 데이터 정리(pruning) 정책을 같이 잡아두지 않으면 DB 가 계속 커져요.
그래서 권장은 이렇게 정리돼요.
- 파일을 거의 안 다루거나, 다뤄도 작고 가끔 →
database모드로 충분. S3 없이 큐모드 OK. - 이미지·PDF·대용량 파일을 자주, 많이 → S3(외부 스토리지) 가 정답. Enterprise 라이선스가 있다면 이쪽을 권장드려요.
참고로 “그럼 NFS 같은 공유 디스크를 워커들에 마운트해서 filesystem 모드를 쓰면 되지 않냐” 는 아이디어가 나올 수 있는데, n8n 은 큐모드 + filesystem 조합 자체를 지원하지 않는다 고 명시해 둬서 권장하지 않아요. 공유 저장소가 필요하면 database 아니면 s3, 이 둘 중에서 고르는 게 안전합니다.
7. 정리
마지막으로 의사결정 흐름만 짧게 짚을게요.
- 동시 실행이 적고 운영을 단순하게 → 일반 모드.
- 동시 실행이 많고 수평 확장·장애 격리가 필요 → 큐 모드 (Redis + 워커 + 공유 DB + 동일 암호화 키).
- 큐모드에서 파일을 다룬다면 →
filesystem은 못 쓰니까,- 작고 가끔이면 →
database모드 (S3·라이선스 불필요) - 크고 자주면 → S3 외부 스토리지 (Enterprise)
- 작고 가끔이면 →
큐모드의 S3 이야기는 “n8n 이 별나서” 가 아니라, 실행을 여러 프로세스로 쪼갠 순간 로컬 디스크를 공유할 수 없게 되는 분산 시스템의 당연한 결과예요. 그 한 줄만 기억하면 바이너리 데이터 설정에서 헤맬 일이 없어요.
일단 오늘은 여기까지…..
다음 글에서는 큐모드 워커를 K8s 로 올릴 때의 HPA·헬스체크 구성을 정리해볼게요.