Docker Compose로 n8n 설치

Docker Compose로 n8n 설치 (Install using Docker Compose)

셀프호스팅 n8n을 위한 Docker Compose 구성을 직접 만들어 봅니다. 여기에는 n8n Assistant를 구동하는 샌드박스 스택까지 포함됩니다. 설정을 완전히 직접 제어하고 싶거나, n8n을 기존 Compose 프로젝트에 접어 넣어야 한다면 이 방법이 좋아요.

출처: 공식문서 - Install using Docker Compose

💡 빠른 대안: 파일을 직접 작성하지 않고 n8n(과 n8n Assistant)을 빨리 띄우고 싶다면 원라인 설치를 사용하세요. 아래 내용을 전부 자동으로 설정해 줍니다.

시작 전에 필요한 것

  • Docker Engine과 Docker Compose v2. docker compose version으로 확인하세요.
  • 최소 4 GB RAM과 2 vCPUs. AI 생성 코드를 실행하는 샌드박스(sandbox-runner-1)는 Docker-in-Docker를 사용해서 일반 컨테이너보다 더 많은 여유 리소스가 필요해요.

💡 Windows 사용자: WSL을 사용하되, Docker Desktop(WSL2 백엔드) 또는 Linux 배포판에 직접 설치한 Docker Engine을 쓰세요. 프로젝트 폴더는 /mnt/c/...가 아니라 WSL 파일시스템 안(예: ~/n8n)에 두세요. 그 경계를 넘는 바인드 마운트는 느리고 권한 문제가 발생할 수 있습니다.

1단계: 프로젝트 폴더 만들기

mkdir n8n && cd n8n

2단계: .env 만들기

이 파일은 샌드박스 서비스들이 서로 통신할 때 쓰는 시크릿을 담습니다. 자리 표시자를 당신의 값으로 바꿔 .env 파일을 만들고, 이 파일은 버전 컨트롤에 넣지 마세요.

# Sandbox service secrets — pick your own values
SANDBOX_API_KEYS=change-me-api-key
SANDBOX_API_RUNNER_REGISTRATION_TOKEN=change-me-registration-token
SANDBOX_API_RUNNER_API_KEY=change-me-runner-key

# Must match a value in SANDBOX_API_KEYS above — this is how n8n authenticates to the sandbox
N8N_SANDBOX_SERVICE_API_KEY=change-me-api-key

# Web search: secret for the bundled SearXNG instance — pick your own value
SEARXNG_SECRET=change-me-searxng-secret
N8N_INSTANCE_AI_SEARXNG_URL=http://searxng:8080

아직 AI 프로바이더 키는 필요 없어요. 아래의 n8n Assistant 켜기에서 모든 것이 실행된 뒤 설정하면 됩니다.

3단계: searxng-settings.yml 만들기

기본 SearXNG 이미지는 HTML만 제공하는데, n8n의 웹 검색은 JSON API가 필요해요. 이 파일이 그 JSON API를 켭니다.

use_default_settings: true
search:
  formats:
    - html
    - json

4단계: compose.yml 만들기

이 파일은 설정하는 모든 서비스를 정의합니다. n8n 자체, n8n Assistant가 안전하게 코드를 실행하게 해 주는 샌드박스 스택, 그리고 웹 검색용 SearXNG입니다.

compose.yml의 실제 서비스 정의는 아래 설치한 것 확인 표와 함께, n8n 공식 문서의 compose.yml 블록을 참고하세요.

5단계: 모두 시작하기

docker compose up -d
docker compose ps

sandbox-apihealthy로 표시될 때까지 기다리세요. 그러면 sandbox-runner-1n8n이 자동으로 시작됩니다.

6단계: 정상 동작 확인

# sandbox-api가 n8n에서 접근 가능한지
docker compose exec n8n wget -qO- http://sandbox-api:8080/healthz

# runner가 API에 스스로 등록했는지
docker compose logs sandbox-api | grep -i runner

# n8n이 떠 있는지
curl -sf http://localhost:5678/healthz

웹 브라우저에서 http://localhost:5678을 열어 n8n을 시작하세요.

방금 설치한 것들

컴포넌트 용도
n8n 워크플로 편집기 자체. http://localhost:5678에서 접근합니다.
sandbox-certs 한 번 실행되어 다른 샌드박스 서비스가 필요로 하는 TLS 인증서를 생성한 뒤 종료됩니다.
sandbox-api n8n Assistant가 코드를 실행해야 할 때 n8n이 통신하는 컨트롤 플레인입니다.
sandbox-runner-1 실제 작업을 수행. 샌드박스를 만들고 실행하는 특권(privileged) Docker-in-Docker 컨테이너입니다.
searxng n8n Assistant용으로 번들된 웹 검색 백엔드입니다.

