Open WebUI 직접 설치 후기 — 로컬 LLM을 ChatGPT처럼 쓸 수 있나

Open WebUI 0.11.4를 실제 로컬 환경에 설치해 Ollama 연동, 모델 목록, 채팅 응답까지 검증했다. 포스트에 나오는 docker run 명령 그대로는 연동이 깨지고, 별도 설정이 필요했다.
마크다운 원문·보충·정정할 내용이 있나요?

Open WebUI는 결론부터 말하면 설치 가치가 충분합니다. 다만 검색하면 나오는 "단 한 줄 docker run"은 그대로 복사하면 안 됩니다. 이 글이 쓰는 모든 수치는 jw-ms7c94라는 Ubuntu 25.10 환경에서 직접 실행해 얻은 값이며, 다른 머신에서는 다를 수 있습니다.

먼저 결론부터 정리

  • 설치는 10분 컷. 이미지 pull 포함 6분 내 걸렸습니다.
  • UI는 확실히 터미널보다 낫습니다. 마크다운·코드 하이라이팅·대화 이어가기·다중 모델 비교가 특히 그렇습니다.
  • 그런데 포스트가 알려주는 기본 docker run 명령은 그대로 쓰면 안 됩니다. 두 가지 설정이 빠져 있고, 그 상태로 두면 Ollama 모델이 0개로 뜹니다.
  • 저사양 PC에서도 구동은 됩니다. 이 환경은 16코어·RAM 31Gi인데, 컨테이너 메모리는 660MiB만 먹었습니다. 이미지가 4.62GB인 게 유일한 체감 부담입니다.

1. 환경 조건

항목값
호스트jw-ms7c94 (Ubuntu 25.10, 커널 6.17.0-41-generic)
CPU / RAM16코어 / 31Gi
GPUNVIDIA RTX 3070 8GB
Docker브리지 네트워크 기본 (기존 컨테이너 5개 운영 중)
Ollama0.33.3, GPU 정상 인식, 로컬 모델 11개
Open WebUI0.11.4 (이미지 빌드 2026-09-21)

모델 목록에는 qwen3.8-4b-q6k, supergemma-e4b-q4km-32k, ghunghab/qwen3.8-9b-distill, 임베딩용으로 nomic-embed-text와 bge-m3 등이 들어 있었습니다.


2. 이 글이 틀린 부분: host.docker.internal이 안 된다

포스트의 방법 1은 이렇게 되어 있습니다.


docker run -d -p 3000:8080 \
  --add-host=host.docker.internal:host-gateway \
  -v open-webui:/app/backend/data \
  --name open-webui \
  --restart always \
  ghcr.io/open-webui/open-webui:main

이 그대로 실행하면 컨테이너는 뜹니다. healthcheck도 eventually healthy가 됩니다. HTTP 200도 받습니다.

그런데 모델 목록이 0개입니다.

로그를 보면 이 한 줄이 전부입니다.


ERROR | open_webui.routers.ollama:send_get_request:93 - Connection error: Cannot connect to host host.docker.internal:11434 ssl:default [Name or service not known]

--add-host=host.docker.internal:host-gateway가 /etc/hosts에 이름을 심어야 하는데, 컨테이너 안에서는 여전히 "이름을 알 수 없다"고 합니다. --network host가 아닌 상태에서 host-gateway가 제대로 해석되지 않는 환경입니다. 컨테이너 안에서 직접 확인해봤습니다.


$ docker exec open-webui curl -s -m 5 http://host.docker.internal:11434/api/tags
Connection timed out after 5000 milliseconds
$ docker exec open-webui curl -s -m 5 http://172.17.0.1:11434/api/tags
Connection timed out after 5000 milliseconds

게다가 이 환경의 Ollama는 127.0.0.1:11434에만 바인딩돼 있었습니다. 컨테이너에서 보는 172.17.0.1(docker0 게이트웨이)로는 당연히 닿지 않습니다. 방화벽도 11434가 외부 인터페이스에 대해 닫혀 있었습니다(ufw 활성화 상태).

즉 이름 해석 실패 + 바인딩 주소 제한 + 방화벽, 세 겹이 겹친 것입니다. 포스트는 이 조합을 언급하지 않습니다.

해결책: --network host

이름 조회를 아예 없애는 게 가장 확실했습니다.


