n8n을 Zeabur에서 VPS로 이전: encryption key를 안 적어 두면 credentials가 전부 날아간다 (테스트 완료)

n8n 이전에서 가장 사고 나기 쉬운 부분은 N8N_ENCRYPTION_KEY입니다. 테스트해 보니 잘못된 키로 가져와도 n8n은 성공이라고 표시했습니다. 키 찾는 법, 내보내기와 가져오기, 바로 쓸 수 있는 Docker Compose와 Caddy 설정을 정리했습니다.

핵심 요약

  • n8n의 credentials는 encryption key로 암호화된다. 키가 바뀌면 전부 복호화할 수 없다.
  • 키는 환경 변수 N8N_ENCRYPTION_KEY 또는 n8n 폴더 안의 config 파일에 있다.
  • 테스트 결과: 잘못된 키로 가져와도 성공이라고 나온다. export:credentials --decrypted로 꼭 확인할 것.
  • 바로 쓸 수 있는 Docker Compose + Caddy 설정(HTTPS 자동) 포함.
이 글은 어떻게 검증했나요? (직접 테스트 4개, 공식 문서 기준 3개)
직접 테스트한 내용
  • 공식 n8n 2.43.7 이미지: export:workflow, export:credentials로 내보내고 새 n8n으로 가져오기
  • 같은 encryption key면 credentials 복호화 성공. 다른 키면 가져오기는 성공으로 나오지만 나중에 복호화 실패
  • 환경 변수가 없으면 n8n이 키를 자동 생성해 /home/node/.n8n/config에 기록
  • 이 글의 docker-compose.yml과 Caddyfile: 로컬에서 n8n.localhost로 띄워 Caddy를 거친 HTTPS로 n8n에 접속, healthz 200 응답
공식 문서를 바탕으로 작성 (직접 실행하지 않음)
  • Zeabur 대시보드에서 명령어 실행과 파일 내려받기: Zeabur 공식 문서(영어)를 바탕으로 작성했으며 Zeabur에서 직접 조작하지 않았습니다
  • Zeabur n8n 템플릿의 데이터 저장 위치, SQLite와 PostgreSQL 중 무엇을 쓰는지: 각자 서비스 설정을 확인하세요
  • 실제 도메인에서 Let's Encrypt 인증서 자동 발급: 로컬 테스트는 Caddy 자체 서명 인증서를 썼으며, 운영 환경 동작은 Caddy 공식 문서를 따릅니다
목차 (6개 섹션)
  1. 가장 중요한 한 가지: encryption key 찾기
  2. 1단계: Zeabur에서 내보내기
  3. 2단계: VPS에 새 n8n 구축
  4. 3단계: 가져오기
  5. 4단계: credentials가 정말 복호화되는지 검증
  6. 5단계: 마무리

가장 중요한 한 가지: encryption key 찾기

n8n이 저장하는 모든 credentials(API 키, OAuth 토큰, 데이터베이스 비밀번호)는 하나의 encryption key로 암호화됩니다. 다른 컴퓨터로 옮겨서 키가 달라지면 credentials를 하나도 복호화할 수 없고, 전부 다시 설정해야 합니다.

키는 다음 두 곳 중 하나에 있습니다.

  1. 환경 변수 N8N_ENCRYPTION_KEY: Zeabur 서비스의 Variables 탭에서 확인하세요
  2. n8n 폴더 안의 config 파일: 환경 변수가 없으면 n8n이 처음 시작할 때 키를 자동으로 만들어 이 파일에 기록합니다. 공식 이미지에서의 경로는 /home/node/.n8n/config이고 내용은 다음과 같습니다.
{
	"encryptionKey": "무작위 문자열"
}

키를 적어서 비밀번호 관리자에 보관하세요.

테스트에서 발견한 함정: 잘못된 키로 가져와도 n8n은 오류를 내지 않는다.

KEY_A로 내보낸 credentials를 KEY_B로 설정한 n8n에 가져왔더니 Successfully imported 1 credential.이라고 나오며 아무 문제 없어 보였습니다. 실제로 쓰려고 할 때가 되어서야 다음 오류가 나왔습니다.

Credentials could not be decrypted. The likely reason is that a different "encryptionKey" was used to encrypt the data.

그러니 이전 후에는 꼭 아래 ‘4단계’ 검증을 하세요. 가져오기 성공 메시지를 믿으면 안 됩니다.

1단계: Zeabur에서 내보내기

Zeabur 문서에 따르면 서비스의 Overview 탭에 있는 Command 영역에서 컨테이너 안에서 명령어를 실행할 수 있습니다. workflow와 credentials를 n8n 폴더로 내보냅니다.

