modelship-cuda

modelship-cuda

Docker app from Alz7777's Repository

Overview

Self-hosted, OpenAI-compatible inference for the agentic era. Modelship runs your whole AI stack -- reasoning LLMs with universal tool calling, embeddings, speech-to-text, text-to-speech, and image generation -- as multiple models sharing your GPUs behind a single OpenAI-compatible gateway. Built on Ray Serve with pluggable backends (vLLM, llama.cpp, Diffusers, plugins). This is the GPU variant, and requires the NVIDIA Driver plugin + NVIDIA Container Toolkit set up on this Unraid box. For a CPU-only host, use the separate "modelship-cpu" template instead. IMPORTANT: create your models.yaml at the path below BEFORE starting the container -- it will not start without one. See https://github.com/modelship-ai/modelship/blob/main/docs/model-configuration.md and https://github.com/modelship-ai/modelship/tree/main/config/examples for the format and ready-made examples. IMPORTANT: --shm-size is shared memory Ray uses to pass data between models fast. The default 2g is fine for a single small quick-start model; raise it (roughly 30% of the RAM you want to give modelship) for bigger or multi-model / GPU stacks, or things will slow down.
Modelship

Modelship

CI License: Apache 2.0 Python 3.12+ Docs Discord

Your agents already speak OpenAI. Give them a /v1/responses backend you own.

Modelship serves the whole agentic surface — reasoning, tool calling, server-side conversation state, and MCP servers the gateway calls for you — and scores 17/17 on the independent Open Responses conformance suite. Embeddings, speech, and image generation ride the same /v1. Change the base URL and your agent runs unchanged, on hardware you control.

Quick Start

One command, one model. Pick your hardware:

CPU — Linux, Windows, or macOS via Docker (images are multi-arch: amd64 + arm64):

docker run --rm --shm-size=8g -p 8000:8000 -v modelship-cache:/.cache \
  ghcr.io/modelship-ai/modelship:latest-cpu deploy \
  --model "Qwen/Qwen3-8B-GGUF:*Q4_K_M.gguf" --loader llama_server \
  --usecase generate --num-cpus 4

NVIDIA GPU — needs the NVIDIA Container Toolkit:

docker run --rm --shm-size=8g --gpus all -p 8000:8000 -v modelship-cache:/.cache \
  ghcr.io/modelship-ai/modelship:latest-cuda deploy \
  --model "Qwen/Qwen3-8B-GGUF:*Q4_K_M.gguf" --loader llama_server \
  --usecase generate --num-gpus 1

Apple Silicon — native, with Metal offload:

uv tool install mship && mship bootstrap --metal
mship deploy --model "Qwen/Qwen3-8B-GGUF:*Q4_K_M.gguf" --loader llama_server \
  --usecase generate --num-gpus 1

Wait for Deployed app 'modelship' successfully, then talk to it — this hits the Responses API and streams the model's reasoning as it thinks:

uvx --with httpx llm openai endpoint http://localhost:8000/modelship/v1 \
  -m qwen3-8b --responses "Which is larger, 9.11 or 9.9?"

Add --chat for an interactive session, -T to hand it a tool, or --models to list what's deployed.

