Ollama and Open WebUI on a GPU server: what you get
To run Ollama and Open WebUI on a GPU server, install Ollama with its official script, which sets it up as a systemd service on 127.0.0.1:11434, pull a model, then start Open WebUI in Docker connected to that local Ollama and open it through an SSH tunnel. Ollama loads models onto the GPUs and serves them through its own API and an OpenAI-compatible one. Open WebUI adds a ChatGPT-style interface with accounts and chat history: together, a self-hosted ChatGPT.
| Ollama + Open WebUI | vLLM | |
|---|---|---|
| Models | Pulled by tag from the Ollama library; the default tag is usually 4-bit | Hugging Face checkpoints in BF16, FP8 or 4-bit formats |
| Concurrency | One request per model at a time by default (OLLAMA_NUM_PARALLEL) | Continuous batching of incoming requests |
| Several GPUs | Spreads a model across GPUs only when it does not fit on one | Tensor, pipeline and data parallelism |
| Web interface | Open WebUI | None built in; Open WebUI can connect to it |
Ollama suits a private chat interface for you or a small team, and quick model switching; vLLM (Inference serving) suits many users or applications calling the API at once. If you picked Ollama with Open WebUI in the Software step when you ordered, both came installed with your model; this guide sets them up by hand. Why self-host: a private ChatGPT on your own GPU server.
Install Ollama on Ubuntu
Install the GPU driver first (NVIDIA drivers and CUDA or ROCm on MI355X): if the installer finds an NVIDIA GPU without a driver, it installs NVIDIA’s cuda-drivers package itself, which mixes driver sources. Then run the script from Ollama’s Linux page:
sudo apt update && sudo apt install -y zstd # the installer needs zstd to unpack
curl -fsSL https://ollama.com/install.sh | sh
ollama -v # installed version
systemctl status ollama --no-pager # active (running)
nvidia-smi # NVIDIA: every GPU listed
amd-smi list # AMD MI355X
The script creates an ollama user (in the render and video groups), installs and starts the ollama systemd service on 127.0.0.1:11434 and, on AMD hosts, adds Ollama’s ROCm libraries but not the kernel driver.
- NVIDIA: Ollama needs compute capability 5.0 or later and driver 550 or newer (GPU page). That page has no row for the B200 or B300 (the 0.32.3 release notes mention B200 support through CUDA 12); on either card, check with
ollama ps(below) that models load on the GPU. - AMD: Ollama needs the ROCm v7 driver. The GPU page lists the Instinct MI350X and the gfx950 target, which the MI355X shares, but not the MI355X by name: check that page before you rely on it.
Configure Ollama: OLLAMA_HOST, models on NVMe, GPU settings
Ollama reads environment variables. Set them in a systemd drop-in, not in ollama.service, which the install script rewrites on every run (sudo systemctl edit ollama opens the drop-in in an editor). Models go on the NVMe data disk (Storage and data), owned by the ollama user:
sudo mkdir -p /data/ollama/models
sudo chown -R ollama:ollama /data/ollama
sudo mkdir -p /etc/systemd/system/ollama.service.d
sudo tee /etc/systemd/system/ollama.service.d/override.conf >/dev/null <<'EOF'
[Service]
Environment="OLLAMA_HOST=127.0.0.1:11434"
Environment="OLLAMA_MODELS=/data/ollama/models"
Environment="OLLAMA_NO_CLOUD=1"
Environment="OLLAMA_CONTEXT_LENGTH=32768"
Environment="OLLAMA_NUM_PARALLEL=4"
Environment="OLLAMA_KEEP_ALIVE=30m"
Environment="OLLAMA_KV_CACHE_TYPE=q8_0"
EOF
sudo systemctl daemon-reload
sudo systemctl restart ollama
journalctl -u ollama -b --no-pager | grep -i "cloud disabled" # Ollama cloud disabled: true
| Variable | Effect |
|---|---|
OLLAMA_HOST | API address (default 127.0.0.1:11434). Keep it on localhost: Ollama checks no API key. |
OLLAMA_MODELS | Model directory (default /usr/share/ollama/.ollama/models; models already pulled stay there). |
OLLAMA_NO_CLOUD | 1 turns off Ollama’s cloud models and web search, so models run only on your GPUs. |
OLLAMA_CONTEXT_LENGTH | Context window in tokens; by default 4k below 24 GiB of GPU memory, 32k from 24 to 48 GiB, 256k from 48 GiB (context length). |
OLLAMA_NUM_PARALLEL | Requests each model processes at once (default 1). Context memory grows with this value × the context length. |
OLLAMA_KEEP_ALIVE | How long an idle model stays loaded (default 5m; -1 keeps it loaded). |
OLLAMA_MAX_LOADED_MODELS | Models loaded at the same time, if they fit (default 3 per GPU). |
OLLAMA_FLASH_ATTENTION | On automatically where supported; 1 forces it on, 0 turns it off. |
OLLAMA_KV_CACHE_TYPE | KV cache precision when Flash Attention is on: f16 (default), q8_0 (about half the memory, very small loss) or q4_0 (about a quarter). |
CUDA_VISIBLE_DEVICES, ROCR_VISIBLE_DEVICES | Limit Ollama to some GPUs; UUIDs from nvidia-smi -L or rocminfo are more reliable than numbers. |
Multiple GPUs
Ollama loads a model on one GPU when it fits, which its FAQ says usually performs best (less data crosses the PCI bus), and spreads it across all GPUs only when it does not. The other GPUs then hold other models, up to OLLAMA_MAX_LOADED_MODELS.
Pull and run models
ollama pull gpt-oss:20b
ollama run gpt-oss:20b "Explain a KV cache in two sentences."
ollama ls # models on disk
ollama ps # loaded models: SIZE, PROCESSOR, CONTEXT, UNTIL
ollama stop gpt-oss:20b # unload it now
ollama rm gpt-oss:20b # delete it from disk
Without a prompt, ollama run opens a chat. In ollama ps, PROCESSOR should read 100% GPU; a split such as 48%/52% CPU/GPU means part of the model runs on the CPU from system memory (see Troubleshooting). CONTEXT is the context length it was given.
Default tags are usually 4-bit (qwen3.5:9b is Q4_K_M). Tag lists also offer q8_0 and bf16 versions: more memory, closer to the original weights. If they fit, compare them with the default on your own prompts.
Ollama keeps the whole tag in GPU memory, plus a KV cache that grows with the context. CryptGPU’s sizing rule adds 20% to the tag size and counts 92% of GPU memory as usable:
| Tag | Size in the library | Cheapest single NVIDIA GPU that fits |
|---|---|---|
qwen3.5:9b | 6.6–7.6 GB | RTX 5080 (16 GB) |
gpt-oss:20b | 14 GB | RTX 4090 (24 GB) |
gemma4:31b | 19–20 GB | RTX 5090 (32 GB) |
gemma4:31b-it-bf16 | 63 GB | RTX PRO 6000 (96 GB) |
gpt-oss:120b | 65 GB | RTX PRO 6000 (96 GB) |
Long contexts, OLLAMA_NUM_PARALLEL above 1 and several loaded models need more. The sizing helper and the model pages show what each catalog model needs.
Run Open WebUI in Docker
Install Docker first (see Containers); Open WebUI itself needs no GPU.
Open WebUI’s quick start reaches Ollama at host.docker.internal, the host’s internal Docker address, not the 127.0.0.1 where Ollama listens. Its connection guide gives the fix: host networking with OLLAMA_BASE_URL=http://127.0.0.1:11434. Docker then publishes no ports and the app listens on the host itself, so bind it to localhost with HOST and PORT, read by the image’s start script:
# once: a fixed secret key (root-only file) keeps users logged in across updates
sudo install -m 600 /dev/null /etc/open-webui.env
echo "WEBUI_SECRET_KEY=$(openssl rand -hex 32)" | sudo tee /etc/open-webui.env >/dev/null
sudo docker run -d --network=host \
--env-file /etc/open-webui.env \
-e OLLAMA_BASE_URL=http://127.0.0.1:11434 \
-e HOST=127.0.0.1 -e PORT=3000 \
-v open-webui:/app/backend/data \
--name open-webui --restart always \
ghcr.io/open-webui/open-webui:main
sudo docker logs -f open-webui # wait for start-up, then Ctrl-C
ss -tln | grep -E "3000|11434" # both on 127.0.0.1 only
The open-webui volume holds accounts, chats and settings. Open http://localhost:3000 through the SSH tunnel (next section) and create your account at once: the first account becomes the administrator, and creating it switches off new sign-ups. To add people, an admin turns sign-ups back on under Settings > Admin > Authentication; new accounts wait as Pending until approved.
Avoid the connection guide’s other fix, OLLAMA_HOST=0.0.0.0: Ollama checks no API key, so its API would answer anyone who reaches port 11434, with only the firewall in the way. If you run Open WebUI on a bridge network instead, publish it as -p 127.0.0.1:3000:8080: Docker’s port rules bypass ufw.
Remote access to Ollama and Open WebUI
Both services stay on 127.0.0.1. Reach them through SSH, or put an authenticated TLS proxy in front of Open WebUI, never in front of Ollama.
SSH tunnel: for you and a few colleagues
# on your computer; gpu1 is the host entry from the SSH guide
ssh -N -L 3000:127.0.0.1:3000 -L 11434:127.0.0.1:11434 gpu1
Then open http://localhost:3000. The second forward serves the API examples below; if Ollama also runs on your computer, use another local port, such as -L 11435:127.0.0.1:11434. Keys and ~/.ssh/config are covered in SSH access.
Caddy with TLS: for a team
Open WebUI does not terminate TLS, and its hardening guide asks for an extra access-control layer in front of it on the internet, such as an allowlist of known IP ranges. Point a DNS name at the server and install Caddy from its official repository; it obtains the certificate and passes WebSockets and streaming through without extra settings (Open WebUI’s Caddy page).
sudo tee /etc/caddy/Caddyfile >/dev/null <<'EOF'
chat.example.com {
@team remote_ip 198.51.100.7 203.0.113.0/24
handle @team {
reverse_proxy 127.0.0.1:3000
}
handle {
respond 403
}
}
EOF
sudo systemctl reload caddy
sudo ufw allow 80,443/tcp
# allow the public origin for WebSockets, then recreate the container (see Updates)
echo "CORS_ALLOW_ORIGIN=https://chat.example.com;http://localhost:3000" | sudo tee -a /etc/open-webui.env
Put your team’s addresses after remote_ip; each person still signs in with their own Open WebUI account.
Keep Open WebUI on HOST=127.0.0.1 behind the proxy, so the proxy stays the only way in, and never proxy or publish Ollama’s port 11434.
Use Ollama’s OpenAI-compatible API
Ollama serves an OpenAI-compatible API under /v1: chat completions, completions, models, embeddings and responses. Over the tunnel above:
curl -s http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "gpt-oss:20b", "messages": [{"role": "user", "content": "Say hello in five words."}]}'
curl -s http://localhost:11434/api/tags # native API: the models on disk
In an OpenAI client, set base_url to http://localhost:11434/v1/ and api_key to any value, such as ollama: the field is required but ignored.
For people or services without SSH access, use Open WebUI’s own OpenAI-compatible endpoint, /api/chat/completions, behind the Caddy proxy. It checks a per-user API key created under Settings > Account; non-admin users need an admin to allow keys first (API endpoints).
Updates and troubleshooting
Update
curl -fsSL https://ollama.com/install.sh | sh # update Ollama; the drop-in is kept
ollama -v
# Open WebUI: back up the volume, then replace the container
sudo docker run --rm -v open-webui:/data -v /data/backups:/backup alpine \
tar czf /backup/openwebui-$(date +%Y%m%d).tar.gz /data
sudo docker rm -f open-webui
sudo docker pull ghcr.io/open-webui/open-webui:main
# then repeat the docker run command from the Open WebUI section
Open WebUI’s update guide warns that database migrations are one-way, hence the backup. To stay on known versions, install Ollama with curl -fsSL https://ollama.com/install.sh | OLLAMA_VERSION=<version> sh and replace :main with a release tag, :v<x.y.z>. Models and the Open WebUI volume are wiped with the server if the term is not renewed: keep copies elsewhere.
Troubleshooting
| Symptom | What to check |
|---|---|
ollama ps shows 100% CPU | No usable GPU, or its memory is taken. Check nvidia-smi or amd-smi list, then journalctl -u ollama --no-pager --follow --pager-end; add Environment="OLLAMA_DEBUG=1" to the drop-in for detail. On AMD, id ollama must list render and video. |
| A CPU/GPU split, or out-of-memory errors | The model and its context do not fit. Lower OLLAMA_CONTEXT_LENGTH or OLLAMA_NUM_PARALLEL, set OLLAMA_KV_CACHE_TYPE=q8_0, unload other models with ollama stop, stop other GPU jobs, or choose a smaller tag. |
| Open WebUI lists no models | On the server, curl -s http://127.0.0.1:11434/api/tags must list them. Then check the Ollama URL under Settings > Admin > Connections, and sudo docker logs open-webui. |
| Requests wait, or fail with 503 | Each model handles OLLAMA_NUM_PARALLEL requests at once (default 1). The rest queue; beyond OLLAMA_MAX_QUEUE (default 512), Ollama answers 503. |
More fixes, such as reloading the nvidia_uvm module, are in Ollama’s troubleshooting page.
From the blog
Need help with this guide?
Tell us your GPU, the commands you ran and the output you got through the contact form.