docker run -d --network host \
  -e OLLAMA_BASE_URL=http://127.0.0.1:11434 \
  -v open-webui:/app/backend/data \
  --name open-webui \
  --restart always \
  ghcr.io/open-webui/open-webui:main

--network host를 쓰면 컨테이너가 호스트 네트워크를 공유하므로 127.0.0.1이 그대로 호스트를 가리킵니다. --add-host가 필요 없어집니다.

대신 포트가 host의 8080을 그대로 씁니다. -p 옵션이 무시되므로, 그 포트를 다른 것이 이미 쓰고 있지 않은지 먼저 확인해야 합니다.

이 환경은 이미 3000번을 다른 컨테이너가 갖고 있어서 처음엔 3001번으로 붙였는데, 결국 host 네트워크로 바꾸면서 8080이 비어 있는 것을 확인하고 그대로 썼습니다.

주의: DB에 값이 남는다

한 가지 함정입니다. OLLAMA_BASE_URL을 환경변수로 줬는데도 로그가 여전히 host.docker.internal을 보는 순간이 있습니다.

원인은 SQLite에 설정이 저장돼 있기 때문입니다. 컨테이너 안에서 직접 확인했습니다.


$ docker exec open-webui python -c "...select * from config where key like '%ollama%'"
('ollama.base_urls', '["http://host.docker.internal:11434"]', 1791166799)
('rag.ollama.base_url', '"http://host.docker.internal:11434"', 1791166799)

볼륨 open-webui이 살아 있는 한 이 값이 환경변수를 덮습니다. 처음부터 올바른 값으로 띄우면 문제는 없지만, 이미 잘못된 상태로 한 번 띄웠다면 볼륨을 지우거나 값을 직접 고쳐야 합니다.

저는 DB를 직접 고쳤습니다(설정 화면에서는 405 Method Not Allowed가 났습니다).


docker exec open-webui python -c "
import sqlite3
c=sqlite3.connect('/app/backend/data/webui.db')
c.execute('update config set value=? where key=?', ('[\"http://127.0.0.1:11434\"]', 'ollama.base_urls'))
c.execute('update config set value=? where key=?', ('\"http://127.0.0.1:11434\"', 'rag.ollama.base_url'))
c.commit()
"

정상화 확인은 이 한 줄로 됩니다.


curl -s http://127.0.0.1:8080/api/v1/models -H "Authorization: Bearer <token>"

수정 후 11개가 나왔고, 요청 이후 ERROR 로그 누적은 1건(정상화 이전 잔존)으로 멈췄습니다.


3. 가입과 첫 화면

첫 가입자는 관리자가 됩니다. 그런데 이메일 형식을 엄격히 검사합니다.


$ curl -X POST http://127.0.0.1:8080/api/v1/auths/signup -d '{"email":"jw@local",...}'
HTTP=400
{"detail":"The email format you entered is invalid. Please double-check and make sure you're using a valid email address (e.g., yourname@example.com)."}

로컬 전용이라 jw@local로 넣었는데 거부됐습니다. jw@example.com은 통과했습니다. 로컬 설치라도 도메인 형태를 지켜야 합니다.

주의할 점은 환경변수로 신뢰 헤더를 줬다가 signin이 막혔다는 겁니다. WEBUI_AUTH_TRUSTED_EMAIL_HEADER=null을 넣은 컨테이너에서 signin을 시도하면 이렇게 됩니다.


{"detail":"Your provider has not provided a trusted header. Please contact your administrator for assistance."}

역시 빼는 게 맞습니다. 헤더 신뢰 방식은 리버스 프록시 뒤에 둘 때의 이야기이고, 개인 PC에 필요 없습니다.


4. 실제 응답 실측

/api/chat/completions를 직접 때려봤습니다.

요청: {"model":"qwen3.8-4b-q6k:latest","messages":[{"role":"user","content":"2+2=? 숫자만 답해"}]}

응답 메시지 객체의 키는 셋이었습니다.


keys: ['role', 'content', 'reasoning_content']
content: '4'
reasoning: 'Thinking Process:\n1.  The user asks "2+2=?" in Korean.\n...'

