Docker란 무엇인가: n8n 초보자를 위한 최소 필수 지식
n8n을 설치할 때 가장 막히기 쉬운 부분은 n8n 자체가 아니라 그 앞에 있는 Docker다. 튜토리얼대로 다섯 줄 명령을 복사해 넣고 container가 뜨고 화면에서 두어 번 클릭하면 쓸 수는 있는데, 다음 날 돌아와 보면 workflow가 전부 사라져 있다 — 원인은 그 다섯 줄에 하나가 빠졌기 때문일 뿐이다 -v。
이 글은 n8n 설치에 실제로 필요한 Docker 부분만 다룬다: image, container, volume, port. 네 단어다. 네트워크 모드, Compose, 직접 image 빌드는 정말 필요해질 때 보면 되고, 미리 배워도 잊어버린다.
전제 조건은 하나뿐이다: 터미널에서 명령을 칠 수 있고, 「port」가 뭔지 아는 것. Docker를 한 번도 안 써봤어도 전혀 영향 없다.
- n8n이 결국 Docker에 설치되는 이유
- 네 단어: image, container, volume, port
- 먼저 Docker를 설치한다
- n8n 하나 띄우기, 세 단계
- 설치가 됐다는 기준
- 데이터는 결국 어느 파일에 있는가
- 실제로 마주치는 여섯 가지 오류
- n8n과 관련된 몇 가지 보안 사항
n8n이 결국 Docker에 설치되는 이유
공식 설치 페이지는 여러 경로를 제시한다: npm 전역 설치, npx로 바로 실행, Docker, 데스크톱 버전. 앞의 두 가지가 가장 가볍고 한 줄 npm install -g n8n 이면 끝나지만, 문제는 n8n을 당신机器의 Node에 묶어버린다는 점이다. n8n은 Node 버전을 까다롭게 따지고, 문서에 적힌 범위는 20.19부터 24.x까지다. 언젠가 다른 프로젝트 때문에 Node를 26으로 올리면 n8n도 같이 멈추고, 오류 메시지가 반드시 알아듣기 쉬운 것도 아니다.
Docker는 발상을 바꿨다: n8n과 그것이 필요로 하는 Node, 시스템 라이브러리, 시간대 데이터를 전부 하나의 패키지에 넣고, 이 패키지는 누구机器에서든 똑같이 돌아간다. 나는 일부러 2.43.7 image 안에 들어가 확인해봤는데, Node는 26.7.0이고 기반 image는 Alpine이다. 이것은 당신 호스트에 Node 18이 설치돼 있든 26이 설치돼 있든 전혀 상관없다.
비용도 분명히 말해두자. 유지해야 할 것이 하나 더 늘어난다: image가 디스크를 차지하고(2.43.7 압축 해제 후 1.8 GB), 문제가 생기면 로그를 한 곳 더 봐야 하고, 업그레이드가 「npm update」에서 「image 교체 후 재실행」으로 바뀐다. 나는 값어치 있다고 본다—업그레이드할 때 tag 바꾸고, container 삭제하고, 재실행하면 3분이면 되고, Node 버전은 신경 쓸 필요 없다.
로컬에서 이틀만 가지고 놀 거라면 Docker를 권하지 않는다, npm이 더 빠르다. 장기적으로 돌릴 생각이거나 동료에게 써주려면 바로 Docker로 가라, 우회하지 말고.
네 단어: image, container, volume, port
image
image는 다른 사람이 미리 만들어둔 읽기 전용 패키지로, 안에는 n8n을 돌릴 수 있는 완전한 파일 시스템이 들어 있다.n8nio/n8n 는 이름이고,2.43.7 는 tag다. 같은 이름 아래에 여러 tag가 붙어 있고, tag 하나가 버전 하나에 대응한다.
tag에 latest를 쓰면 「최신 안정판」이라는 뜻이다. 프로덕션에서는 이렇게 쓰지 않는 게 좋다: 어느 날 container를 재시작하다가 새 image를 끌어오면 동작이 바뀌어도 어디서부터 확인해야 할지조차 모른다. 버전 번호를 고정하고, 업그레이드는 별개의 작업으로 다뤄라.