n8n export:workflow --all --output=/home/node/.n8n/workflows.json
n8n export:credentials --all --output=/home/node/.n8n/credentials.json

같은 탭의 Files 영역에서 컨테이너 안의 파일을 둘러보고 내려받을 수 있습니다. 이 두 파일과 config를 내려받으세요.

내보낸 credentials.json은 암호화되어 있어서 열어 봐도 비밀번호를 읽을 수 없습니다. 테스트에서 설정해 둔 SECRET123이라는 값은 파일 어디에서도 찾을 수 없었습니다. 그래서 원래 키와 함께 있어야만 쓸모가 있습니다.

다른 방법: 볼륨을 통째로 백업하기. n8n이 내장 SQLite를 쓴다면(폴더에 database.sqlite가 있음), Zeabur Backup 탭의 오프라인 백업으로 폴더 전체를 내려받을 수 있습니다. Zeabur 문서상 먼저 서비스를 일시 중지해야 하는데(Settings → Suspend Service), 이게 맞는 방법입니다. 실행 중인 SQLite에는 -wal 파일이 있어서 멈추지 않고 복사하면 데이터가 불완전할 수 있습니다.

2단계: VPS에 새 n8n 구축

Ubuntu VPS를 만들어 Docker를 설치하고, 도메인(예: n8n.example.com)의 A 레코드를 VPS IP로 지정한 다음, 다음 두 파일을 만듭니다.

# docker-compose.yml
services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: unless-stopped
    environment:
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}   # Zeabur에서 쓰던 키를 그대로 사용
      - N8N_HOST=n8n.example.com
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.example.com/
    volumes:
      - n8n_data:/home/node/.n8n
  caddy:
    image: caddy:2
    restart: unless-stopped
    ports: ["80:80", "443:443"]
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
volumes:
  n8n_data:
  caddy_data:
# Caddyfile
n8n.example.com {
  reverse_proxy n8n:5678
}
echo "N8N_ENCRYPTION_KEY=Zeabur에서 적어 온 키" > .env
docker compose up -d

DNS가 이 VPS를 가리키고 80, 443 포트가 열려 있으면 Caddy가 HTTPS 인증서를 자동으로 발급받습니다. n8n을 리버스 프록시 뒤에서 돌릴 때는 n8n 문서에 따라 WEBHOOK_URL을 설정해야 n8n이 만드는 Webhook URL이 올바른 공개 주소가 됩니다.

3단계: 가져오기

docker compose cp workflows.json n8n:/tmp/
docker compose cp credentials.json n8n:/tmp/
docker compose exec n8n n8n import:workflow --input=/tmp/workflows.json
docker compose exec n8n n8n import:credentials --input=/tmp/credentials.json

4단계: credentials가 정말 복호화되는지 검증

docker compose exec n8n n8n export:credentials --all --decrypted --output=/tmp/check.json
docker compose exec n8n cat /tmp/check.json
  • 평문 내용이 보인다: 키가 맞음
  • Credentials could not be decrypted가 나온다: 키가 틀림, 첫 번째 섹션으로 돌아가 확인

확인한 뒤에는 바로 파일을 지우세요. 평문 비밀번호가 들어 있습니다.

docker compose exec n8n rm /tmp/check.json /tmp/credentials.json

5단계: 마무리

  • workflow를 하나씩 열어 트리거를 확인하고, Webhook으로 실행되는 것은 상대 서비스(LINE, Stripe, 폼 도구)에서 URL을 새 주소로 바꾸기
  • Zeabur에서 보안 사고가 있었으니 credentials에 저장된 외부 API 키는 모두 교체하기를 권합니다(보안 편 참고)
  • 매일 백업 설정하기: 최소한 매일 n8n_data 볼륨을 압축해 다른 곳에 보관

Dify, LobeChat처럼 여러 서비스로 이루어진 도구는 공식 저장소의 docker-compose.yml을 쓰고 같은 방식으로 Caddy를 추가하면 됩니다. 데이터베이스 이전은 데이터베이스 편을 참고하세요.

자주 묻는 질문

n8n의 encryption key는 어디에 있나요?

환경 변수 N8N_ENCRYPTION_KEY가 설정되어 있다면 거기에 있습니다. 설정되어 있지 않다면 n8n이 처음 시작할 때 키를 자동으로 만들어 n8n 폴더의 config 파일(공식 이미지에서는 /home/node/.n8n/config)에 기록합니다.

credentials 가져오기가 성공했다는데 왜 쓸 수 없나요?

encryption key가 다르기 때문입니다. n8n은 가져올 때 키가 맞는지 확인하지 않고, 실제로 쓸 때가 되어서야 Credentials could not be decrypted 오류가 납니다. 원래 키로 다시 가져오세요.