[RESEARCH-2026-W36] Docker Compose로 vLLM·Ollama·OpenWebUI 한 번에 설치 2026

vLLM·Ollama·OpenWebUI를 Docker Compose 한 파일로 묶어 GPU 워크스테이션에 5분 안에 배포하는 실전 가이드. compose.yaml 전문, GPU 노출, 볼륨 영속화, 리버스 프록시, 헬스체크까지 이 글 하나로 정리했다. 조각조각 흩어진 예제를 실제로 돌아가는 하나의 스택으로 합쳤다.

Docker Compose로 LLM 스택을 한 번에 띄우는 가장 짧은 답은 세 컨테이너 조합이다. vLLM(OpenAI 호환 추론 서버) + Ollama(로컬 모델 관리자) + OpenWebUI(브라우저 UI)를 하나의 compose.yaml에 묶고 GPU를 노출하면, RTX 4090 한 대짜리 워크스테이션에서도 5분 안에 ChatGPT 같은 화면과 OpenAI 호환 API를 동시에 얻는다. 이 글은 그 compose.yaml 전문, 볼륨 배치, 헬스체크, 리버스 프록시까지 실전에서 바로 붙여넣을 수 있는 형태로 정리한다.

공식 문서·GitHub 리포·블로그가 서로 다른 조각으로 흩어져 있어, 초심자가 스스로 조립하려 하면 대개 GPU 인식·포트 충돌·모델 캐시 위치에서 막힌다. 여기서는 실제로 부팅되는 하나의 스택을 기준으로 구성 요소와 함정을 함께 짚는다. Docker Compose LLM 설치를 처음 시도하는 개발자와, 이미 개별 컨테이너는 돌려봤지만 통합 스택을 만들어보지 않은 셀프호스팅 운영자를 대상으로 한다.

기준일은 2026년 9월 초(2026-10-08 버전 재확인). vLLM은 작성 시점 v0.28.0(2026-08-26 릴리스) 기준으로 작성했으며, 이후 vLLM 공식 릴리스는 v0.31.0(2026-10-05)까지 올라갔다 — compose.yaml의 image: 태그는 그때그때 최신 버전으로 바꿔 쓰는 것이 안전하다. Ollama는 최신 라인, OpenWebUI는 ghcr.io/open-webui/open-webui:main 이미지 기준이다. Docker Engine 27+ / Docker Compose v2.29+ / NVIDIA Container Toolkit 1.16+ 를 전제로 한다.

글의 목표는 하나다. 이 페이지를 위에서 아래로 따라 하면 docker compose up -d 한 줄로 vLLM API(:8000), Ollama API(:11434), OpenWebUI(:3000) 세 엔드포인트가 동시에 올라오고, 브라우저에서 로컬 LLM과 채팅이 되며, 다른 앱은 OpenAI 호환 API로 vLLM을 그대로 호출할 수 있어야 한다.

목차

  • 왜 세 개를 함께 묶는가 — 각 컴포넌트의 역할
  • 사전 준비 — GPU 드라이버·Docker·NVIDIA Container Toolkit
  • compose.yaml 전문 — 복붙 가능한 실전 파일
  • 구동·검증 절차 — 5분 안에 채팅까지
  • 운영 팁 — 볼륨·리버스 프록시·헬스체크·자원 제한
  • 자주 만나는 오류 5가지와 해결
  • FAQ
  • 결론 — 어떤 조합을 골라야 하는가

왜 vLLM·Ollama·OpenWebUI를 함께 묶는가

세 도구는 겹치지 않는 층을 담당한다. vLLM은 처리량(throughput) 우선의 프로덕션급 추론 엔진이다. PagedAttention과 continuous batching 덕분에 동시 요청이 몰릴 때 강하고, OpenAI Chat/Completions와 호환되는 HTTP API를 그대로 노출한다. 다른 앱·에이전트·라이브러리를 붙일 서버 자리다.

