Ollama is the fastest way to get a language model answering HTTP requests on your own server, and its defaults are built for a laptop rather than a machine with a public IP address. This guide covers the server-side decisions: whether you need a GPU at all, how to tune the service, how to expose the API without handing out free inference, and the point at which Ollama stops being the right tool.

Ollama is a wrapper around llama.cpp that adds three things: a model registry with one-line pulls, a REST API, and a systemd service. That packaging is why it spread so fast, and also why it is easy to deploy badly. Out of the box it listens only on localhost, holds a single model in memory for five minutes at a time, and ships no authentication whatsoever. Every one of those defaults is correct on a laptop and wrong on a server.

MassiveGRID infrastructure for local inference: Proxmox HA cluster with automatic failover · Ceph 3x replicated NVMe storage · independent CPU, RAM and storage scaling · 12 Tbps DDoS protection · deploy in any of 85+ metros across 30+ countries · 24/7 human support

Cloud VPS for CPU inference — from $1.99/mo
Dedicated VPS for consistent token throughput
GPU servers when CPU is no longer enough — A100 from $1,649/mo

CPU or GPU: Decide Before You Provision

Ollama runs on both, and the difference is not subtle. Text generation is bound by memory bandwidth, not raw compute, because every token requires reading the whole active model out of memory. A server DDR4 or DDR5 channel set delivers roughly 50 to 100 GB/s. An A100 delivers around 1.9 TB/s and an H100 around 3.4 TB/s.

That ratio shows up directly in generation speed. As a rough planning figure for an 8B model at 4-bit quantization:

TargetHardwareGeneration speedHonest use case
Experiments, batch jobs8 vCPU, 16 GB RAMroughly 5–12 tokens/secOvernight summarisation, cron-driven classification
Internal tooling16 vCPU, 32 GB RAMroughly 10–20 tokens/secA handful of colleagues, no concurrency
Interactive appsGPU, 20–48 GB VRAM50–150+ tokens/secChat UIs, agents, anything user-facing

The practical rule: if a human waits for the output, use a GPU. If a queue waits for it, CPU is often fine and dramatically cheaper. Reading speed is around 5 to 8 tokens per second, so anything under that feels broken to a user.

Prerequisites

A clean Ubuntu 24.04 LTS server, root or sudo access, and a domain name pointed at the server if you intend to expose the API. For CPU inference, size RAM at roughly twice the model file size so the page cache can hold the weights. For GPU inference, install the NVIDIA driver first and confirm nvidia-smi reports the card before touching Ollama.

Storage matters more than people expect. Model weights are large and you will accumulate them: an 8B model at Q4 is around 4.7 GB, a 70B at Q4 is around 40 GB. Budget 100 GB of disk minimum if you plan to compare models.

Installation

The install script detects the platform, adds the systemd unit and creates a dedicated ollama user:

curl -fsSL https://ollama.com/install.sh | sh
systemctl status ollama

Piping a remote script into a shell deserves a moment of thought. If that is not acceptable in your environment, download it, read it, then run it:

curl -fsSL https://ollama.com/install.sh -o ollama-install.sh
less ollama-install.sh
sh ollama-install.sh

Pull a model and confirm the service answers:

ollama pull llama3.1:8b
ollama list
curl http://127.0.0.1:11434/api/generate -d '{
  "model": "llama3.1:8b",
  "prompt": "Reply with the single word: ready",
  "stream": false
}'

Weights land in /usr/share/ollama/.ollama/models when Ollama runs as a service. That path is on the root filesystem by default, so if you attach a separate volume for models, move the directory and symlink it rather than fighting the service user's permissions later.

Server-Side Configuration

Ollama reads its configuration from environment variables, which on a systemd host belong in a drop-in file rather than the shipped unit:

systemctl edit ollama
[Service]
Environment="OLLAMA_HOST=127.0.0.1:11434"
Environment="OLLAMA_KEEP_ALIVE=30m"
Environment="OLLAMA_MAX_LOADED_MODELS=2"
Environment="OLLAMA_NUM_PARALLEL=4"
Environment="OLLAMA_FLASH_ATTENTION=1"

Then reload and restart:

systemctl daemon-reload
systemctl restart ollama

What these actually do:

VariableEffectWhy you would change it
OLLAMA_HOSTBind address and portLeave on localhost and terminate TLS in a reverse proxy. Setting 0.0.0.0 publishes an unauthenticated API
OLLAMA_KEEP_ALIVEHow long an idle model stays residentDefault is 5 minutes. Longer avoids reload latency, at the cost of holding memory
OLLAMA_MAX_LOADED_MODELSModels resident at onceRaise only if total VRAM or RAM can hold them together
OLLAMA_NUM_PARALLELConcurrent requests per modelEach slot reserves its own KV cache, so memory use scales with this number
OLLAMA_FLASH_ATTENTIONEnables flash attentionReduces KV cache memory and speeds up long contexts on supported GPUs

