Ubuntu + Cloudflared + Dify.ai - 셀프호스트 AI 환경 구축기
1. 개인화된 인공지능 욕심
인공지능을 만지작거리다 보니 자연스럽게 욕심이 생겼다.
대부분의 인공지능 사용자가 같은 마음 같다. 자신만의 자료를 중심으로 LLM을 활용하고 싶은 마음이다.
결국 이 욕심은 ‘셀프호스트(Self-host)’의 방향으로 이어진다. 즉, 클라우드 서비스가 아닌 내가 직접 운영하는 서버 위에 나만의 인공지능을 띄워두는 것이다.
이런 셀프호스트 환경에서는 최근 노코드(No-code) 기반의 AI 오케스트레이션 툴들이 하나둘 나오고 있다. 대표적으로 Flowise, Dify, n8n 같은 서비스들이다.
이들은 모두 복잡한 코딩 없이도 LLM을 중심으로 여러 API, 데이터 소스, 외부 서비스 등을 연결해 자동화된 AI 워크플로를 시각적으로 구성할 수 있는 플랫폼이라 할 수 있다.
여러 서비스 중 ‘디피(Dify)’를 선택했다.
단순한 챗봇이 아니라 플랫폼 단위의 확장성을 갖고 있었기 때문이다. 단일 모델을 연결하는 수준을 넘어, 워크플로와 에이전트 구조까지 직접 설계할 수 있다는 점이 매력적이다.
2. 미니PC 위에서 실험해보기
집에 남아있던 미니PC 꺼냈다. 환경은 쨔잔
호스트: Proxmox VM (ubuntu-vm, 192.168.0.??? / Ubuntu Server 24.04)
Docker: 28.x / Compose v2
성능 n100 CPU 2g, RAM 8g, SSD 128gb -> 가상pc에 성능을 통제하였다.
외부 노출: Cloudflare Tunnel(Zero Trust Web UI 관리)
환경은 사용자마다 다를 수 있으므로, 여기서는 Ubuntu 기반이라는 것만 보면된다.
구글클라우드든, 아마존이든, 오라클이든 우분투 + 도커 기반이면 동일하게 작동할것이다.
아래 작업에 핵심은 복잡한 SSL 설정이나 DNS 레코드 작업 없이 Cloudflare Tunnel을 통해 외부에서 안전하게 접근하는 방법에 있다.
3. Cloudflare Tunnel
보통 도메인을 연결하려면 DNS 설정, SSL 인증서 발급, 방화벽 개방 등 여러 단계를 거쳐야 한다. 하지만 Cloudflare Tunnel을 사용하면 이런 복잡한 과정을 단 한 번의 터널 연결로 해결할 수 있다.
자세한 내용은 Cloudflare Tunnel 소개를 보자
문제는 이 부분이다.
Dify의 공식 Docker Compose 배포본은 이미 SSL 설정을 포함하고 있어, Cloudflare Tunnel을 사용할 경우 예외 처리가 필요하다. 즉, Docker Compose의 기본 구조를 그대로 쓰면 SSL이 중복 처리되어 오류가 발생할 수 있다.
4. 설치 과정 요약
1) 공식 레포 받아 설치
1-1. 작업 디렉터리 만들기
소유자는 User로 예시표기하였고, 위치는 /data폴더 안에 apps 폴더를 만들고 그 안에 컨테이너들을 넣으려한다.
sudo mkdir -p /data/apps
sudo chown -R zahir:zahir /data/apps
1-2. Dify 공식 레포 클론
아래와 같이 하면 apps폴더 아래 /dify 폴더가 생기면서 공식 레포가 다운받아질 것이다.
(추후 규정이 바뀌어 dify 폴더 아래 다운로드가 안된다면 dify폴더 만들고 작업할것. 현재 /data/apps -> /data/apps/dify)
cd /data/apps/
git clone https://github.com/langgenius/dify.git
1-3. docker 디렉터리로 이동
cd /data/apps/dify/docker
1-4. env 템플릿 복사 및 편집
cp .env.example .env
nano .env
아래 3가지를 점검, 수정한다
nano 에서 찾기는 (ctrl (cmd) + w)
- 내부 Nginx는 항상 80으로 듣게 한다.
(단, 이 부분은 아래설명과 같이 dify 서버가 cloudflare tunnel 을 사용할때에 한함)NGINX_HTTPS_ENABLED=false
NGINX_PORT=80 - 비밀번호 2개를 직접 넣는다(영문 대소문자/숫자/특수문자 섞어서 길게).
POSTGRES_PASSWORD=여기에_강력한_DB_비밀번호
REDIS_PASSWORD=여기에_강력한_REDIS_비밀번호 - CONSOLE머시기, APP머시기, SERVICE머시기로 시작하는 URL들이 있다. 이 항목들은 현재와 같이 비워둔다.
- 저장한다 : ctrl+o -> ctrl+x
1-5. 이제 docker-compose.yaml을 수정하자 (도커 설치의 공식가이드 부분 수정)
(위 폴더와 동일한 폴더다 /data/apps/dify/docker)
nano docker-compose.yaml
- 찾기: Ctrl + W → services: 아래의 nginx: 블록으로 이동
- ports: 항목에 아래 한 줄만 남기고, 443 매핑이나 다른 매핑은 모두 주석 처리/삭제한다.
ports:
- "18080:80"
# - "443:443" ← 사용X, 주석처리 (Cloudflare가 TLS 처리)
# - "80:${NGINX_PORT}" ← 사용X, 주석처리
- 동일하게 저장하고 닫기
1-6. 컨테이너를 올린다.
docker compose up -d
docker compose ps