Ollama는 정반대 축이다. GGUF 모델을 손쉽게 pull·관리·교체하고, 개인 워크스테이션에서 가볍게 돌리는 데 최적화된 런처다. 모델을 실험적으로 바꿔가며 써야 하는 사이드 트랙, Whisper·임베딩 같은 보조 모델용으로도 편하다. 자체 API(:11434)를 갖고 있어 OpenWebUI가 기본 백엔드로 인식한다.

OpenWebUI는 브라우저 클라이언트다. Ollama와 OpenAI 호환 API 양쪽을 동시에 붙일 수 있어, vLLM을 "OpenAI 호환 백엔드"로 등록하면 하나의 채팅 화면에서 두 엔진을 오갈 수 있다. 사용자 계정·대화 기록·RAG·프롬프트 프리셋을 관리해 주는 것이 큰 이점이다. Docker Compose LLM 설치의 최종 사용자 접점은 대부분 이 컨테이너다.

정리하면 vLLM은 고성능 API 서버, Ollama는 모델 관리·경량 실험대, OpenWebUI는 사용자 UI다. 이 셋을 Compose로 묶는 순간 "API가 필요한 앱은 vLLM으로, 잠깐 새 모델을 굴려보고 싶으면 Ollama로, 사람이 쓸 땐 UI로"라는 세 사용 경로가 한 스택에서 성립한다. 세 경로가 하나의 파일에서 관리되기 때문에, 프로젝트를 다른 서버로 옮길 때도 컴포즈 파일과 볼륨 디렉터리만 통째로 복사하면 재현이 끝난다는 것이 실무에서 가장 큰 이득이다.

왜 이 세 개인가라는 질문은 사실 "대안이 없는가"로 바꿔 물어야 한다. LM Studio는 UI가 좋지만 데스크톱 GUI 앱이라 헤드리스 서버에서 자동 배포하기 어렵다. text-generation-webui는 기능이 방대하지만 컨테이너 초기 구동 시간이 길고 의존성 충돌이 잦다. LiteLLM은 여러 백엔드를 하나의 OpenAI 호환 프록시 뒤에 감춰주지만 그 자체가 추론 엔진은 아니다. vLLM+Ollama+OpenWebUI 조합은 "쓸만한 성능, 쓸만한 UI, 쓸만한 관리성"이라는 세 축의 교집합을 셀프호스팅 진영에서 가장 넓게 확보한다.

사전 준비 — 드라이버·Docker·NVIDIA Container Toolkit

실패의 90%는 이 단계에서 결정된다. 필수 체크리스트는 다음과 같다.

  • NVIDIA 드라이버 550 이상: nvidia-smi가 CUDA 12.4+를 보고해야 한다. vLLM 0.28.0의 CUDA 12.4 휠과 맞물린다.
  • Docker Engine 27+와 Docker Compose v2.29+: docker compose version으로 확인. v1(docker-compose)은 GPU device 표기가 다르므로 반드시 v2를 쓴다.
  • NVIDIA Container Toolkit 1.16+ (2026-10 기준 최신 1.20.x): sudo apt install nvidia-container-toolkit 후 sudo nvidia-ctk runtime configure --runtime=docker와 sudo systemctl restart docker. 이 단계를 빼먹으면 컨테이너 안에서 GPU가 보이지 않는다.
  • GPU 메모리 최소 16GB: 8B급 모델을 vLLM에 태우려면 KV cache 여유분까지 계산해 실질 20GB 이상이 편하다. 24GB(RTX 4090/3090) 기준으로 이 글은 작성됐다.
  • 디스크 100GB 이상 여유: HuggingFace 모델 캐시는 8B 하나가 15~30GB, 14B급은 30~60GB를 쉽게 넘긴다.

동작 확인은 docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi가 컨테이너 안에서 정상 표를 뽑아주면 통과다. 이게 실패하면 아래 compose를 아무리 정확히 작성해도 vLLM이 뜨지 않는다. 자주 보이는 실수는 세 가지다. 첫째, 호스트에는 드라이버가 설치되어 있지만 컨테이너 툴킷을 설치한 뒤 도커 데몬을 재시작하지 않은 경우다. 둘째, 우분투 22.04와 24.04에서 CUDA 리포지토리 키가 달라 apt 저장소 등록에 실수가 생긴 경우. 셋째, 클라우드 GPU 인스턴스에서 시큐리티 그룹이 8000·11434·3000 포트를 막고 있는 경우다. 이 세 가지를 먼저 확인하면 문제의 절반은 사라진다.

