llama.swap 모델 스위처: OpenAI 호환 로컬 LLM 빠른 시작
클라이언트 변경 없이 로컬 LLM을 핫스왑하세요.
가까운 미래에는 vLLM, llama.cpp 등 다양한 스택을 여러 포트에서 동시에 관리하게 될 것입니다. 하지만 다운스트림의 모든 시스템은 여전히 단일 /v1 기반 URL을 요구합니다. 그렇지 않으면 포트, 프로필, 일회용 스크립트를 계속 치환하는 수고스러운 일이 반복됩니다. llama-swap은 이러한 스택 앞단에 배치되는 /v1 프록시입니다.
llama-swap은 OpenAI 및 Anthropic 호환 단일 진입점을 제공하며, 각 model 이름을 해당 상류(upstream) 서버를 시작하는 명령어에 매핑하는 YAML 파일을 사용합니다. 특정 모델을 요청하면 프록시가 해당 서버를 시작하거나 스왑합니다. VRAM이 부족하거나 여러 모델이 공존해야 하는 경우 TTL과 그룹을 구성할 수 있습니다. 이 가이드는 설치 방법, 실용적인 config.yaml 예시, HTTP 인터페이스, 그리고 스트리밍과 리버스 프록시가 관여하기 시작할 때 나타나는 실패 유형들을 다룹니다.
LLM 호스팅 옵션에 대한 더 포괄적인 비교를 원하시면 2026년 LLM 호스팅: 로컬, 셀프호스팅 및 클라우드 인프라 비교을 참조하세요.
OpenAI 호환 로컬 LLM API를 위한 llama-swap 모델 스위처 개요
llama-swap은 경량 프록시 서버로, 단순한 운영 모델을 중심으로 설계되었습니다: 단일 바이너리, 단일 YAML 설정 파일, 종속성 없음. Go 언어로 작성되어 있어 스택의 나머지 부분 옆에 단일 정적 바이너리가 위치하며, Python 런타임이나 데스크톱 앱이 필요하지 않습니다. 모델 스왑 레이어로서 모든 OpenAI 및 Anthropic 호환 상류 서버 앞에 위치합니다.
개념적으로 이는 로컬 LLM 스택에서 자주 나오는 매우 실용적인 질문에 답합니다:
OpenAI 호환 클라이언트에서 모델을 어떻게 전환할 수 있나요?
llama-swap를 사용하면 기존처럼 /v1/... 요청을 그대로 사용하되, 요청하는 model 값을 변경하기만 하면 됩니다. llama-swap은 해당 model 값을 읽고 일치하는 서버 구성을 로드한 후, “잘못된” 상류가 실행 중이라면 이를 올바른 것으로 스왑합니다.
프로덕션 수준의 설정에서 몇 가지 설계 세부 사항이 중요합니다:
llama-swap은 MIT 라이선스를 사용하며 텔레메트리가 없습니다 — 실제 프롬프트를 다루는 호스트에 대한 경우 여전히 확인하는 것이 좋습니다.
llama.cpp, vLLM, Whisper, stable-diffusion.cpp와 같은 백엔드의 온디맨드 로딩용으로 설계되었으며, 단일 추론 엔진에 고정시키는 것을 방지합니다.
기본 상태(특별한 그룹 지정 없음)에서는 동시에 하나의 모델만 실행합니다: 다른 model을 요청하면 현재 상류를 중지하고 올바른 것을 시작합니다. 상주(resident) 모델이 둘 이상이거나 공존에 대한 더 세밀한 제어가 필요한 경우 groups를 구성하세요.
대부분의 개발자에게 유용한 사고 모델은 다음과 같습니다:
이것이 바로 모델 스위처 프록시가 “단순히 모델을 실행하는 것"과 다른 이유입니다: 이는 하나 이상의 추론 서버 위에 얹는 오케스트레이션과 라우팅이기 때문입니다.
llama-swap vs Ollama vs LM Studio vs llama.cpp server
네 가지 옵션 모두 “로컬 LLM API"를 제공할 수 있지만, 최적화하는 워크플로우는 다릅니다. 가장 빠른 선택 방법은 러타임(모델 다운로드 + 실행)을 원하는지, 아니면 라우터/프록시(러타임 간 전환 + 오케스트레이션)를 원하는지 결정하는 것입니다.
llama-swap
llama-swap은 OpenAI 호환 엔드포인트(/v1/chat/completions, /v1/completions, /v1/embeddings, /v1/models 포함)를 지원하는 투명한 프록시에 초점을 맞춥니다. 요청된 모델에 따라 요청을 올바른 상류로 라우팅합니다. 또한 /running, /logs/stream 및 /ui의 웹 UI와 같은 비추론 운영 엔드포인트도 제공합니다.
Ollama
Ollama은 자체 HTTP API(POST /api/chat, POST /api/generate, 포트 11434의 일반 로컬 기본값)를 노출합니다.
keep_alive는 모델이 로드된 상태로 유지되는 시간을 제어하며, 0은 즉시 언로드를 의미합니다.
최소한의 연결로 **모델을 가져와서 대화(Pull a model and chat)**하기를 원하는 사용자에게 적합합니다. llama-swap은 모델별 명령어, 혼합된 백엔드, 모든 클라이언트를 위한 단일 OpenAI 스타일 URL에 적합합니다 — vLLM을 llama-server 옆에 모델별 다른 플래그와 함께 오케스트레이션하는 것은 Ollama의 목표 범위 밖입니다.
LM Studio
LM Studio는 개발자 탭(localhost 또는 LAN)에서 로컬 API 서버를 제공하는 데스크톱 앱으로, OpenAI 호환 및 Anthropic 호환 모드, 터미널의 lms server start를 포함합니다.
GUI 우선 루프에 적합합니다: 모델 탐색, 클릭, 테스트. llama-swap은 서버 스타일 역할에 적합합니다: YAML, 프로세스 감시, 혼합 상류, 데스크톱 세션 불필요.
llama.cpp server
llama-server는 /v1/completions, /v1/chat/completions, /v1/responses를 노출하며, 일반적인 패턴은 OpenAI 클라이언트를 base_url을 통해 해당 서버로 가리키는 것입니다. 해당 서버 설치와 실제로 지원하는 플래그에 대한 내용은 llama.cpp 퀵스타트에서 다룹니다.
llama.cpp 또한 라우터 모드를 제공합니다: llama-server를 라우터로 실행하고, --models-dir를 설정한 후 POST /models/load와 POST /models/unload로 별도의 프록시 없이 GGUF 모델을 관리할 수 있습니다. 전체 설정 가이드는 llama-server 라우터 모드: 재시작 없이 동적 모델 전환에서 확인하세요.
모든 모델이 하나의 llama.cpp 라우터 아래에 있다면, 추가 프록시는 종종 불필요합니다. llama.cpp가 vLLM이나 다른 OpenAI 스타일 서버와 함께 배치되어야 할 때, llama-swap은 단일 /v1 표면과 그 뒤에 여러 프로세스를 제공합니다.
유사한 OpenAI 호환 호스팅 솔루션에 대해 LocalAI 퀵스타트: 로컬에서 OpenAI 호환 LLM 실행 또는 SGLang 퀵스타트: OpenAI API를 통해 LLM 설치, 구성, 서빙를 참조하세요.
Docker, Homebrew, WinGet 또는 바이너리로 llama-swap 모델 스위처 설치
Linux, macOS, Windows 모두 1급公民(first-class) 지원 대상입니다: Docker, Homebrew, WinGet, GitHub 바이너리, 또는 소스에서 빌드. 일반적인 선택 사항: 헤드리스 서버에서는 Docker, 워크스테이션에서는 Homebrew 또는 WinGet, 설치 규모를 최소로 유지해야 하는 경우 스탠드얼론 바이너리.
Docker 설치
하드웨어와 일치하는 이미지를 가져오세요. 이미지는 상류를 긴밀하게 추적하며(나이트리 빌드) CUDA, Vulkan, Intel, MUSA, CPU를 커버합니다 — 습관적으로 “최신” 대신 실제로 가속하는 방식을 선택하세요.
# Example platform pulls
docker pull ghcr.io/mostlygeek/llama-swap:cuda
docker pull ghcr.io/mostlygeek/llama-swap:vulkan
docker pull ghcr.io/mostlygeek/llama-swap:intel
docker pull ghcr.io/mostlygeek/llama-swap:musa
docker pull ghcr.io/mostlygeek/llama-swap:cpu
가능하다면 non-root 이미지 변형을 선호하세요: 컨테이너 경계가 잘못되었을 때 후회가 덜합니다.
Homebrew 설치
macOS 및 Linux에서 탭을 추가하고 설치하세요:
brew tap mostlygeek/llama-swap
brew install llama-swap
llama-swap --config path/to/config.yaml --listen localhost:8080
WinGet 설치
Windows에서:
winget install llama-swap
winget upgrade llama-swap
사전 빌드된 바이너리 및 릴리스
패키지 매니저를 원하지 않는다면 GitHub Releases에서 Linux, macOS, Windows, FreeBSD 바이너리를 제공합니다.
릴리스 번호는 빠르게 이동합니다(예: 2026년 초의 v198, v197) — 자동화에서는 “어제 있었던 것"이 아닌 특정 버전으로 고정(pin)하세요.
모델 스왑, TTL, 그룹을 위한 llama-swap config.yaml 구성
llama-swap의 모든 것은 설정 주도입니다. 최소 유효 구성은 단순히 models: 딕셔너리와 각 모델에 대한 cmd이며, 보통 ${PORT}를 모델별로 치환하여 llama-server를 시작합니다.
설정 시스템은 단순히 “프로세스 시작"을 넘어 많이 확장되며, 몇 가지 옵션은 일반적인 FAQ 스타일 문제(자동 언로드, 보안, /v1/models에 의존하는 클라이언트)에 직접 답하기 때문에 초기에 이해할 가치가 있습니다.
실제로 사용할 전역 설정
healthCheckTimeout은 모델이 정상(healthy) 상태가 될 때까지 llama-swap이 기다리는 시간입니다 (기본값 120s, 최소 15s). 느린 디스크에서 다중 GB 로딩 시 프록시를 탓하기 전에 이 값을 올려보세요.
globalTTL은 자동 언로드 전 유휴 시간(초)입니다; 기본값 **0**은 설정하지 않는 한 “언제나 로드 유지"를 의미합니다 — 장난감 수준을 넘어선 모든 설정에서 VRAM이 잊혀진 모델로 가득 차지 않도록 명시적으로 TTL을 선택하세요.
startPort는 ${PORT} 매크로의 시드입니다 (기본값 5800); 할당은 알파벳 모델 ID 순서로 결정론적이며, “누가 어떤 포트를 가져갔나"를 디버깅할 때 기능이 되지만, 무심코 모델을 이름 변경하면 함정이 됩니다.
includeAliasesInList는 별명이 /v1/models에서 개별 행으로 표시되는지 결정합니다; UI가 나열된 모델만 제공한다면 켜세요.
apiKeys는 로컬호스트 외에 도달 가능한 모든 것을 게이트합니다: Basic, Bearer, 또는 x-api-key. llama-swap은 포워딩 전 헤더를 제거하여 상류 로그에 클라이언트 비밀이 남지 않도록 합니다.
프로덕션 인체공학(ergonomics)을 여는 모델 레벨 설정
모델마다 cmd는 유일한 필수 필드입니다.
proxy는 기본적으로 http://localhost:${PORT}입니다 — 해당 모델의 상류에 대한 포워딩 대상입니다.
checkEndpoint는 기본적으로 /health입니다; 백엔드에 헬스 루트가 없거나 콜드 스타트가 기다릴 의지보다 길다면 "none"으로 설정하세요 — 깨진 /health를 남겨두었다가 왜 ready 상태에 도달하지 못하는지 궁금해하지 마세요.
ttl: -1은 globalTTL 상속, 0은 영원히 언로드 안 함, N > 0은 N초 유휴 후 언로드 — 하나의 모델은 오래 유지되고 다른 하나는 빠르게 사라져야 할 때 모델별 TTL을 사용하세요.
aliases와 useModelName은 특정 식별자를 요구하는 상류를 충족시키면서 안정적인 클라이언트 대면 이름을 유지합니다.
cmdStop은 컨테이너에 필수적이며: docker stop(또는 동등한 것)에 매핑하세요; 없다면 llama-swap이 시작한 PID에 POSIX SIGTERM / Windows taskkill이 보내집니다 — 맨 바이너리에는 적합하지만 컨테이너 이름에는 부적합합니다.
concurrencyLimit은 모델별 병렬 요청을 제한하며 초과 시 HTTP 429를 반환합니다 — 영원히 대기하기보다 부하를 떨치길 원할 때 설정하세요.
groups는 공존(swap, exclusive)과 항상 켜짐 모델(persistent)을 커버합니다. 훅은 시작 시 프리로딩할 수 있습니다; 그룹 없이 여러 모델을 동시에 프리로딩하면 **경쟁(fight)**할 것으로 예상하세요 — 모델이 GPU를 공유하는 원하는 방식과 프리로딩이 일치하도록 그룹을 먼저 정의하세요.
llama.cpp 및 vLLM을 위한 최소 config.yaml 예시
이 예시는 모범 사례 노브(knobs)를 설명하기에 “충분한” 것을 목표로 합니다: 기본 TTL, 명시적 헬스 체크, 안정적인 별명, 그리고 큰 채팅 모델이 스왑되는 동안 작은 “항상 켜짐” 모델이 실행되도록 유지하는 그룹.
# config.yaml
healthCheckTimeout: 180
globalTTL: 900 # 15분 유휴 후 언로드
includeAliasesInList: true
startPort: 5800
# 로컬호스트 개발을 넘어선 모든 경우 옵션이지만 권장
apiKeys:
- "${env.LLAMASWAP_API_KEY}"
models:
llama-chat:
cmd: |
llama-server --port ${PORT} --model /models/llama-chat.gguf
--ctx-size 8192
aliases:
- "llama-chat-latest"
# 기본값 사용:
# proxy: http://localhost:${PORT}
# checkEndpoint: /health
# ttl: -1 (globalTTL 상속)
qwen-coder:
cmd: |
llama-server --port ${PORT} --model /models/qwen-coder.gguf
--ctx-size 8192
aliases:
- "qwen-coder-latest"
vllm-coder:
# 예시 패턴: 컨테이너화된 OpenAI 호환 서버 관리
proxy: "http://127.0.0.1:${PORT}"
cmd: |
docker run --name ${MODEL_ID} --init --rm -p ${PORT}:8000 vllm/vllm-openai:latest
cmdStop: docker stop ${MODEL_ID}
checkEndpoint: "none"
ttl: 0 # 자동 언로드 없음 (예: GPU 유지)
groups:
chat-models:
swap: true
exclusive: true
members: ["llama-chat", "qwen-coder"]
always-on:
persistent: true
swap: false
exclusive: false
members: ["vllm-coder"]
이 중 하나도 장식적인 것이 아닙니다: cmd는 프로세스를 구동하고, proxy/checkEndpoint/ttl은 라우팅과 수명 주기를 제어하며, cmdStop은 Docker 기반 상류가 실제로 멈추게 하는 것입니다. 그리고 groups는 “동시에 하나의 큰 모델"과 “이 두 채팅 모델은 공존할 수 있으며 임베딩 서버는 고정됨"을 구분합니다.
OpenAI 호환 엔드포인트를 통한 모델 실행 및 스왑
llama-swap가 실행되면 다른 OpenAI 호환 엔드포인트와 마찬가지로 상호작용할 수 있습니다. API 표면에는 /v1/chat/completions, /v1/completions, /v1/embeddings, /v1/models와 같은 핵심 엔드포인트가 포함되며, llama-swap은 요청된 model을 사용하여 어떤 상류를 실행하고 라우팅할지 결정합니다.
실용적인 퀵스타트 흐름:
# 1) Start llama-swap
llama-swap --config ./config.yaml --listen localhost:8080
# 2) Discover available models
curl http://localhost:8080/v1/models
모델 목록화는 1급 관리 기능이며 ID별 정렬, unlisted 모델 제외, 별명 포함 여부 옵션과 같은 동작을 포함합니다.
# 3) Make a chat completion request for a specific model
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${LLAMASWAP_API_KEY}" \
-d '{
"model": "qwen-coder",
"messages": [{"role":"user","content":"Write a TypeScript function that retries fetch with backoff."}]
}'
이제 "model": "llama-chat"로 호출을 반복하면, llama-swap은 요청에서 요청된 모델을 추출하고適切な 서버 구성을 로드하기 때문에 상류 프로세스를 스왑합니다 (그룹 구성이 공존을 허용하지 않는 한).
SDK를 사용 중인 경우, 클라이언트를 http://localhost:8080/v1로 가리키세요 — OpenAI Python 라이브러리를 llama-server로 가리키는 것과 같은 트릭이지만, 이제 안정적인 URL은 llama-swap이며 model 필드가 상류를 선택합니다.
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8080/v1",
api_key="sk-your-llamaswap-key"
)
resp = client.chat.completions.create(
model="qwen-coder",
messages=[{"role": "user", "content": "Explain the difference between mutexes and semaphores."}],
)
print(resp.choices[0].message.content)
첫 번째 실제 요청 전에 모델을 **워밍업(warm)**하여 (콜드 스타트 지연을 숨기기) /upstream/<model>을 사용하세요 — 필요 시 자동 로드되고 해당 상류로 직접 포워딩됩니다. 벤치마크나 스크립트 테스트 전에 가중치가 상주(resident)되어 있음을 보장하는 직관적인 방법입니다.
관리 API 엔드포인트 및 SSE 이벤트를 통한 llama-swap 제어 및 모니터링
llama-swap은 단순히 “프록시"가 아닙니다; 모델 수명 주기와 관측 가능성 주위에서 도구화를 구축할 수 있는 운영 제어 엔드포인트도 노출합니다.
실행 중인 것 확인
GET /running은 로드된 모델의 런타임 상태를 반환하며, ready, starting, stopping, stopped, shutdown과 같은 상태 값을 포함합니다.
curl http://localhost:8080/running
VRAM을 자유롭게 모델 언로드
즉시 모든 것을 언로드하려면 API 버전화 엔드포인트 POST /api/models/unload를 사용하세요. 단일 모델을 언로드하려면(ID 또는 별칭으로) POST /api/models/unload/<model>을 사용하세요. 하위 호환성을 위한 레거시 GET /unload도 존재합니다.
# unload all
curl -X POST http://localhost:8080/api/models/unload
# unload one model
curl -X POST http://localhost:8080/api/models/unload/qwen-coder
TTL을 기다리지 않고 VRAM이 지금 필요할 때 벤치마크, 빠른 모델 전환, 또는 의도치 않게 훨씬 더 큰 체크포인트를 로드한 후 이 엔드포인트를 사용하세요.
SSE를 통한 라이브 이벤트 스트리밍
GET /api/events는 Server-Sent Events 스트림을 설정하며, 모델 상태 변경, 로그, 메트릭, 진행 중 요청 개수를 포함한 실시간 업데이트용으로 설계되었습니다.
curl -N http://localhost:8080/api/events
SSE 및 토큰 스트리밍은 어떤 중간 박스(middle box)가 버퍼링할 때 깨집니다 — nginx(또는 동등한 것)에서 /api/events와 /v1/chat/completions에 대해 버퍼링을 비활성화하세요. llama-swap은 SSE에서 X-Accel-Buffering: no를 설정하지만; 프록시에서도 버퍼링을 비활성화하세요 — 헤더는 올바른 프록시 구성의 대체재가 아닙니다.
메트릭 및 요청 캡처
GET /api/metrics는 토큰 사용량 메트릭을 반환하며, 인메모리 retentions는 metricsMaxInMemory(기본값 1000)로 제어됩니다.
GET /api/captures/<id>는 captureBuffer > 0이 구성된 경우에만 전체 요청/응답 캡처를 가져올 수 있습니다.
로그 및 웹 UI
llama-swap은 웹 인터페이스를 위한 /ui와 실시간 모니터링을 위한 /logs 및 /logs/stream과 같은 운영 로그 엔드포인트를 노출합니다.