이 구성은 n8n 자체 샌드박스(n8n-sandbox)를 번들로 쓰는데, 로컬 개발과 테스트에 적합합니다. 프로덕션 인스턴스의 경우 n8n은 현재 샌드박스 프로바이더로 Daytona를 권장합니다.

여기에는 데이터베이스 서비스가 정의되어 있지 않아요. n8n은 내장 SQLite 데이터베이스로 폴백하며, 볼륨을 마운트하지 않으면 컨테이너 안에 저장됩니다. 프로덕션 인스턴스라면 아래의 PostgreSQL 사용로 교체하세요.

옵션: n8n Assistant 켜기

위 구성은 전체 샌드박스 스택을 실행하지만, n8n Assistant 자체는 모델을 지정할 때까지 꺼져 있어요. n8n이 실행된 뒤 UI(인스턴스의 AI 설정)에서 할 수도 있고, 첫 로그인 전에 .env로 미리 구성할 수도 있습니다.

  1. AI 프로바이더 키를 .env에 추가하세요.

    N8N_INSTANCE_AI_MODEL_API_KEY=<your-api-key>
    
  2. 변경 사항을 적용하려면 n8n을 재시작하세요.

    docker compose up -d n8n
    

웹 검색은 기본적으로 번들된 SearXNG 서비스를 통해 실행됩니다. Brave Search를 쓰고 싶다면 UI에서 설정하거나 .env에 Brave API 키를 추가하세요. 설정하면 SearXNG보다 우선합니다.

INSTANCE_AI_BRAVE_SEARCH_API_KEY=BSA-xxx

지원되는 모델 프로바이더를 포함한 전체 설정 단계는 n8n Assistant 설정 문서에 있습니다.

옵션: SQLite 대신 PostgreSQL 사용

SQLite는 시도해 보기엔 충분하지만, 한두 명 이상이거나 연중무휴로 돌아가는 워크플로가 많은 프로덕션 인스턴스라면 Postgres를 사용하세요.

  1. .env에 샌드박스 시크릿과 함께 Postgres 크레덴셜을 추가하세요.

    POSTGRES_USER=change-me-user
    POSTGRES_PASSWORD=change-me-password
    POSTGRES_DB=n8n
    
  2. compose.ymlpostgres 서비스와 그 데이터용 볼륨을 추가하세요.

    volumes:
      db-storage:
    
    services:
      postgres:
        image: postgres:18
        restart: always
        environment:
          POSTGRES_USER: ${POSTGRES_USER}
          POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
          POSTGRES_DB: ${POSTGRES_DB}
          PGDATA: /var/lib/postgresql/data
        volumes:
          - db-storage:/var/lib/postgresql/data
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
          interval: 5s
          timeout: 5s
          retries: 10
    

    ⚠️ 주의: Postgres 18은 데이터를 기본적으로 저장하는 위치가 바뀌었습니다. PGDATA를 설정하면 이전 버전과 같은 폴더에 저장되어 볼륨 마운트가 그대로 유지됩니다. 그 줄을 지우지 마세요. 지우면 Postgres 18이 볼륨이 커버하지 않는 곳에 기록해서 데이터베이스가 빈 채로 시작됩니다.

    ⚠️ 주의: 이미 더 오래된 Postgres를 운영 중인가요? 바로 18로 올리는 것은 메이저 버전 업그레이드이고, Postgres는 이전 메이저 버전이 쓴 데이터 디렉터리를 열 수 없습니다. 기존 설정에서 이미지 태그를 올리면 database files are incompatible with server 오류로 실패합니다. 데이터는 그대로 유지됩니다. pg_dumpall로 먼저 백업한 뒤 공식 PostgreSQL 업그레이드 가이드를 따르세요.

  3. n8n이 Postgres를 가리키도록 n8n 서비스의 environment 블록에 다음을 추가하고, Postgres도 함께 기다리게 하세요.

    environment:
          # ...your existing N8N_ENABLED_MODULES, N8N_INSTANCE_AI_* settings stay as they are
          DB_TYPE: postgresdb
          DB_POSTGRESDB_HOST: postgres
          DB_POSTGRESDB_PORT: '5432'
          DB_POSTGRESDB_DATABASE: ${POSTGRES_DB}
          DB_POSTGRESDB_USER: ${POSTGRES_USER}
          DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
      depends_on:
        sandbox-api:
          condition: service_healthy
        postgres:
          condition: service_healthy
    
  4. 모든 것을 다시 시작하세요.

    docker compose up -d
    

    n8n은 시작 시 새 Postgres 데이터베이스로 스스로 마이그레이션합니다. 기존 SQLite 데이터는 자동으로 옮겨지지 않아요. 이 구성은 새 인스턴스용이지, 그 자리에서의 마이그레이션이 아닙니다.

    💡 참고: 전용 non-root Postgres 사용자와 외부 task runner 같은 더 견고한 구성은 n8n hosting 저장소의 withPostgres 예시를 참고하세요.