The model serves as qwen3-8b, inferred from the reference. It pulls ~5 GB and wants ~8 GB of free RAM; --num-cpus 4 reserves four cores for it, so lower it if the container has fewer (the deploy waits for resources it can't get). On a small box, swap in lmstudio-community/Qwen3-0.6B-GGUF:*Q4_K_M.gguf.

Deploying several models at once uses a models.yaml instead; one model's tuning blocks have flags too (--llama-server-config.n-ctx 8192) — see Model Configuration. Hitting an error? Check Troubleshooting.

Prefer curl?
curl http://localhost:8000/modelship/v1/responses \
  -H "Content-Type: application/json" \
  -d '{"model": "qwen3-8b", "input": "Which is larger, 9.11 or 9.9?"}'

The response carries both output_text and a first-class reasoning output item. /modelship/v1/chat/completions is there too, if that's what your client speaks.

Open Responses conformance

/v1/responses is tested against the independent Open Responses compliance suite (bun run test:compliance), which drives the endpoint over real HTTP against a live deployment rather than mocks.

Latest result: 17/17 — every core, compaction, vision, and WebSocket test (gemma-4-12B-it QAT w4a16, vLLM, 2026-08-24).

Loader parity, 2026-09-10 (suite 92c12d9): the vllm and llama_server loaders return the same verdict on all 17 tests, across a text pair (Qwen2.5-7B-Instruct AWQ vs. its Q4_K_M GGUF) and a vision pair (Qwen2.5-VL-3B-Instruct safetensors vs. GGUF + mmproj) — four runs, no divergence. Each loader passes all 17 across the pair; neither model passes all 17 alone, since image-input needs a vision model and tool-calling needs a stronger one than the 3B.

Full test breakdown
Test Category Status
Basic Text Response Core ✅ Pass
Assistant Message Phase Core ✅ Pass
Response Output Phase Schema Core ✅ Pass
Streaming Response Core ✅ Pass
System Prompt Core ✅ Pass
Multi-turn Conversation Core ✅ Pass
Tool Calling Core ✅ Pass
Compaction Endpoint /v1/responses/compact ✅ Pass
Compaction Missing Required Model /v1/responses/compact ✅ Pass
Image Input Vision ✅ Pass
WebSocket Response WebSocket ✅ Pass
WebSocket Sequential Responses WebSocket ✅ Pass
WebSocket Continuation WebSocket ✅ Pass
WebSocket Store False Reconnect Recovery WebSocket ✅ Pass
WebSocket Missing Previous Response WebSocket ✅ Pass
WebSocket Failed Continuation Evicts Cache WebSocket ✅ Pass
WebSocket Compact New Chain WebSocket ✅ Pass

Fourteen cluster guarantees — zero-downtime model cutover, load-driven autoscaling, engine crash recovery, fractional-GPU multi-tenancy, server-side MCP tool loops — are likewise asserted end-to-end against a live cluster with real models over real HTTP. See Verified cluster behaviour.

Why Modelship

  • Conversations that survive the replica that started them. previous_response_id, reasoning, and tool state live in one store every gateway replica shares — a Ray actor by default, Redis when you want it to outlive the cluster. Scale the gateway out without sharding your users onto specific replicas.
  • One endpoint for the whole app. Chat, embeddings for RAG, speech-to-text, text-to-speech, and image generation on a single OpenAI-compatible /v1 — instead of a service per modality.
  • A stack that fits the hardware you have. Allocate GPU fractions per model (70% for the LLM, 5% for TTS), or run the same model CPU-only. vLLM, llama.cpp, Diffusers, sherpa-onnx, and whisper.cpp coexist in one deployment.
  • Changes you can ship on a Tuesday. Edit the model set and reconcile: models are added, replaced, or dropped incrementally, with a blue-green cutover per model and no gateway restart.
Full feature list
  • Multi-model, multi-GPU — chat, embedding, STT, TTS, and image models running at once across one or more GPUs, with tunable per-model GPU memory allocation
  • CPU-only support — vLLM or llama.cpp (llama_server) without a GPU (chat, embeddings, transcription, vision)
  • Multiple inference backends — vLLM, llama.cpp, Diffusers, sherpa-onnx, whisper.cpp, stable-diffusion.cpp
  • Zero-downtime hot-reloads — reconcile a changed model set incrementally, without interrupting the gateway or unchanged models
  • Reasoning and tool calling<think>-style reasoning parsed into first-class output, and universal tool/function calling across the vLLM and GGUF (llama_server) backends
  • Server-side MCP tool execution — point /v1/responses at any self-hosted MCP server (tools: [{"type": "mcp", ...}]) and the gateway discovers its tools, calls them, and loops — with an approval flow (require_approval) client-driven tool calling doesn't have
  • Multiple gateways per cluster--gateway-name mounts each gateway at its own route prefix on the same port, with its own model set
  • Per-model isolated deployments — independent lifecycle, health checks, failure isolation, and replica count per model
  • Streaming — SSE for chat completions, responses, and TTS audio
  • Client disconnect detection — cancels in-flight inference when the client goes away, freeing the GPU immediately
  • Security — gateway API-key auth (MSHIP_API_KEYS), Ray cluster token auth (--ray-auth=token), payload and concurrency limits
  • Built-in observability — Prometheus metrics, custom modelship:* metrics, vLLM engine stats, structured JSON logging, OpenTelemetry export, plus a pre-built Grafana dashboard and alerting rules

Supported OpenAI endpoints

Endpoint Usecase
POST /v1/chat/completions Chat / text generation (streaming and non-streaming)
POST /v1/responses Responses API — text, reasoning, client-driven tool calls, server-side MCP tool execution, and stored conversations (streaming and non-streaming)
GET/DELETE /v1/responses/{id} Fetch or drop a stored response (/input_items lists its input); background: true on create + POST .../cancel for queued/pollable runs
POST /v1/embeddings Text embeddings
POST /v1/audio/transcriptions Speech-to-text
POST /v1/audio/translations Audio translation
POST /v1/audio/speech Text-to-speech (SSE streaming or single-response)
POST /v1/images/generations Image generation
GET /v1/models List available models

Architecture

Modelship architecture: an agent app calls Modelship over HTTP; one Ray Serve HTTP proxy on port 8000 fronts any number of replicated gateways, each at its own route prefix with its own model set, sharing one conversation-state store and routing each request by model name to Ray Serve deployments across GPU and CPU cluster nodes.

Each model runs as an isolated Ray Serve deployment with its own lifecycle, health checks, and resource budget. Several inference backends are available:

Backend Best for GPU required
vLLM High-throughput chat, embeddings, transcription No — installs on GPU or CPU
llama.cpp (llama_server) High-efficiency quantized GGUF models (chat, embeddings, vision) No
Diffusers Image generation Yes
sherpa-onnx TTS (Kokoro) No
whisper.cpp STT No

A model name maps to exactly one deployment — swapping GPU/CPU or backend replaces it with a blue-green cutover rather than adding a second one alongside it. Each deployment scales horizontally with num_replicas, load-balanced by Ray Serve. Multiple gateways can share one cluster and one port (--gateway-name), each mounted at /<name>/v1 with its own model set.

More ways to run

Several models at once — a models.yaml replaces the flags and unlocks the nested tuning blocks (vllm_engine_kwargs, llama_server_config, autoscaling_config):

docker run --rm --shm-size=8g -p 8000:8000 -v modelship-cache:/.cache \
  -v ./models.yaml:/modelship/config/models.yaml \
  ghcr.io/modelship-ai/modelship:latest-cpu deploy

deploy reads config/models.yaml by default; --config <path> picks another.

See Model Configuration for the full reference, and config/examples/ for working files per backend.

Native install, any node role — one install command everywhere; pick the role once, at bootstrap:

uv tool install mship      # or: pipx install mship / pip install mship

mship bootstrap --cuda     # NVIDIA GPU node
mship bootstrap --cpu      # CPU node (includes vLLM CPU)
mship bootstrap --metal    # Apple Silicon
mship bootstrap --thin     # coordinator/head only, no capacity

mship deploy --config models.yaml

mship bootstrap installs a pinned Python 3.12.10 environment for that role and records it, so deploy afterwards needs no variant flag and installs nothing. A new node joins the cluster by running the same two commands. Platform prerequisites apply — see Native install.

High-throughput GPU serving — use --loader vllm with safetensors or AWQ/GPTQ/FP8 weights instead of GGUF, and set HF_TOKEN for gated models. Multi-node — join hosts into one Ray cluster with plain docker run, or deploy on Kubernetes with the Helm chart.

[!TIP] Always set --shm-size=8g (or higher) — Ray falls back to slower disk-backed storage without enough shared memory.

Documentation

Full docs are hosted at docs.model-ship.ai. The same source files are browsable in this repo:

Contributing

See CONTRIBUTING.md for guidelines on setting up the dev environment, code style, and submitting pull requests.

Install modelship-cuda on Unraid in a few clicks.

Find modelship-cuda in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.

Open the Apps tab on your Unraid server Search Community Apps for modelship-cuda Review the template variables and paths Click Install

Requirements

GPU variant -- needs the NVIDIA Driver plugin and NVIDIA Container Toolkit. For CPU-only hosts, use the "modelship-cpu" template instead.

Categories

Related apps

Explore more like this

Explore all

Details

Repository
ghcr.io/modelship-ai/modelship:latest-cuda
Last Updated2026-09-17
First Seen2026-08-03

Runtime arguments

Web UI
http://[IP]:[PORT:8000]/
Network
bridge
Shell
sh
Privileged
false
Extra Params
--shm-size=2g --runtime=nvidia

Template configuration

WebUI PortPorttcp

OpenAI-compatible API port (chat completions, embeddings, STT, TTS, images)

Target
8000
Default
8000
Value
8000
Metrics PortPorttcp

Prometheus metrics scrape port (Ray cluster, Ray Serve, vLLM, and modelship:* metrics). Only useful if Metrics Enabled is true.

Target
8079
Default
8079
Value
8079
Models ConfigPathrw

Your models.yaml defining which models to run, on GPU or CPU. MUST exist before first start or the container will exit. See docs/model-configuration.md and config/examples/ in the modelship repo for the format.

Target
/modelship/config/models.yaml
Default
/mnt/user/appdata/modelship/models.yaml
Value
/mnt/user/appdata/modelship/models.yaml
Model CachePathrw

Persistent cache for downloaded model weights (HuggingFace, vLLM, plugins). Keep this outside the container so weights survive updates/restarts.

Target
/.cache
Default
/mnt/user/appdata/modelship/cache
Value
/mnt/user/appdata/modelship/cache
HF_TOKENVariable

HuggingFace access token, required only for gated models (e.g. Llama, Gemma, Mistral variants).

MSHIP_API_KEYSVariable

Comma-separated API keys clients must send as 'Authorization: Bearer key'. Leave empty to disable auth (fine for a LAN-only deployment).

MSHIP_LOG_LEVELVariable

Log verbosity: TRACE, DEBUG, INFO, or WARNING.

Default
INFO
Value
INFO
MSHIP_METRICSVariable

Enable Prometheus metrics on the Metrics Port. Set to false to disable.

Default
true
Value
true
NVIDIA_VISIBLE_DEVICESVariable

Which GPUs to expose to the container (GPU variant only). 'all', or a comma-separated list of GPU UUIDs/indices.

Default
all
Value
all
NVIDIA_DRIVER_CAPABILITIESVariable

NVIDIA driver capabilities exposed to the container (GPU variant only).

Default
all
Value
all