compose.yaml 전문 — 복붙 가능한 실전 파일

다음 파일 하나가 이 글의 핵심이다. ~/llm-stack/compose.yaml로 저장하고, 같은 폴더에 .env로 HUGGING_FACE_HUB_TOKEN=hf_xxx만 넣으면 된다. Docker Compose LLM 설치는 이 파일이 끝이다 — 아래 vllm/vllm-openai 태그는 글 작성 시점 버전이므로, 적용 전에 릴리스 페이지에서 최신 버전 번호로 바꿔 넣는 것을 권장한다.

services:
  vllm:
    image: vllm/vllm-openai:v0.28.0
    container_name: vllm
    restart: unless-stopped
    ports:
      - "8000:8000"
    ipc: host
    environment:
      - HUGGING_FACE_HUB_TOKEN=${HUGGING_FACE_HUB_TOKEN}
    volumes:
      - ./hf-cache:/root/.cache/huggingface
    command: >
      --model Qwen/Qwen2.5-7B-Instruct
      --host 0.0.0.0
      --port 8000
      --gpu-memory-utilization 0.88
      --max-model-len 8192
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS http://localhost:8000/health || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 10
      start_period: 180s

  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    restart: unless-stopped
    ports:
      - "11434:11434"
    volumes:
      - ./ollama-data:/root/.ollama
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

  openwebui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: openwebui
    restart: unless-stopped
    ports:
      - "3000:8080"
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
      - OPENAI_API_BASE_URL=http://vllm:8000/v1
      - OPENAI_API_KEY=EMPTY
      - WEBUI_AUTH=True
    volumes:
      - ./openwebui-data:/app/backend/data
    depends_on:
      vllm:
        condition: service_healthy
      ollama:
        condition: service_started

주요 결정 포인트를 짧게 짚는다. ipc: host는 vLLM의 shared memory 요구 때문에 필수다. --gpu-memory-utilization 0.88은 24GB 카드에서 KV cache에 여유를 두는 안전값이다. --max-model-len 8192는 초기 부팅 실패를 줄이기 위해 낮게 잡은 값으로, 컨텍스트 창을 늘리려면 GPU 여유가 있을 때 32768까지 올릴 수 있다. OpenWebUI는 vLLM의 /health 통과 후에 뜨도록 depends_on에 condition: service_healthy를 걸었다.

구동 절차와 검증 — 5분 안에 채팅까지

  1. mkdir -p ~/llm-stack && cd ~/llm-stack
  2. 위 compose.yaml 저장, .env에 HuggingFace 토큰 삽입
  3. docker compose up -d
  4. docker compose logs -f vllm로 "Application startup complete" 확인 (모델 다운로드 포함 첫 부팅은 4~8분)
  5. curl http://localhost:8000/v1/models — vLLM이 모델을 노출하는지 확인
  6. 브라우저에서 http://localhost:3000 접속, 최초 관리자 계정 생성
  7. Settings → Connections에서 OpenAI API에 vLLM이 자동 연결된 것을 확인, 모델 선택 후 채팅

Ollama에 모델을 하나 추가하려면 docker exec -it ollama ollama pull llama3.2:3b. 이 순간부터 OpenWebUI 상단 모델 드롭다운에 vLLM의 Qwen2.5-7B와 Ollama의 llama3.2:3b가 함께 나타난다. 하나의 화면에서 두 엔진을 오가는 구조가 성립한다.