정상 예시(참고): docker-nginx-1에 0.0.0.0:18080->80/tcp 표시가 보여야 한다.
2) Cloudflare Tunnel 설정 반영
Cloudflare Zero Trust 대시보드에서 터널을 이미 있다고 가정했을때 이제 Public Hostname을 추가해 Dify를 외부에 연결한다.
- Cloudflare 대시보드 → Zero Trust → Tunnels → 만들어둔 터널 선택
- Public Hostnames → Add a public hostname
- 값 입력
– Subdomain : dify (혹은 원하는것)– Domain : 소유한 도메인– Type : HTTP (로컬이므로)– URL : http://localhost:18080 - 저장
3) 서비스 가동 확인
로컬에서 확인하기
curl -v http://localhost:18080
정상이라면 HTML 어쩌구 저쩌구가 나올것이다.

브라우저에서 접속하면 dify초기 페이지가 보여야한다.
- 내부망 : http://192.168.0.100:18080
- 외부 : https://서브도메인.보유도메인.com
TLS/인증서는 Cloudflare가 처리. 서버는 HTTP만 처리
컨테이너의 상태 재확인
설치한 우분투에서 명령어를 입력할경우
docker compose ps
핵심 컨테이너들은 아래 포트에 떠있고 Status는 “up” 상태입니다.
- docker-nginx-1 : 0.0.0.0:18080->80/tcp
- docker-web-1 : 3000/tcp (내부)
- docker-api-1 : 5001/tcp (내부)
- docker-db-1 : 5432/tcp (healthy)
- docker-redis-1 : 6379/tcp (healthy)
5. 뻘글 마무리
거창하게 썼지만, Dify.ai의 공식 레포와 docker-compose를 사용한 것뿐이고 Cloudflare Tunnel에 맞춰 일부를 수정한 것뿐이다.
이걸 몰라서 내 네트워크 환경에서 몇 번이나 도커 컨테이너를 올리는 데 실패했기 때문에 까먹지 말자고 기록으로 남긴다.
문제의 대부분은 내부 포트와 외부 매핑 문제에서 비롯되었다.
기억해야 할 원칙은 하나다.
내부 서비스 포트는 항상 80(기본값)으로 두고, 외부 노출만 Docker Compose에서 매핑하는 것
이제 dify.ai 코뮤니티에디숀으로 다양한 삽질을 해보자.
