n8n 셀프호스팅 방법: Coolify 서버에서 직접 운영하며 겪은 문제
n8n 셀프호스팅 방법으로 오라클 Arm 서버에서 월 0원으로 돌립니다(제 계정 기준). 설치 순서, 프록시 뒤에 꼭 넣을 WEBHOOK_URL과 타임존, 실제 워크플로우 3개, 이전 때 credential이 끊긴 경험을 정리했습니다.
목차
Next.js를 Coolify로 배포한 글에서 같은 서버에 n8n이 이미 돌고 있다고 짧게 적었습니다. 이번에는 그 n8n 이야기입니다. 개인용 서브도메인에 붙여 두고 반복 업무를 하나씩 워크플로우로 옮기고 있습니다.
서버에 이미 있던 n8n을 골랐습니다#
Zapier 같은 서비스는 실행 횟수(task) 단위로 과금합니다. 자동화를 많이 돌릴수록 요금이 오릅니다. 폴링 주기를 짧게 잡거나 시험 삼아 워크플로우를 늘리면 바로 요금을 계산하게 됩니다.
n8n을 직접 호스팅하면 이 계산이 없어집니다.
- 실행 횟수 제한이 없습니다. 5분마다 도는 스케줄 트리거를 몇 개 만들어도 추가 비용이 없습니다.
- 서버가 이미 있었습니다. Coolify에서 다른 앱들이 돌고 있어서 n8n을 하나 더 올리는 비용도 거의 없습니다.
- credential이 내 서버에 남습니다. Slack 봇 토큰과 API 키를 제3자 SaaS에 맡기지 않아도 됩니다.
서버 사양과 비용#
n8n이 도는 서버는 오라클 클라우드 춘천 리전(ap-chuncheon-1)의 인스턴스 1대입니다. 사양은 2026년 10월 18일 콘솔에서 확인했습니다.
| 항목 | 값 |
|---|---|
| 셰이프 | VM.Standard.A1.Flex (Arm) |
| OCPU | 4 |
| 메모리 | 24GB |
| 부트 볼륨 | 200GB |
| 네트워크 대역폭 | 4Gbps |
| OS | Ubuntu 22.04 (aarch64) |
이 서버 한 대에서 Coolify로 n8n과 다른 서비스 약 20개를 같이 돌립니다. n8n은 메인 컨테이너와 Task Runners 컨테이너 두 개로 돌고, 이미지 태그는 2.10.2입니다.
제 계정 기준으로 이 서버와 n8n에 나가는 월 청구액은 0원입니다(2026년 10월 확인).
다만 새로 서버를 만든다면 같은 결과를 기대하기 전에 확인할 것이 있습니다. 현재 오라클 공식 문서의 Always Free 무료분은 이 서버 사양보다 작습니다(Oracle Cloud Always Free Resources).
- A1 Flex는 매달 1,500 OCPU 시간과 9,000 GB 시간이 무료이고, 문서는 이것이 2 OCPU와 메모리 12GB에 해당한다고 적습니다.
- 블록 스토리지는 부트 볼륨과 블록 볼륨을 합쳐 200GB가 무료입니다.
- 무료 자원은 테넌시의 홈 리전에서 만들어야 합니다.
인스턴스를 만들기 전에 이 문서의 한도를 읽고, 만든 뒤에는 자기 계정의 청구서를 확인하세요.
대신 매니지드 서비스가 해 주던 백업, 인증, 업데이트를 직접 챙겨야 합니다. 이 부분은 뒤에서 다룹니다.
Coolify에 n8n을 설치하는 순서#
설치는 Coolify에 서비스로 추가하고 도메인을 연결하면 끝납니다. HTTPS는 프록시가 처리합니다. 외부 생존 확인은 같은 서버의 Uptime Kuma에 모니터를 하나 추가했습니다. Uptime Kuma 설정은 모니터링 글에, 서버 한 대에 서브도메인을 여러 개 붙인 구성은 Coolify 구성 글에 있습니다.
까다로운 점은 서브도메인과 프록시 뒤라는 환경입니다. n8n은 자기 주소를 알아야 하는 앱입니다. 환경변수를 명시하지 않으면 에러 없이 엉뚱한 곳에서 어긋납니다.
N8N_HOST=automation.example.com
N8N_PROTOCOL=https
WEBHOOK_URL=https://automation.example.com/
GENERIC_TIMEZONE=Asia/SeoulWEBHOOK_URL은 외부 서비스가 콜백을 보낼 주소입니다. 이 값이 비면 n8n은 내부 주소로 webhook URL을 만듭니다. 그러면 워크플로우는 활성 상태인데 트리거가 들어오지 않습니다. 에러 로그도 남지 않습니다.GENERIC_TIMEZONE도 명시합니다. 스케줄 트리거를 금요일 09:00으로 잡아도 UTC로 돌면 한국 시간 18:00에 알림이 갑니다.
실제로 돌리는 워크플로우#
에러 알림 워크플로우가 다른 워크플로우를 감시합니다#
가장 먼저 만든 워크플로우입니다. n8n의 Error Trigger 노드는 다른 워크플로우가 실패하면 실행됩니다. 여기에 Code 노드로 메시지를 만들어 Slack으로 보냅니다.
// Error Trigger 뒤 Code 노드
const w = $json.workflow || {};
const ex = $json.execution || {};
const errMsg = String(ex.error?.message || '(no message)').slice(0, 800);
const text = [
'*n8n 워크플로우 실패*',
`*워크플로우:* ${w.name || 'unknown'}`,
`*실패 노드:* ${ex.lastNodeExecuted || 'unknown'}`,
`*에러:*\n\`\`\`${errMsg}\`\`\``,
].join('\n');
return [{ json: { text } }];셀프호스팅 자동화에서 최악의 경우는 돌고 있는 줄 알았던 워크플로우가 3주 전에 멈춰 있던 경우입니다. 이 워크플로우가 그런 경우를 막습니다.
머지된 브랜치를 매주 알립니다#
매주 금요일 09:00에 git 저장소를 확인합니다. main에 머지됐는데 지워지지 않은 브랜치가 있으면 마지막 커밋 작성자를 멘션해 Slack으로 알립니다. 노드는 이 순서로 연결합니다.
- Schedule Trigger
- Set(설정값)
- HTTP Request
- Code(판별과 메시지 작성)
- IF
- Slack
대상이 없으면 IF에서 알림 없이 끝냅니다.
원칙이 하나 있습니다. 자동화는 알림까지만 하고 삭제는 사람이 합니다. 브랜치를 자동으로 지우면 편하겠지만, 되돌릴 수 없는 동작이 사람 없이 돌기 시작하면 자동화가 위험 요소가 됩니다.
회의 녹취를 위키 회의록으로 올립니다#
회의 녹취록을 정리해 위키 페이지로 올리는 워크플로우도 돌립니다. 여기서는 위키 API가 429(rate limit)를 돌려줬습니다. HTTP Request 노드에 retryOnFail을 켜고 maxTries 5, 재시도 간격 5초로 잡은 뒤로는 안정적으로 돕니다. 외부 API를 연달아 부르는 워크플로우라면 재시도 설정을 처음부터 넣는 것이 좋습니다.
메모를 AI로 분류하고 백링크를 거는 개인 위키 파이프라인은 Obsidian 글에 따로 적었습니다.
워크플로우도 git으로 관리합니다#
n8n 에디터에서 만든 워크플로우는 JSON으로 내보낼 수 있습니다. 이것을 저장소에 커밋해 버전을 관리합니다.
workflows/
error-notifier/workflow.n8n.json
stale-branch-reminder/workflow.n8n.json
meeting-to-wiki/workflow.n8n.json
docs/
intent.md # 각 워크플로우의 이유, 제약, 완료 조건규칙은 두 가지입니다.
- 시크릿은 n8n credential로만 관리합니다. 워크플로우 JSON에 토큰을 직접 넣지 않습니다. Code 노드에 API 키를 적으면 그 JSON은 커밋할 수 없는 파일이 됩니다.
- 결정한 이유를 같이 남깁니다. 자동 삭제 대신 알림을 고른 이유 같은 결정은 JSON만 봐서는 알 수 없습니다. 그래서
docs/intent.md에 적습니다.
워크플로우 JSON을 내보내면 credential은 이름만 남고 시크릿은 빠집니다. 그래서 git에 올려도 안전합니다. 하지만 백업으로는 이 특성이 함정이 됩니다.
셀프호스팅에서 직접 챙길 것은 백업, 인증, 업데이트입니다#
백업#
워크플로우 JSON을 git에 커밋해도 백업은 끝나지 않습니다. credential과 실행 이력은 n8n DB에만 있습니다. 인스턴스를 통째로 옮긴 적이 있습니다. 워크플로우 JSON은 REST API로 뽑아 한꺼번에 가져오면 됐습니다. 하지만 credential은 모두 끊긴 채로 들어와서 하나씩 다시 연결해야 했습니다. 워크플로우가 수십 개면 이 작업이 이전 시간의 대부분을 차지합니다. 데이터 볼륨 백업은 git과 따로 챙겨야 합니다.
인증#
셀프호스팅 n8n은 공개 인터넷에 노출된 관리 화면입니다. owner 계정 비밀번호를 강하게 정하고 불필요한 가입을 막아야 합니다. webhook URL은 인증 없이 열려 있다는 점도 기억해야 합니다. URL 경로가 곧 비밀값이므로 추측하기 쉬운 경로를 쓰면 안 됩니다.
업데이트#
n8n은 릴리스 주기가 빠르고, 마이너 버전에서도 노드 동작이 바뀌기도 합니다. 그래서 latest를 자동으로 따라가지 않고 버전을 고정했습니다. 백업을 확인한 뒤 직접 올립니다. 업데이트 직후 에러 알림 워크플로우가 조용한지 확인하는 것까지가 한 번의 업데이트입니다.
직접 운영하며 정한 규칙#
- 첫 워크플로우는 에러 알림으로 만듭니다. 감시하지 않는 자동화는 멈춘 지 3주가 지나도 모릅니다.
- 내보낸 JSON에는 시크릿이 없습니다. git 커밋에는 안전하지만 백업으로는 모자랍니다. credential은 따로 백업합니다.
- 프록시 뒤의 n8n에는
WEBHOOK_URL과 타임존을 명시합니다. 빠뜨리면 에러 없이 어긋납니다. - 되돌릴 수 없는 동작은 자동화에 넣지 않습니다. 알림까지 자동으로 하고 실행은 사람이 합니다.
- 실행 횟수를 신경 쓰지 않게 되면 설계가 달라집니다. 폴링을 짧게 잡고, 시험용 워크플로우를 부담 없이 만듭니다.
Zapier였다면 요금 명세서를 보며 워크플로우를 줄였을 겁니다. 지금은 서버 한 대에서 워크플로우를 늘려 가고 있습니다. 반복 업무를 하나 옮길 때마다 커밋이 하나 남습니다.
Slack 멘션 알림을 macOS에서 처리한 다른 자동화는 Slack 커스텀 알림음 글에 있습니다. 에이전트와 카드로 일을 주고받는 주간 보드는 Plan Wall에서 쓸 수 있습니다.