apiKeys를 활성화한 경우, **다층 방어(defence in depth)**를 전제하세요: /health와 /ui의 일부는 키 없이도 도달 가능합니다 — 로컬 신뢰 경계에는 적합하지만, 호스트가 공유 네트워크에 있다면 적합하지 않습니다. llama-swap을 실제 정책을 강제하는 무언가 뒤에 배치하세요; 내장 인증은 우발적인 클라이언트를 성실하게 만드는 용도이지 공개 멀티테넌트 API를 위한 것이 아닙니다.
프로덕션 환경에서 llama-swap 모델 스위칭 트러블슈팅
llama-swap의 대부분의 문제는 리버스 프록시를 통한 스트리밍, 콜드 스타트 중 헬스 체크, 포트 및 프로세스 수명 주기, 인증이라는 소수의 운영 범주로 나뉩니다.
nginx 또는 다른 리버스 프록시 뒤에서 스트리밍이 깨짐
nginx는 기꺼이 SSE와 스트리밍 된 completions을 버퍼링해 버립니다. /api/events와 /v1/chat/completions에 대해 proxy_buffering(및 proxy_cache)을 비활성화하세요. llama-swap은 SSE에서 X-Accel-Buffering: no를 출력하므로 도움이 되지만 — 프록시를 어쨌든 수정하세요.
모델이 준비(ready) 상태로 전환되지 않음
기본적으로 모델별 checkEndpoint는 /health이며 프로세스가 준비된 것으로 간주되려면 HTTP 200을 반환해야 합니다. checkEndpoint를 다른 경로로 설정하거나 헬스 체크를 완전히 비활성화하려면 "none"으로 설정할 수 있습니다.
큰 모델이 로딩에 시간이 더 걸리는 경우, healthCheckTimeout을 증가시키거나(기본 120s) 특정 상류에 맞춘 헬스 체크를 사용하세요.
모델 전환 후 오래된 컨테이너가 실행 중
상류가 Docker 또는 Podman인 경우, cmdStop을 설정하세요 — 그렇지 않으면 llama-swap은 래퍼 프로세스를 중지하지만 컨테이너는 백그라운드에서 VRAM을 계속 소비합니다.
보안 활성화 후 401 응답 수신
apiKeys가 구성된 경우, llama-swap은 유효한 키를 요구하며 세 가지 방법(Basic auth, Bearer token, x-api-key)을 수락합니다. 또한 상류로 포워딩 전 인증 헤더를 제거합니다.
429 Too Many Requests 수신
concurrencyLimit은 초과 시 429를 반환합니다 — 설계된 동작입니다. 부족하게 설정했다면 캡을 올리고, 스로틀링을 의도하지 않았다면 제한을 낮추세요.
포트 충돌 또는 이상한 라우팅 문제
cmd에서 하드코딩된 포트를 피하세요; ${PORT}를 사용하세요. 5800+가 다른 것과 충돌하면 startPort를 이동하세요. 포트는 모델 ID의 알파벳 순서로 할당된다는 것을 기억하세요 — 모델 이름 변경 시 포트 매핑이 달라집니다.
운영 디버깅 체크리스트
사실을 위해 /running, 시작이 불투명할 때 /logs/stream, VRAM이 지금 필요할 때 POST /api/models/unload. 이 삼종 세트가 대부분의 “왜 GPU가 가득 차 있나” 세션을 커버합니다.