문제 해결

증상 원인
sandbox-api 또는 sandbox-runner-1 시작 실패, 인증서 오류 sandbox-certs가 완료되지 않았습니다. docker compose logs sandbox-certs를 확인하세요.
sandbox-api가 절대 healthy가 되지 않음 로그를 확인하고 그 이미지에 wget이 실제로 있는지 확인하세요.
sandbox-runner-1... must be set 오류로 시작 시 크래시 루프 필수 환경 변수가 빠져 있습니다. 가장 흔한 것은 SANDBOX_RUNNER_API_KEYS 또는 SANDBOX_RUNNER_REGISTRATION_TOKEN입니다. runner가 요구하는 전체 변수 목록은 runner 컨테이너 안에서 strings /usr/local/bin/sandbox-runner | grep -oE 'SANDBOX_[A-Z_]+ must be set'를 실행해 확인하세요.
Runner가 API에 등록되지 않음 SANDBOX_RUNNER_REGISTRATION_TOKEN 불일치 또는 SANDBOX_RUNNER_API_GRPC_ADDR 오류.
n8n의 샌드박스 호출 실패 .env의 샌드박스 URL/키가 sandbox-api의 주소나 SANDBOX_API_KEYS와 일치하지 않습니다.
Linux에선 되는데 WSL에선 실패 보통 바인드 마운트 경로 문제입니다. 프로젝트를 /mnt/c/...가 아니라 WSL 파일시스템 안에 두세요.

보안 체크리스트

  • sandbox-runner-1(privileged: true, Docker-in-Docker)은 절대 공개 인터넷에 노출하지 마세요. 호스트의 root와 동등한 것으로 취급하세요.
  • 클라우드 방화벽에서 n8n의 포트만 여세요.
  • SANDBOX_API_KEYS, 등록 토큰, runner 키는 고유해야 하고 change-me-...로 남겨두면 안 되며 주기적으로 교체해야 합니다.
  • sandbox-apisandbox-runner-1env_file: .env를 사용하지 않습니다. 각각은 필요한 특정 변수만 environment 블록에서 명시적으로 받습니다. 모델 API 키, Brave 키, Postgres 비밀번호, n8n 암호화 키는 결코 샌드박스 컨테이너에 닿지 않아요.
  • sandbox-tls 볼륨 아래 mTLS 키(루트 CA 키 포함)는 0600으로 잠그고, 필요한 서비스만 소유하게 하세요(sandbox-api는 자신의 키, runner의 키와 CA 키는 root). 어느 것도 전 세계적으로 읽을 수 없게 두지 마세요.
  • sandbox-tls 볼륨을 재생성할 계획이 있어야 합니다. sandbox-certs가 생성하는 인증서는 자동 갱신되지 않아요.

서비스 아키텍처

flowchart LR
    subgraph compose["docker-compose.yml (one project)"]
        n8n["n8n"]
        certs["sandbox-certs<br/>(runs once, then exits)"]
        api["sandbox-api"]
        runner["sandbox-runner-1<br/>(privileged, DinD)"]
        sandboxes["sandbox containers<br/>(per execution)"]

        certs -.->|TLS certs| api
        certs -.->|TLS certs| runner
        n8n -->|HTTP :8080| api
        api -->|control gRPC| runner
        runner --> sandboxes
    end

    classDef oneshot fill:#eee,stroke:#999,stroke-dasharray: 3 3;
    class certs oneshot

n8n은 코드 실행 요청을 sandbox-api로 보내고, sandbox-api는 이를 sandbox-runner-1에 넘겨 실제 샌드박스 컨테이너를 만들고 실행합니다. sandbox-certs는 시작 시 한 번 실행되어 나머지 두 서비스가 필요로 하는 TLS 인증서를 생성한 뒤 종료되고, 나머지는 모두 그걸 기다립니다.

더 알아보기 (Learn more)