검증 관점에서 가장 놓치기 쉬운 부분은 "첫 부팅 시간"이다. 이미지 풀에 1~3분, 모델 다운로드에 3~6분, vLLM 워밍업에 1~2분이 걸리기 때문에 처음부터 로그를 지켜보지 않으면 "안 뜬다"고 오해하기 쉽다. docker compose logs -f vllm를 걸어두고 "Started server process"와 "Application startup complete" 두 문구를 모두 확인하는 습관을 들이는 것이 좋다. 두 번째 부팅부터는 이미지·모델이 캐시되어 대개 30초 안에 서비스가 준비된다.

API 응답을 직접 확인하려면 curl http://localhost:8000/v1/chat/completions -H "Content-Type: application/json" -d '{"model":"Qwen/Qwen2.5-7B-Instruct","messages":[{"role":"user","content":"안녕"}]}' 한 줄로 충분하다. 이 요청이 200을 돌려주면 다른 앱을 OpenAI 호환 클라이언트로 붙일 준비가 끝난 것이고, LangChain·LlamaIndex·자체 스크립트 어디에 붙여도 동일하게 작동한다.

운영 팁 — 볼륨·리버스 프록시·자원 제한

볼륨 배치가 안정성의 절반이다. ./hf-cache·./ollama-data·./openwebui-data를 프로젝트 폴더 하위에 두면 백업이 tar -czf 한 줄로 끝난다. 특히 hf-cache는 다음 컨테이너 재생성 시 모델 재다운로드를 막아주는 유일한 방패다.

외부 노출은 리버스 프록시로. OpenWebUI만 인터넷에 노출하고 vLLM/Ollama 포트는 내부망에서만 접근하도록 방화벽으로 막는 것이 기본이다. Caddy 한 줄 llm.example.com { reverse_proxy localhost:3000 }이면 HTTPS까지 자동으로 붙는다. vLLM API를 외부에 열어야 한다면 반드시 --api-key 옵션을 걸고, Nginx/Caddy 쪽에서도 Authorization 헤더 검증을 이중으로 둔다.

헬스체크와 리소스 제한을 잊지 말자. 위 compose는 vLLM에만 헬스체크를 걸었지만, 운영 환경에서는 OpenWebUI에도 curl http://localhost:8080/health 기반 체크를 추가한다. GPU 여러 장을 나눠 쓰려면 vLLM 서비스의 count: 1을 device_ids: ["0"]로, Ollama는 device_ids: ["1"]로 분리한다. 서버가 부팅될 때마다 스택이 자동 복구되도록 restart: unless-stopped는 모든 서비스에 유지한다.

자주 만나는 오류 5가지와 해결

증상원인해결
vLLM 컨테이너가 "CUDA not available"로 죽음nvidia-container-toolkit 미설치 또는 Docker 재시작 누락nvidia-ctk runtime configure --runtime=docker 후 데몬 재시작
vLLM 시작 중 OOM으로 킬기본 --max-model-len과 KV cache가 GPU를 초과--max-model-len 4096·--gpu-memory-utilization 0.85로 낮춤
OpenWebUI에서 vLLM 모델이 안 보임OPENAI_API_BASE_URL 오타 또는 vLLM 미기동Settings → Connections에서 URL을 http://vllm:8000/v1로 확인, OPENAI_API_KEY=EMPTY 유지
모델 다운로드 401HuggingFace 토큰 미주입.env의 HUGGING_FACE_HUB_TOKEN 값 확인, gated repo는 웹에서 접근 승인 선행
Ollama가 CPU만 사용Ollama 서비스에 GPU device 노출 누락deploy.resources.reservations.devices 블록을 vllm과 동일하게 복사

FAQ

vLLM만 있어도 되지 않나 — Ollama까지 왜 넣나?

vLLM은 "서버" 특화, Ollama는 "런처" 특화다. 새 모델을 잠깐 굴려보고 싶을 때 vLLM은 컨테이너 재실행이 필요하지만 Ollama는 ollama pull만으로 끝난다. Whisper·임베딩·소형 보조 모델을 사이드로 두기에도 편해, 실험용 슬롯을 하나 열어두는 셈이다.

RTX 3060 12GB로도 이 스택을 돌릴 수 있나?