The one that surprises people is OLLAMA_NUM_PARALLEL. Four parallel slots on a model with a 16k context does not cost one KV cache, it costs four. On a 20 GB card that can be the difference between running and an out-of-memory crash mid-request.

Exposing the API Safely

Ollama has no authentication, no rate limiting and no request logging worth the name. Anything reachable on the public internet will eventually be found and used to generate someone else's tokens on your hardware. Put nginx in front of it and require a key.

apt install nginx apache2-utils
htpasswd -c /etc/nginx/.ollama-users apiclient

A minimal server block, with the long timeouts that generation requires:

server {
    listen 443 ssl;
    http2 on;
    server_name llm.example.com;

    ssl_certificate     /etc/letsencrypt/live/llm.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/llm.example.com/privkey.pem;

    location / {
        auth_basic           "Inference API";
        auth_basic_user_file /etc/nginx/.ollama-users;

        proxy_pass         http://127.0.0.1:11434;
        proxy_set_header   Host localhost:11434;
        proxy_http_version 1.1;
        proxy_buffering    off;
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
    }
}

Two details are easy to miss. proxy_buffering off is what makes streamed responses arrive token by token instead of in one lump at the end. And overriding the Host header to localhost:11434 keeps Ollama's origin check happy, which otherwise rejects proxied requests.

Get the certificate with Let's Encrypt, and close the API port at the firewall so nothing bypasses the proxy:

certbot --nginx -d llm.example.com
ufw allow 443/tcp
ufw allow 22/tcp
ufw deny 11434/tcp
ufw enable

The OpenAI-Compatible Endpoint

Ollama exposes an OpenAI-shaped API at /v1, which means most existing SDKs and tools point at it with a base URL change and nothing else. This is the single most useful thing about running it:

curl https://llm.example.com/v1/chat/completions \
  -u apiclient:yourpassword \
  -H "Content-Type: application/json" \
  -d '{
    "model": "llama3.1:8b",
    "messages": [{"role": "user", "content": "Summarise this log line: OOMKilled"}]
  }'

The compatibility is good but not total. Function calling, structured output modes and some sampling parameters behave differently or are ignored, so test the specific features your application depends on rather than assuming parity.

Choosing a Quantization

Ollama tags default to 4-bit quantization, usually Q4_K_M. That is a deliberate default and it is normally the right one, but it is worth knowing what you are trading:

QuantizationBytes per parameter8B model sizeQuality impact
FP162.0~16 GBReference quality
Q8_0~1.0~8.5 GBEssentially indistinguishable
Q5_K_M~0.7~5.7 GBVery close to reference
Q4_K_M~0.6~4.9 GBSmall measurable loss, rarely noticeable in practice
Q3_K_M~0.45~4.0 GBVisible degradation on reasoning tasks

Below 4-bit, quality falls off faster than size does. If a model at Q3 fits and at Q4 does not, the better move is almost always a smaller model at Q4 rather than a bigger model at Q3.

Monitoring and Troubleshooting

Two commands answer most questions. ollama ps shows which models are resident and whether they are on GPU or CPU, which is how you catch a model that silently fell back to CPU because it did not fit in VRAM:

ollama ps
journalctl -u ollama -f

On a GPU host, watch utilisation and memory while a request runs:

nvidia-smi dmon -s um

Common failure modes and what they actually mean:

SymptomUsual cause
Sudden 10x slowdownModel did not fit in VRAM and is running partly or wholly on CPU. Check ollama ps
First request slow, rest fastNormal. Weights are being loaded. Raise OLLAMA_KEEP_ALIVE
Out of memory under loadOLLAMA_NUM_PARALLEL multiplied by context length exceeds available memory
Streaming arrives all at onceproxy_buffering is still on in nginx
403 through the proxyOrigin check. Override the Host header as shown above

When to Move Off Ollama

Ollama is excellent for a single user, a small team or a batch pipeline. It is not a serving stack. It does not do continuous batching, so concurrent requests queue rather than share a forward pass, and throughput per GPU is a fraction of what the hardware can do.

The signal to switch is concurrency. Once you have more than a few simultaneous users, or you are paying for a GPU and want the throughput you are paying for, move to vLLM, which batches requests continuously and manages KV cache in pages. Our guide to serving an LLM API with vLLM covers that migration.

Infrastructure That Does Not Drop Your Context

An inference endpoint tends to become load-bearing quickly. Something starts calling it on every page view, or a nightly job depends on it, and an hour of downtime turns into a queue of failed requests.

Every MassiveGRID server runs on a Proxmox high-availability cluster with automatic failover, so a hardware fault migrates the workload to another node instead of ending it. Storage is Ceph with three-way replication across independent NVMe drives, which matters when your model library and fine-tuned adapters live on that disk. Because CPU, RAM and storage scale independently, you can add the memory a larger model needs without paying for cores you will not use.

Start on a Cloud VPS to prove the workload out, move to a Dedicated VPS when consistent throughput matters, and step up to dedicated GPU infrastructure when a human is waiting on the output.

Further Reading