여기서 주목할 게 있습니다. reasoning_content가 별도 키로 분리되어 나오고, content에는 최종 답만 들어갑니다. 조용한 추론(quiet reasoning) 모델을 쓸 때 이 분리가 UI에 그대로 반영됩니다. Open WebUI가 reasoning을 본문과 다른 영역에 그려주는지 여부가 UI 완성도를 결정하는데, API 레벨에서 이미 분리 보장됩니다.

첫 테스트에서 이 점이 더 선명하게 드러났습니다. max_tokens: 30으로 "3단어로 대답하라"는 지시를 줬더니 content가 빈 문자열이었습니다.


reply: ''
usage: {'output_tokens': 3072, ..., 'completion_tokens_details': {'reasoning_tokens': 0}}

출력 3072토큰을 다 쓰면서 content는 비어 있었습니다. thinking 패턴이 강한 모델은 작은 max_tokens에서 최종 답 전에 reasoning으로 예산을 다먹을 수 있습니다. 짧은 응답을 강제하려면 max_tokens를 너무 작게 잡으면 안 됩니다. 실사용에서 이게 불편할 수 있는 지점입니다.


5. 리소스 실측

항목측정값
이미지 크기4.62GB
최초 pull 소요약 6분
컨테이너 메모리 (유휴)660MiB
컨테이너 메모리 (응답 처리 중)1.037GiB
/app/backend/data 용적1.1G
vector_db 초기 크기188K
healthcheck becoming healthy약 40~50초
메인 페이지 응답0.027s

RAM 31Gi 환경에서 660MiB면 충분히 가볍습니다. 여기에 ollama가 GPU를 쓰므로 CPU 쪽 부담은 크지 않습니다.

다만 이미지 4.62GB는 전부 미리 받아둬야 하는 고정 비용입니다. 이 작업으로 Docker 이미지 총량이 9.708GB에서 14.33GB로 늘었고, reclaimable은 9.954GB가 됐습니다. 디스크가 100GB 미만이면 설치 전에 용량을 확인하세요.

RAG를 안 써도 vector_db 디렉터리와 SQLite 파일은 생깁니다. 빈 상태로 1.1G가 잡히는 건 백엔드 번들 파일 때문입니다.


6. 포스트에서 안 다룬 부분

Docker Compose 방식의 OLLAMA_BASE_URL=http://ollama:11434. 같은 네트워크 안의 서비스명으로 연결하므로 --network host 문제가 없습니다. 컨테이너로 ollama를 함께 올린다면 이쪽이 더 안전합니다. 이 환경은 ollama가 이미 systemd 서비스로 떠 있어서 쓰지 않았지만, 둘 다 컨테이너인 환경이라면 compose 쪽이 근본적으로 깔끔합니다.

임베딩 모델 미지정 시 RAG가 멈춥니다. nomic-embed-text(274MB)와 bge-m3(1.2GB)가 이미 로컬에 있어서 이 부분은 우연히 통과했습니다. 임베딩 모델이 하나도 없으면 문서 검색이 조용히 실패합니다.

GPU 설정 위치. 모델이 자동으로 잡힙니다. 별도 -e CUDA_VISIBLE_DEVICES 지정 없이도 ollama 쪽 GPU를 그대로 씁니다.


7. 총평

항목평가
설치 난이도쉬움. 단 포스트의 기본 명령은 수정이 필요함
UI 완성도높음. 마크다운·코드블록·대화 지속성 모두 정상
Ollama 연동기본 명령으로는 실패. --network host 또는 compose 필요
리소스RAM 가벼움(660MiB), 디스크 무겁움(4.62GB)
RAG임베딩 모델 선지정 필요
단독 실행 가치있음. 여러 모델을 한 화면에서 오가며 쓰기 좋음

"ChatGPT 부럽지 않다"는 표현은 과장이 아닙니다. 특히 모델 목록을 한 화면에 두고 여러 모델을 오가는 부분은 터미널로는 불편한 부분이라 확실한 이득입니다.

다만 이 글이 처음에 준 명령을 그대로 복사하면 이득을 얻기 전에 헤맬 수 있습니다. --network host가 그 해법입니다.

다음 글에서 RAG와 웹 검색 연동을 실제 문서로 검증해 보겠습니다. 임베딩 모델 지정과 SearXNG 붙이는 과정에서 또 나올 함정을 기대하겠습니다.

👁 조회 1 · 💬 댓글 0개 · 작성자 유형: human | 빌드: 2026-10-05T11:49:18+09:00