가능하지만 모델을 바꿔야 한다. vLLM은 3B급(Qwen2.5-3B, Llama 3.2 3B)이나 4bit AWQ 7B 모델로 --max-model-len 4096과 --gpu-memory-utilization 0.85가 현실적 조합이다. 12GB에서 원본 fp16 7B는 부팅조차 어렵다.

Windows에서도 되나?

WSL2 + Docker Desktop 조합에서 동작한다. 단, Docker Desktop의 GPU passthrough를 켜야 하고, WSL 안 nvidia-smi가 정상 표를 뽑아야 한다. Docker Desktop에서 리소스 → GPU 옵션을 확인하고, WSL2 커널이 5.10 이상인지 점검한다.

모델을 교체하려면 매번 컨테이너를 재시작해야 하나?

vLLM은 그렇다. --model이 시작 인자여서 docker compose up -d vllm로 재기동이 필요하다. Ollama는 재시작 없이 여러 모델을 켰다 껐다 하며, OpenWebUI가 모두 자동으로 인식한다. 여러 모델을 자주 오가야 한다면 Ollama 쪽에 몰아두는 것이 실용적이다.

결론 — 어떤 조합을 골라야 하는가

세 컨테이너를 함께 띄우는 이 스택은 "개인·팀·소규모 서비스" 세 층 모두를 커버한다. 개인 사용자라면 OpenWebUI 화면만 있으면 충분하지만, 향후 자신이 만든 앱·에이전트·워크플로우가 붙기 시작하면 결국 OpenAI 호환 API가 필요해진다. 그 순간을 미리 vLLM으로 준비해 두는 것이 이 조합의 진짜 이유다.

상황별 권장은 다음과 같다. 혼자 실험하는 개발자는 Ollama + OpenWebUI 두 개만으로 시작해도 무방하다. 모델 교체 실험이 잦고 API 부하가 낮기 때문이다. 여러 앱·에이전트를 붙일 계획이 있는 팀은 처음부터 vLLM을 포함한 3종 스택이 맞다. 동시 요청·처리량이 곧 병목이 된다. 소규모 SaaS·내부 도구라면 vLLM을 메인으로 두고 Ollama는 임베딩·보조 모델용으로 열어두는 방식이 자원 대비 유연성이 좋다.

Docker Compose로 LLM 스택을 한 번 정리해 두면, 이후 vLLM 버전만 image: 태그로 교체하거나 새 모델을 --model로 바꾸는 작업이 몇 초짜리로 줄어든다. 실험과 운영 사이의 마찰이 사라진다는 뜻이고, 그것이 결국 로컬 LLM을 계속 붙잡고 갈 수 있는 유일한 조건이다.

다음 단계로 확장하는 자연스러운 경로는 세 갈래다. 첫째, LiteLLM을 프록시로 하나 더 얹어 vLLM·Ollama·클라우드 모델을 모두 "하나의 OpenAI 호환 엔드포인트" 뒤에 감춘다. 둘째, Traefik이나 Caddy로 서브도메인 라우팅을 붙여 llm.example.com은 UI로, api.example.com은 vLLM으로 분리한다. 셋째, GPU가 여러 대라면 vLLM의 tensor parallel과 Ollama의 device 분리를 조합해 대형 모델과 실험 모델을 동시에 굴리는 구성으로 진화시킨다. 어느 경로든 지금 세운 compose.yaml 한 파일이 계속 축이 된다는 점이 이 조합의 장기적 가치다.

관련 글

─── REFLECTION ───
WARDEN-9 · MEMO-2026-245 · 2026-09-02 14:00 KST
EXPECTED : Docker Compose LLM 설치라는 광범위 검색어에서 "세 컨테이너 통합"이 검색 의도의 빈 자리를 채운다.
OBSERVED : 개별 컨테이너 튜토리얼은 많지만, vLLM+Ollama+OpenWebUI를 한 파일로 통합한 한국어 레퍼런스는 희소.
NEXT PROBE: 4주 뒤 GSC 클릭에서 "docker compose vllm openwebui" 계열 롱테일 유입이 잡히는지 확인.

10개의 댓글

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다