docker pull 이 바로 네가 입력할container
image는 정적이다.docker run 그 후에 실행되는 프로세스가 바로 container다. container는 image에 쓰기 가능한 레이어 하나를 더한 것과 같고, 안에서 바꾼 모든 것은 이 레이어에 기록된다. container를 삭제하면 이 레이어도 함께 사라진다—이 문장이 뒤에 나오는 모든 함정의 근원이다.
卷
그래서 n8n의 데이터는 container 안에 두면 안 된다. volume은 호스트 머신 위의 디렉터리 한 조각으로, Docker가 관리하며 container의 특정 경로에 mount된다. n8n이 인식하는 경로는 /home/node/.n8n, 데이터베이스, 암호화 키, 이벤트 로그가 모두 거기에 있다.
내가 세어봤다: owner 계정을 막 만들고 워크플로를 한 번 실행한 .n8n 디렉터리가 5.5 MB. 그중 database.sqlite 1.5 MB, 별도로 4 MB의 -wal 파일이 있다. 이게 전부라서, 잃으면 전부 다시 해야 한다.
port
-p 5678:5678, 콜론 왼쪽은 호스트 port, 오른쪽은 container 내부 port다. container 안의 n8n은 항상 5678을 listen한다 (네가 N8N_PORT을 바꾸지 않는 한), 밖에서 번호를 바꾸고 싶으면 왼쪽만 바꾸면 된다:-p 8080:5678。
이렇게 쓰면 127.0.0.1:5678:5678 본机에만 bind되고 밖에서는 접속할 수 없다. 나는 테스트할 때 항상 이렇게 써서 끄는 걸 잊지 않는다.
먼저 Docker를 설치한다
Ubuntu / Debian에서 가장 간편한 것은 공식 스크립트다:
curl -fsSL https://get.docker.com | sh
공식 소스를 추가하고 docker-ce를 설치하며 compose 플러그인을 포함한다. 「curl로 받은 것을 그대로 sh에 넣는 것」이 꺼려지면,老老实히 apt 공식 소스 방식을 쓰면 된다. 단계는 Docker 문서에 있으며, 그냥 길 뿐이다.
설치 후 두 명령으로 버전을 확인한다:
docker --version
# Docker version 29.1.3, build 29.1.3-0ubuntu3~24.04.2
내 이 232는 Ubuntu 24.04 + Docker 29.1.3이다. 24, 25 어느 것을 써도 되고 명령에 차이는 없다.
Docker는 기본적으로 root 또는 sudo가 필요하다. 어떤 사람들은 사용자를 docker 그룹에 추가하면 sudo 없이 된다고 하는데, 이 말은 맞지만 그 사용자에게 root 권한을 준 것이나 다름없다. 이유는 보안 섹션에 있다.
n8n을 띄우는 방법, 세 단계
첫 번째 단계: 볼륨 생성
docker volume create n8n_data
# n8n_data
볼륨 이름이 출력되면 생성된 것이다. 이 단계를 안 해도 되지만,docker run 에 쓰면 -v n8n_data:/home/node/.n8n 자동으로 생성된다——그래도 한 번 먼저 생성하면 실제로 존재하는지 확인할 수 있다.
두 번째 단계: 실행하기
docker run -d
--name n8n
-p 127.0.0.1:5678:5678
-e TZ=Asia/Shanghai
-e GENERIC_TIMEZONE=Asia/Shanghai
-e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
-v n8n_data:/home/node/.n8n
n8nio/n8n:2.43.7
공식 문서의 원본 명령과 비교해서 두 군데를 바꿨다.
한 군데는 -d를 추가한 것이다. 문서에서 준 것은 docker run -it --rm로, 포그라운드에서 실행되어 터미널을 닫거나 Ctrl+C를 누르면 n8n이 멈추고,--rm 컨테이너까지 삭제된다. 디버깅할 때 이렇게 쓰면 깔끔하지만 장기 실행에는 안 되니 -d 를 추가해서 백그라운드에 있게 한다.
다른 한 군데는 포트 앞에 를 보충한 것이다 127.0.0.1:. 문서에는 -p 5678:5678라고 적혀 있는데, 이는 0.0.0.0에 바인딩하는 것과 같아서 머신에 공인 IP만 있으면 누구든 5678을 스캔해서 로그인 페이지를 볼 수 있다. 내 232의 5678은 점유되어 있어서 실제로는 -p 127.0.0.1:5681:5678를 사용하는데, 매핑 관계는 동일하다.
두 개의 타임존 변수를 모두 지정해야 한다.TZ 는 컨테이너의 시스템 시간을 관리하고,GENERIC_TIMEZONE 는 n8n 자체가 시간을 계산하는 방식을 관리하는데, Schedule Trigger가 인식하는 것은 후자이다. TZ만 설정하면定时 작업이 여전히 UTC 기준으로 돌아가서, 생각했던 아침 9시가 베이징 시간 17시가 된다. 이런 일은 로그에 예외 하나도 남지 않는다.
3단계: 로그를 보고, 뜨기를 기다린다
docker logs -f n8n
첫 시작 시 수십 개의 데이터베이스 마이그레이션이 실행되어,Starting migration ... 和 Finished migration ... 가 번갈아 출력되는데 정상이다. 다음 두 줄이 나타날 때까지 기다린다:
Version: 2.43.7
Editor is now accessible via:
http://localhost:5678
그 http://localhost:5678 는 컨테이너 내부 주소라서 브라우저에서 열 수 없다. 열 수 있는 것은 http://服务器IP:5678이거나, 본机의 http://127.0.0.1:5678이다. 이 문구에 속은 사람이 적지 않은데, 처음 실행한 나도 포함이다.
2.43.7은 시작할 때 다섯 개의 deprecation 경고도 출력하는데, N8N_RUNNERS_MODE、N8N_UNVERIFIED_PACKAGES_ENABLED 이 기본값들은 나중에 변경해야 한다. 사용에는 영향이 없고, 미래 버전을 위한 발판이다. 또 한 줄이 더 있는데 Failed to start Python task runner in internal mode. because Python 3 is missing from this system.——이미지에 Python이 설치되어 있지 않으므로, JS의 Code 노드만 사용한다면 전혀 신경 쓸 필요가 없다.

