Open WebUI 실무 완벽 가이드 1편 - 아키텍처 이해와 프로덕션 배포(Docker, PostgreSQL, Redis)
최근 사내 AI 도입이나 개인 개발 환경 구축에서 가장 주목받는 오픈소스 프로젝트 중 하나는 단연 Open WebUI입니다. 많은 사람들이 처음에는 “로컬에서 돌리는 ChatGPT 웹 UI” 정도로 접근하지만, 공식 문서를 깊이 들여다보고 실무에 적용해 보면 그 실체는 확장 가능한 자립형 AI 플랫폼(Sovereign AI Platform)이자 에이전트 제어 허브에 가깝습니다.
SERIES Open WebUI 실무 완벽 가이드 1 / 4
- ▶ Open WebUI 실무 완벽 가이드 1편 - 아키텍처 이해와 프로덕션 배포(Docker, PostgreSQL, Redis)
- 2 Open WebUI 실무 완벽 가이드 2편 - 성능과 비용을 잡는 필수 최적화(Task Model, KV Cache, Context Compaction)
- 3 Open WebUI 실무 완벽 가이드 3편 - 엔터프라이즈 RAG 구축과 파일시스템식 검색(KB_EXEC, PGVector, Web Search)
- 4 Open WebUI 실무 완벽 가이드 4편 - 차세대 에이전트 구축(Open Terminal, MCP 연동, Open WebUI Computer)
최근 사내 AI 도입이나 개인 개발 환경 구축에서 가장 주목받는 오픈소스 프로젝트 중 하나는 단연 Open WebUI입니다. 많은 사람들이 처음에는 “로컬에서 돌리는 ChatGPT 웹 UI” 정도로 접근하지만, 공식 문서를 깊이 들여다보고 실무에 적용해 보면 그 실체는 확장 가능한 자립형 AI 플랫폼(Sovereign AI Platform)이자 에이전트 제어 허브에 가깝습니다.
단일 사용자용 토이 프로젝트 수준을 넘어 팀이나 전사 조직에서 안정적으로 운영하려면, 기본 SQLite/ChromaDB 설정의 한계를 이해하고 상태(State)와 연산(Compute)이 분리된 프로덕션 아키텍처로 전환해야 합니다.
본 시리즈는 Open WebUI 공식 문서를 기반으로, 인프라 배포부터 성능 최적화, 엔터프라이즈 RAG, 그리고 Open Terminal/Computer를 활용한 에이전트 확장까지 단계별로 깊이 있게 다룹니다. 그 첫 번째로 Open WebUI의 아키텍처 분석과 고가용성(HA) 프로덕션 배포를 정리합니다.
1. Open WebUI의 내부 아키텍처 분석
Open WebUI는 기본적으로 FastAPI/Uvicorn 비동기 백엔드와 SvelteKit 프론트엔드가 결합된 컨테이너 기반 아키텍처를 가집니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌─────────────────────────────────────┐
│ Client (Browser / PWA) │
└──────────────────┬──────────────────┘
│ HTTP / WebSocket (Socket.IO)
▼
┌─────────────────────────────────────┐
│ Open WebUI Backend (FastAPI) │
│ - Auth & RBAC - Native Tool Call │
│ - Context Engine- Model Router │
└───────┬──────────┬──────────┬───────┘
│ │ │
┌────────────────┘ │ └────────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ App Database │ │ Vector Database │ │ Storage Backend │
│ (Users/Chats) │ │ (RAG Embeddings) │ │ (Files/Artifacts)│
├──────────────────┤ ├──────────────────┤ ├──────────────────┤
│ Default: SQLite │ │ Default: Chroma │ │ Default: Local │
│ Prod: PostgreSQL │ │ Prod: PGVector │ │ Prod: S3 / MinIO │
└──────────────────┘ └──────────────────┘ └──────────────────┘
기본값(Default)의 편리함과 그 한계
처음 docker run 명령어로 실행하면 추가 인프라 없이 즉시 구동됩니다.
- 애플리케이션 데이터: 컨테이너 내부의
webui.db(단일 SQLite 파일) - RAG 벡터 데이터: 내장 SQLite 기반 ChromaDB
- 문서 임베딩: SentenceTransformers (
all-MiniLM-L6-v2, CPU 약 500MB 메모리 상주) - 동시성: 단일 프로세스 Uvicorn 워커
그러나 이 기본 구성은 동시 사용자가 늘어나거나 다중 인스턴스를 띄우는 순간 깨집니다:
- SQLite의 쓰기 락(Lock) 충돌: 여러 워커나 컨테이너가 동시에
webui.db에 쓰기를 시도하면database is locked오류가 발생하고 세션이 유실됩니다. 특히 NFS 등 네트워크 마운트 스토리지 위에 SQLite를 올리면 비동기 I/O 드라이버와 충돌해 데이터가 손상될 위험이 있습니다. - 내장 ChromaDB의 동시성 결여: 멀티 컨테이너 환경에서 상태를 공유할 수 없습니다.
- 웹소켓 세션 분실: 실시간 스트리밍 대화에 사용되는 Socket.IO가 단일 프로세스 메모리에 묶여 있어 부하 분산(Load Balancing) 시 세션이 끊깁니다.
따라서 실무 배포의 첫 단추는 데이터베이스(PostgreSQL), 세션 브로커(Redis), 오브젝트 스토리지(S3/MinIO)를 애플리케이션 컨테이너 밖으로 외주화(Externalize)하는 것입니다.
2. 단계별 배포 실습
2.1 단일 노드 빠른 시작 (평가 및 단일 사용자용)
개발 PC나 단일 홈랩 서버에서 혼자 테스트할 때는 공식 Docker 이미지만으로 충분합니다.
1
2
3
4
5
6
7
8
docker run -d \
-p 3000:8080 \
--add-host=host.docker.internal:host-gateway \
-v open-webui:/app/backend/data \
-e WEBUI_SECRET_KEY="your-super-secure-secret-key" \
--name open-webui \
--restart always \
ghcr.io/open-webui/open-webui:main
--add-host=host.docker.internal:host-gateway: 호스트 머신에서 실행 중인 Ollama(http://host.docker.internal:11434)나 로컬 모델 서비스에 안전하게 접근할 수 있도록 네트워크 브릿지를 엽니다.WEBUI_SECRET_KEY: 세션 쿠키 및 암호화에 사용되는 시크릿 키입니다. 환경변수로 고정하지 않으면 컨테이너 재시작 시마다 세션이 만료되어 재로그인해야 합니다.
(Python 환경이 익숙하다면 uvx open-webui@latest serve 한 줄로도 구동 가능합니다.)
2.2 프로덕션 고가용성(HA) 스케일아웃 아키텍처
팀 단위(10명 이상)나 사내 전사 배포를 준비한다면 무조건 아래 구조로 시작해야 마이그레이션 고통을 겪지 않습니다. Open WebUI는 SQLite에서 PostgreSQL로의 자동 데이터 마이그레이션을 지원하지 않으므로, 프로덕션 데이터를 쌓기 전에 PostgreSQL을 붙여야 합니다.
실무용 docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
services:
# 1. Open WebUI 애플리케이션 레이어 (Stateless)
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui
restart: always
ports:
- "3000:8080"
environment:
- WEBUI_SECRET_KEY=change-this-to-a-very-strong-secret-key-32chars
# [Database] PostgreSQL 외부 연결
- DATABASE_URL=postgresql://webui_user:webui_password@postgres:5432/webui_db
- DATABASE_POOL_SIZE=20
- DATABASE_POOL_MAX_OVERFLOW=10
# [WebSocket / Coordination] Redis 클러스터링
- WEBSOCKET_REDIS_URL=redis://redis:6379/0
# [Storage] S3 호환 스토리지 (MinIO/AWS S3)
- STORAGE_PROVIDER=s3
- S3_ENDPOINT_URL=http://minio:9000
- S3_ACCESS_KEY_ID=minioadmin
- S3_SECRET_ACCESS_KEY=minioadmin
- S3_BUCKET_NAME=openwebui-storage
# [Model Connections] 기본 Ollama 주소
- OLLAMA_BASE_URL=http://host.docker.internal:11434
# [Workers] 워커 수 설정
- WEBUI_WORKERS=4
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
minio:
condition: service_started
# 2. RDBMS 레이어: PostgreSQL
postgres:
image: postgres:16-alpine
container_name: open-webui-postgres
restart: always
environment:
POSTGRES_USER: webui_user
POSTGRES_PASSWORD: webui_password
POSTGRES_DB: webui_db
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U webui_user -d webui_db"]
interval: 5s
timeout: 5s
retries: 5
# 3. 세션/브로커 레이어: Redis
redis:
image: redis:7-alpine
container_name: open-webui-redis
restart: always
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
# 4. 파일 스토리지: MinIO (S3 호환)
minio:
image: minio/minio:latest
container_name: open-webui-minio
restart: always
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
volumes:
- miniodata:/data
volumes:
pgdata:
miniodata:
핵심 설정 포인트
DATABASE_POOL_SIZE&MAX_OVERFLOW: 각 Open WebUI 워커 프로세스가 유지하는 DB 커넥션 수입니다. 전체 DB 커넥션 풀 크기 =(POOL_SIZE + MAX_OVERFLOW) × 워커 수 × 인스턴스 수이므로 PostgreSQL의max_connections한도를 넘지 않도록 맞춰야 합니다.WEBSOCKET_REDIS_URL: 다중 워커(WEBUI_WORKERS > 1)나 Nginx/ALB 뒤에 다중 컨테이너를 배치할 때, 실시간 스트리밍 답변을 보낸 워커와 클라이언트가 연결된 워커가 다를 수 있습니다. Redis를 브로커로 두면 Socket.IO 메시지가 유실 없이 동기화됩니다.STORAGE_PROVIDER=s3: 사용자가 올린 PDF, 이미지, 생성된 아티팩트 파일을 로컬 볼륨 대신 오브젝트 스토리지에 저장하여, 컨테이너가 언제든 파기되고 재생성되어도 파일이 완벽히 보존됩니다.
3. LLM Provider 연결 및 네트워킹
Open WebUI의 모델 연결은 Settings > Admin > Connections에서 관리합니다.
3.1 Ollama (로컬 오픈소스 모델)
- Docker 환경에서의 주소 설정 주의점:
- 호스트에 설치된 Ollama를 연결할 때
http://localhost:11434는 동작하지 않습니다(컨테이너 자신을 가리키기 때문). - 위 Compose의
extra_hosts설정 덕분에http://host.docker.internal:11434로 정확히 호스트 Ollama와 통신할 수 있습니다. - Linux 환경에서 호스트 Ollama가
127.0.0.1에만 바인딩되어 있다면 외부 수신을 거부하므로, 시스템 데몬 설정에서OLLAMA_HOST=0.0.0.0:11434로 변경해 주어야 합니다.
- 호스트에 설치된 Ollama를 연결할 때
3.2 OpenAI 호환 API (vLLM, 클라우드 공급자)
Open WebUI는 모든 “OpenAI 호환 규격”을 네이티브로 지원합니다.
- vLLM / llama.cpp:
http://vllm-host:8000/v1엔드포인트 등록 - 상용 클라우드 (OpenAI, Anthropic, DeepSeek, Gemini):
- 각 공급자의 API Base URL과 API Key를 입력하면 해당 계정의 모델 목록을 실시간으로 가져옵니다.
- 여러 개의 API 키를 파이프(
;또는 콤마)로 등록하여 로테이션 부하 분산도 가능합니다.
4. 보안 및 운영 거버넌스
사내나 외부에 서비스할 때 반드시 점검해야 할 보안 체크리스트입니다.
- 최초 관리자 생성 후 공개 가입 차단:
- 처음 생성된 계정이 최고 관리자(
Admin)가 됩니다. - 관리자 생성 직후
Settings > Admin > General에서 Enable Sign Up을 즉시 해제하거나, 환경변수에 지정합니다:1
ENABLE_SIGNUP=false
- 처음 생성된 계정이 최고 관리자(
- 엔터프라이즈 인증 연동 (OAuth / OIDC / LDAP):
- 사내 Keycloak, Okta, Google Workspace, GitHub SSO와 연동하려면 OIDC 환경변수를 구성합니다:
1 2 3 4 5
ENABLE_OAUTH_SIGNUP=true OAUTH_MERGE_ACCOUNTS_BY_EMAIL=true OPENID_PROVIDER_URL=https://auth.yourcompany.com/realms/master OPENID_CLIENT_ID=open-webui OPENID_CLIENT_SECRET=your-client-secret
- 사내 Keycloak, Okta, Google Workspace, GitHub SSO와 연동하려면 OIDC 환경변수를 구성합니다:
- 폐쇄망(Air-gapped) 환경 지원:
- Open WebUI는 공식적으로 에어갭(외부 인터넷 차단 환경) 운영을 지원합니다.
- 폰트, 아이콘 등 프론트엔드 정적 번들이 이미지 내부에 모두 패키징되어 있어 외부 CDN 의존성이 없습니다.
- RAG 임베딩 엔진만 외부 다운로드가 발생하지 않도록 Ollama 로컬 모델로 사전에 지정해 주면 완전한 독립 운영이 가능합니다.
요약 및 다음 편 예고
- Open WebUI는 단일 컨테이너로 가볍게 시작할 수 있지만, 멀티 유저 환경에서는 PostgreSQL + Redis + S3 기반의 무상태 스케일아웃 아키텍처가 필수적입니다.
- 데이터베이스 외주화(
DATABASE_URL)를 초기 배포 단계에서 설정해 두어야 추후 데이터 손실 없는 확장이 가능합니다.
다음 2편에서는 Open WebUI를 쓰면서 흔히 겪는 UI 버벅임과 불필요한 API 비용 누수를 잡는 최적화 기법을 다룹니다.
- 사이드바 제목/태그 생성과 자동완성에 메인 모델 대신 가벼운 전용 모델을 붙이는 Task Model 분리 기법
- 공급자별 캐시 무효화를 방지하는 Prompt Caching (KV Cache) 최적화
- 긴 대화에서 토큰 초과 에러를 막는 Context Compaction (
/compact)
// reading compass
이 글과 이어지는 경로
시리즈, 카테고리, 태그 겹침, 최신도를 점수화해 가까운 글일수록 중심에 배치합니다.
댓글남기기