계정을 만들면 바로 들어간다. 이 단계를 volume에 기록하지 않으면 의미가 없다——아래 검증에서 실제로 디스크에 기록되었는지 확인하는 방법을 설명하겠다.
설치 완료 판단 기준
네 가지 확인, 모두 터미널에서 한다.
docker ps에서 STATUS는Up, PORTS에 매핑이 있고, IMAGE는 지정한 tag이다.curl -s http://127.0.0.1:5678/healthz반환{"status":"ok"}. 이 인터페이스는 프로세스가 살아있다는 것만 의미한다.docker exec n8n n8n --version回2.43.7, 지정한 tag와 일치한다.docker restart n8n, 십여 초 기다린 후 페이지를 다시 열면 더 이상 계정 생성을 요구하지 않아야 한다.
4번째 항목이 가장 간과하기 쉽고, 가장 가치 있다. 이것은 동시에 두 가지를 검증한다: volume이 올바르게 mount되었는지, 데이터가 실제로 container 외부에 있는지.

이 스크린샷을 찍을 때 container 안의 것을顺手 확인했다 .n8n, 5.5 MB, 위에서 말한 것과 맞습니다. 만약 당신의 du -sh 결과가 몇백 K로 나온다면, 대부분 데이터가 container 쓰기 가능 레이어에 기록된 것이고 volume이 mount되지 않은 것입니다.
데이터가 도대체 어느 파일에 있는지
volume을 만든 후 호스트 머신의 실제 위치는 Docker가 결정하며, 이 명령으로 확인합니다:
docker volume inspect n8n_data
# [
# {
# "CreatedAt": "2026-11-02T09:28:41+08:00",
# "Driver": "local",
# "Mountpoint": "/var/lib/docker/volumes/n8n_data/_data",
# "Name": "n8n_data",
# "Scope": "local"
# }
# ]
「Mountpoint」가 바로 호스트 머신에서의 실제 위치입니다. 제这台는 Docker의 데이터 루트 디렉토리를 /data/docker로 옮겼기 때문에 보이는 것은 /data/docker/volumes/n8n_data/_data입니다; 기본 설치된 머신에서는 위의 이 /var/lib/docker/...입니다. 자신의 머신 출력을 기준으로 하세요.
container에 들어가서 보면, n8n이 보는 모습입니다:
docker exec n8n ls -la /home/node/.n8n
# -rw-r--r-- 1 node node 1519616 Nov 2 09:29 database.sqlite
# -rw-r--r-- 1 node node 32768 Nov 2 09:38 database.sqlite-shm
# -rw-r--r-- 1 node node 4136512 Nov 2 09:37 database.sqlite-wal
# -rw-r--r-- 1 node node 7972 Nov 2 09:37 n8nEventLog-1.log
# -rw-r--r-- 1 node node 526 Nov 2 09:38 n8nEventLog.log
workflow, 실행 기록, credential 암호문, 전부 database.sqlite 안에 있습니다. credential 복호화에 필요한 암호화 키는 같은 디렉토리에 있으며, 기본은 config 파일이고, 첫 시작 시 자동 생성됩니다.
따라서 n8n 백업은 두 가지입니다: 이 디렉토리, 그리고 버전 번호. sqlite만 백업하고 키를 백업하지 않으면, 다른 머신에서 복구했을 때 credential이 전부 깨져 나옵니다.
자신이 지정한 호스트 머신 디렉토리(예: /data/n8n)로 바꾸고 싶다면, 를 -v n8n_data:/home/node/.n8n 로 바꾸면 -v /data/n8n:/home/node/.n8n 된다. 이것이 「바인드 마운트」다. 디렉터리는 네가 관리하고 백업이 더 직관적이지만, 대가는 권한을 직접 해결해야 한다는 것—다음 함정이 바로 그것이다.
실제로 마주치는 여섯 가지 실수
1. -v를 잊으면 컨테이너 삭제가 곧 DB 삭제
컨테이너 두 개로 대조 실험을 했다. 같은 머신, 같은 image: -v n8n_data:/home/node/.n8n 가 붙은 쪽은 재시작 후 인터페이스가 반환하는 「최초 설정 표시 여부」가 false이고, -v 가 없는 다른 쪽은 같은 인터페이스가 true를 반환한다—다시 빈 인스턴스로 돌아간 것이다.
전 과정에서 오류도, 경고도 없다. 그저 어느 날 열어보면 workflow가 사라져 있을 뿐.
판별법:docker inspect n8n, 위로 올려 "Mounts" 그 구간을 찾아, 안에 "Destination": "/home/node/.n8n" 가 나타나면 mount된 것이고, 구간 전체가 비어 있으면 위험하다.
2. 호스트 디렉터리를 mount하면 EACCES 발생
바인드 마운트를 쓰려고 디렉터리를 만든 뒤 바로 마운트하면:
Error: Failed to load command "start"
Error: EACCES: permission denied, open '/home/node/.n8n/config'
앞에서는 정상적으로 마이그레이션 로그를 찍고 있었는데 갑자기 여기서 죽었다. 이미지 안의 n8n이 root가 아니라 uid 1000 사용자이기 때문이다 node(docker exec n8n id 볼 수 있다 uid=1000(node)), 그리고 네가 mkdir 만든 디렉터리의 소유자가 root, 755라서 다른 사람이 쓸 수 없다.
소유자만 바꾸면 된다:
chown -R 1000:1000 /data/n8n
바꾼 뒤 다시 실행하면 같은 디렉터리, 같은 명령으로 200, 정상적으로 올라오고 파일도 실제로 호스트 디렉터리에 떨어졌다. 호스트에서 uid 1000이 누구든 상관없고, 컨테이너 안에서는 1000으로 인식된다.
3. 포트가 점유됨
docker: Error response from daemon: failed to set up container networking:
driver failed programming external connectivity on endpoint n8n
(...): failed to bind host port 0.0.0.0:5678/tcp: address already in use
호스트의 5678을 이미 누군가 쓰고 있다는 뜻이며 컨테이너 내부와는 전혀 관계가 없다. 먼저 누가 쓰는지 보자:
ss -ltnp 'sport = :5678'
흔한 경우는 이전 n8n 컨테이너가 아직 돌고 있는 것이고,docker ps 볼 수 있다. 또는 이전 컨테이너를 삭제했지만 깔끔하게 지워지지 않은 경우인데, 포트를 바꿔서 일단 띄우는 게 가장 간단하다.
4. IP + http로 접속하면 로그인 후 다시 로그인 페이지로 돌아감
페이지는 열리고 계정 비밀번호도 맞고 로그인을 눌러도 오류가 없는데 페이지가 로그인 창으로 돌아간다. 비밀번호가 틀린 것처럼 보이지만 실제로는 cookie 문제다.
n8n의 로그인 cookie에는 Secure 플래그가 붙어 있고(N8N_SECURE_COOKIE 기본값은 바로 true), 브라우저는 HTTPS에서만 저장을 허용한다. http로 접근하면 서버는 그대로 200을 반환하지만, 브라우저가 이 cookie를 그냥 버리기 때문에 다음 요청에는 다시 로그인 상태가 없다.
두 가지 방법: 정석은 앞단에 Nginx를 두고 인증서를 붙이는 것; 내부망 임시 디버깅에는 -e N8N_SECURE_COOKIE=false를 추가하고,调试가 끝나면 이 줄을 잊지 말고 삭제한다. 장기 설정으로 두지 마라 — 계정 비밀번호가 평문 링크에서 오가는 것과 같다.
5. 터미널을 닫으면 n8n도 같이 멈춘다
문서의 그 docker run -it --rm 대로 실행했다면 Ctrl+C로 끝난다. 이것은 백그라운드 서비스가 아니라 터미널 안의 포그라운드 프로세스다.
补 -d 다시 실행; 이미 실행 중인 것은docker start n8n 로도 다시 끌어올 수 있지만, 그 container가 이미 --rm 가 삭제되었다면 다시 docker run만 할 수 있다 — 데이터는 volume에 있고, 여전히 남아 있다.
6. latest를 사용했더니 어느 날 동작이 바뀌었다
오류도, 경고도 없었고, 그저 어느 날 재시작 후 node의 기본값, 인터페이스, 심지어 어떤 parameter 이름이 바뀌었다. 원인은 새 버전을 pull했기 때문이다.
버전 번호를 고정하고, 업그레이드할 때는 정식 작업처럼 해라: 먼저 volume을 백업하고, tag를 교체한 뒤, 기동 후 두 개의 핵심 workflow를 실행해 결과를 확인한다. 업그레이드 자체에는 기술적 난이도가 없다. 번거로운 점은 「무엇이 바뀌었는지 모른다」는 것이다.
n8n과 관련된 몇 가지 보안 사항
5678을 공인망에 그대로 노출하지 마세요.n8n에는 로그인 페이지가 있지만 자체 호스팅 애플리케이션이라 로그인 외에 추가 보호가 없고, 스캐너가 매일 이런 port를 찾고 있습니다. 0.0.0.0에 올리면서 reverse proxy도 없고 HTTPS도 없으면 집 열쇠를 문 앞에 걸어두는 것과 같습니다. 제가 테스트할 때 쓴 방식은 -p 127.0.0.1:5678:5678 이미 외부를 막았습니다.
docker group은 root와 같습니다.할 수 있는 사람은 / container에 mount할 수 있는 사람은 호스트의 어떤 파일이든 바꿀 수 있습니다. 동료에게 권한을 주기 전에 이 점을 분명히 생각하세요.
volume은 키처럼 다뤄야 합니다.그 안의 database.sqlite 모든 자격 증명의 암호문이 들어 있고, 같은 디렉터리에 복호화용 키가 있습니다. 이 둘을 함께 복사해 가는 사람이 있으면 모든 서드파티 계정의 token이 평문이 됩니다. 백업을 어디에 둘지는 백업 자체보다 더 중요합니다.
명령줄에 비밀번호를 쓰지 마세요.docker run -e DB_PASSWORD=xxx 남습니다 docker inspect 의 출력에 남아서 누구든 실행하면 볼 수 있습니다. 민감한 값을 전달하려면 env 파일을 쓰거나 _FILE 접미사 변수 방식을 쓰세요.
요약
네 단어만 기억하면 실행할 수 있습니다: image는 패키지, container는 실행 중인 패키지, volume은 패키지 밖의 땅, port는 문패입니다.
n8n 설치에서 진짜 문제가 생기는 곳은 한 군데뿐이다: 볼륨이 마운트되지 않은 경우다. 오류도 나지 않고 바로 알아차리지도 못하는데, 알아차릴 때쯤이면 워크플로가 이미 사라진 뒤다. 그래서 설치 후 첫 번째로 할 일은 docker restart 한 번, 데이터가 아직 있는지 확인하는 것이다.
나머지는 숙련도 문제다. 익숙해지면 Docker의 가장 큰 장점은 「크로스 플랫폼」이 아니라 「문제가 생기면 한 번에 지우고 다시 시작할 수 있다」는 점이라는 걸 알게 된다 — 데이터는 볼륨에 있고, container는 원래 언제든 버릴 수 있다.
이전 글은n8n 시작 오류와 port 점유를 어떻게 확인하는가였고, 거기서는 시작되지 않는 상황을 다뤘다. 전체 시리즈의 목차는여기에 있고, 장애 관련은 따로장애排查페이지에 정리했다.
참고 자료
- Install with Docker: 공식
docker run명령,docker volume create、/home/node/.n8n볼륨 설명, 그리고 image 업데이트 단계 - Docker Compose로 설치: compose 버전의 설정,
5678:5678그 주석에는 「유일하게 외부에 노출해야 하는 port」라고 적혀 있다 - Deployment 환경 변수:
N8N_PORT기본 5678,N8N_LISTEN_ADDRESS기본::、N8N_USER_FOLDER与N8N_ENCRYPTION_KEY - Security 환경 변수:
N8N_SECURE_COOKIE기본true、N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS기본false - Docker volumes: volume은 호스트 머신의
/var/lib/docker/volumes/<名字>/_data, 그리고 그것과 bind mount의 차이 - n8nio/n8n의 tag 목록: 버전마다 tag 하나, image digest와 압축 크기를 확인할 수 있다
- Install Docker Engine on Ubuntu: apt 공식 소스의 전체 단계, 원클릭 스크립트를 사용하고 싶지 않다면 이 방법을 따르세요
최종 업데이트: 2026년 11월 2일, n8n 2.43.7 사용.

