The box froze instead of slowing down
The model plus its context cache exceeded RAM and the kernel began paging weights on every token. Fit in memory or drop a size — there is no middle setting.

Six steps to a language model answering on your own hardware — the arithmetic that predicts your tokens per second before you buy the box, which model fits in 4, 8, 16 or 32 GB, the authentication Ollama does not ship, and an honest list of the jobs that do not fit. Tested on Debian 12.
Size
Bandwidth, not cores
Install
Ollama
Measure
Your own tokens/s
Protect
TLS + a token
Connect
One base URL
Bound it
Context, RAM, disk
Almost everything written about running language models locally is about graphics cards, which is unhelpful if what you have is a virtual machine. The useful mental model is simpler than the GPU discourse suggests, and it fits in one sentence: to produce a single token, a dense model has to read every one of its weights out of memory. Not some of them — all of them, once per token.
That single fact settles the whole performance question. Generation speed is not decided by how many cores you rent; it is decided by how fast the machine can stream the model out of RAM. Which gives you a ceiling you can compute on the back of an envelope before you spend anything:
tokens per second ≈ memory bandwidth (GB/s) ÷ model size (GB)
A quantised 8B model occupies about 5 GB. On a virtualised host where you can realistically stream something in the region of 20 GB/s, the ceiling is about four tokens a second, and the number you actually observe will be perhaps half to two-thirds of that. Roughly three words a second. Read that as a fact about the workload rather than a disappointment: it is slow for a chat window and completely adequate for a job that classifies a document and returns one line of JSON.
Cores still matter, just not for the reason people assume. More threads help until they saturate the memory path, which on these machines happens early — often around four to eight threads. Past that point the extra cores are idle while everything waits on RAM. This is why a sixteen-core box is not four times faster than a four-core box at generating text, and why paying for cores in the hope of speed is the most common way to buy the wrong tier.
Reading is fast, writing is slow. There are two phases in every request and they behave nothing alike. Processing the prompt — the prefill — is a compute-bound matrix operation over the whole input at once, and it runs many times faster than generation. Producing the answer is the memory-bound part described above, one token at a time. So handing the model a two-thousand-word document and asking for three sentences back is a comfortable workload, while asking it for a two-thousand-word essay is not. Design your prompts around that asymmetry and a CPU box feels far better than its token rate suggests.
| Tier | Memory | Largest comfortable model | Order of magnitude | What it is for |
|---|---|---|---|---|
| Sentinel · $3.90 | 4 GB | 3B at Q4 (~2 GB) | a sentence per second | Classification, tagging, routing, embeddings |
| Garrison · $7.90 | 8 GB | 8B at Q4 (~5 GB) | a few words per second | The default. Summaries, JSON extraction, RAG answers |
| Ravelin · $16.90 | 16 GB | 14B at Q4 (~9 GB) | about half of the above | Harder reasoning, long context, batch pipelines |
| Bulwark · $32.90 | 32 GB | 32B at Q4 (~20 GB) | under a word per second | Quality over speed. Asynchronous work only |
| Citadel · $62.90 | 64 GB | 70B at Q4 (~40 GB) | minutes per answer | It fits. It is not interactive. Overnight jobs |
Model sizes are the usual Q4_K_M quantisations, which trade a small and generally unnoticed amount of quality for less than half the memory. The speed column is an order of magnitude derived from the division above, not a benchmark — the point of step 03 is that you measure your own machine rather than trusting a table, including this one.
The honest version of this section is worth more than an enthusiastic one, because the fastest way to abandon a self-hosted model is to point it at the one job it is worst at and conclude the whole idea was silly.
Fits comfortably. Anything where the output is short and the value is in the judgement rather than the prose. Classifying a message into one of a handful of buckets. Deciding whether a support ticket is urgent. Extracting five fields from an invoice as JSON. Summarising a thread into three sentences. Tagging a document. Rewriting a product description. Translating a short string. Detecting whether two records describe the same person. Redacting names from a paragraph before it goes anywhere else. Every one of those consumes a long input and produces a short output, which is precisely the asymmetry a CPU handles well.
Fits, with patience. Work with no human waiting on it. Overnight batches, queue workers, a nightly pass over the day's documents, a scheduled report. If nothing blocks on the answer, a token rate that would be intolerable in a chat window stops mattering at all — a job that runs for six minutes at three in the morning is simply a job that ran.
Does not fit. An interactive assistant that people will actually enjoy using: at these rates the cursor crawls and the experience is worse than no assistant. Long-form generation — write me two thousand words — where the entire output is the slow part. Coding agents that iterate over a large repository, where both the context and the quality bar exceed what a small model can hold. Anything needing a hundred-thousand-token context, where the cache alone outgrows the machine. And image or video models, which are a different discipline entirely and genuinely do want a GPU.
If your workload lives in that last group, the sane answer is not to buy a bigger CPU box — it is a hybrid. Keep a local model for the bulk of the calls and route the handful that need frontier reasoning to a hosted API, which is exactly the shape the guide on running an AI agent 24/7 describes for the runtime side. That guide deliberately left the model itself out of scope. This one is the missing half.
Work backwards from the job. Decide what the model has to do, pick the smallest size that does it, look up what that size occupies at Q4_K_M, then add the overheads:
RAM needed = model file + KV cache + ~2 GB for the system
3B Q4_K_M ≈ 2.0 GB KV cache at 8k context ≈ 0.5–1 GB
8B Q4_K_M ≈ 4.9 GB KV cache at 32k context ≈ 2–4 GB
14B Q4_K_M ≈ 9.0 GB
32B Q4_K_M ≈ 20.0 GB
70B Q4_K_M ≈ 40.0 GB
The key-value cache is the overhead people forget. Every token already in the conversation is kept in memory so the model does not recompute it, and that store grows with the context you allow. Doubling the context roughly doubles it. A model that fits with a 4k context can fail to fit at 32k, and it will fail on the tenth request rather than the first, which makes it look like a mystery instead of an arithmetic error.
There is no graceful degradation here. When the total exceeds RAM the kernel does not politely slow down: it starts paging model weights to and from disk, on every single token. A four-second generation becomes four minutes, the load average climbs into the tens, and everything else on the box — the database, the web server, your SSH session — suffers with it. Fitting in memory is not an optimisation. It is the requirement.
In the panel: Order → VPS → the tier the arithmetic pointed at, image Debian 12. No email address is required to open the account, no identity document is requested at any point, and the invoice settles in Monero, Bitcoin, Lightning or any of the other supported assets — which matters more here than on an ordinary web server, for reasons the pillar guide on anonymous VPS hosting lays out and the privacy chapter below applies to prompts specifically.
Harden the machine first — key-only SSH, a firewall that allows nothing you did not ask for, unattended security upgrades. The first-hour checklist takes about an hour and this is a box that will shortly be holding every question you ask it.
Then install the runner. Ollama is llama.cpp with a model registry, a REST API and a systemd unit wrapped around it, and the one-line installer sets up all three:
apt update && apt install -y curl
curl -fsSL https://ollama.com/install.sh | sh
systemctl status ollama --no-pager
ss -ltnp | grep 11434
# → 127.0.0.1:11434 — loopback only. Leave it that way.
That last line is the one to read twice. Out of the box the service listens on the loopback interface, which is exactly right, and the most common mistake made in the next ten minutes is to set OLLAMA_HOST to 0.0.0.0 because something on another machine could not reach it. Step 04 is how to reach it properly; there is no version of this where port 11434 faces the internet directly.
Two settings are worth putting in place now rather than discovering later. Models are stored under /usr/share/ollama by default and they are large, so point that at wherever you have room. And the default keeps a model resident in RAM for five minutes after the last request, which is generous on a box that is also doing other work:
systemctl edit ollama
[Service]
Environment="OLLAMA_MODELS=/var/lib/ollama/models"
Environment="OLLAMA_KEEP_ALIVE=30m"
Environment="OLLAMA_NUM_PARALLEL=1"
Environment="OLLAMA_MAX_LOADED_MODELS=1"
systemctl daemon-reload && systemctl restart ollama
Parallel requests and multiple loaded models both multiply memory use, and on a machine sized to hold exactly one model there is no spare gigabyte for a second copy. Serialising the queue is not a limitation on this hardware; it is the only configuration that does not fall over.
Pull a model. Any of the current 8B-class instruct models will do for a first measurement — they are close enough in size that the timing tells you about the hardware rather than about the model:
ollama pull llama3.1:8b # ~4.9 GB at Q4_K_M
ollama list
ollama ps # nothing resident yet
Now run one generation with timings switched on. The number that matters is the eval rate — tokens produced per second, once the prompt has been read:
ollama run llama3.1:8b --verbose "Reply with exactly one sentence about the Baltic Sea."
total duration: 14.2s
load duration: 3.1s
prompt eval count: 24 token(s)
prompt eval rate: 92.11 tokens/s ← reading: fast
eval count: 38 token(s)
eval rate: 3.42 tokens/s ← writing: the real ceiling
Two lines, two different worlds. Reading ran at ninety-odd tokens a second; writing at three and a half. That ratio is the asymmetry from the arithmetic chapter, measured on your own metal, and it is the single most useful fact you will collect today. It says: give this box long inputs and ask for short outputs.
The same measurement through the API, which is what you actually want if you intend to script the comparison across two or three model sizes:
apt install -y jq
curl -s http://127.0.0.1:11434/api/generate -d '{
"model": "llama3.1:8b",
"prompt": "List three Nordic capitals.",
"stream": false
}' | jq '{
tokens: .eval_count,
seconds: (.eval_duration / 1000000000),
rate: (.eval_count / (.eval_duration / 1000000000))
}'
Measure the size below and the size above, then stop. Pull a 3B and a 14B, run the same prompt through each, and write the three rates down. You now know exactly what the trade costs on this machine — usually something close to a doubling in each direction — and you can pick per job rather than per opinion. Then remove the models you are not keeping, because each of them is several gigabytes.
One caveat on the first run of any model: the load duration in that output is the time spent reading the weights off disk into RAM. It is only paid when the model is not already resident, which is what OLLAMA_KEEP_ALIVE controls. Do not include it when you compare rates, and do not be alarmed by it — on NVMe it is seconds, and it happens once.
This is the part of the guide that stops something bad from happening, so it gets said without hedging: the Ollama API has no password, no token, no user model and no permission system. Whatever can open a TCP connection to port 11434 can generate text on your CPU, enumerate the models you have pulled, pull new ones and delete the ones you use. There is no setting to turn on, because there is no mechanism to turn on.
Port 11434 is scanned continuously, for the obvious reason: an open model runner is free compute belonging to somebody else. The symptom is not an alert. It is a machine that feels slow for a fortnight and a bandwidth graph that does not match anything you did. Keep the service on loopback, and put something in front of it that asks who is calling.
If nothing outside this machine needs the model, stop here — you are already finished. Loopback plus a firewall is a complete answer, and an agent, an automation stack or a web app running on the same VPS reaches the model over 127.0.0.1 without any of what follows. Only continue if something on another machine has to call it.
The proxy, and the token. Generate a long random token, point an A record at the box, and let Caddy handle both the certificate and the door:
openssl rand -hex 32 # → the bearer token, store it in a password manager
apt install -y caddy
/etc/caddy/Caddyfile
llm.example.com {
@unauthorised not header Authorization "Bearer PASTE_THE_HEX_TOKEN_HERE"
respond @unauthorised "unauthorised" 401
reverse_proxy 127.0.0.1:11434 {
# Generation is slow by nature — do not let the proxy give up first.
transport http {
read_timeout 10m
}
}
}
systemctl reload caddy
ufw allow 80,443/tcp
ufw status # 11434 must appear nowhere
There is a small elegance in choosing a bearer token rather than basic authentication. Ollama exposes an OpenAI-compatible API, every OpenAI client sends its key as exactly that header, and so the token you just generated becomes the API key your applications already know how to carry. Nothing needs a custom header, and rotating access is one line in the Caddyfile and one environment variable at the caller.
Verify from a machine that is not the server. The first call should be refused and the second should answer:
curl -s -o /dev/null -w '%{http_code}\n' https://llm.example.com/api/tags
# → 401
curl -s https://llm.example.com/api/tags -H "Authorization: Bearer THE_TOKEN" | jq '.models[].name'
# → the list of models you pulled
If the model is going to be called by agents rather than by you, the exposure question deserves the fuller treatment in the guide on hosting a remote MCP server — same reasoning about TLS, tokens and what should be reachable from where, applied to a service that hands tools rather than tokens.
Ollama serves an OpenAI-compatible surface at /v1 alongside its own API. That is the whole integration story: anything built for OpenAI — the official SDKs, the framework wrappers, the automation nodes — works by changing the base URL and the key, and nothing else changes.
curl -s https://llm.example.com/v1/chat/completions \
-H "Authorization: Bearer THE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "llama3.1:8b",
"messages": [{"role":"user","content":"Reply with the word OK."}]
}' | jq -r '.choices[0].message.content'
From Python, the only lines that differ from a hosted setup are the two at the top:
from openai import OpenAI
client = OpenAI(
base_url="https://llm.example.com/v1",
api_key="THE_TOKEN",
)
r = client.chat.completions.create(
model="llama3.1:8b",
messages=[{"role": "user", "content": "Classify: 'the invoice is overdue'"}],
)
print(r.choices[0].message.content)
Ask for JSON, and get JSON. The single most useful feature for automation is constrained output. Ollama can force the answer to be valid JSON rather than hoping the model behaves, which turns a small model from an unreliable narrator into a dependable parser — and it is the difference between a workflow that runs unattended and one that breaks every fortieth item:
curl -s http://127.0.0.1:11434/api/generate -d '{
"model": "llama3.1:8b",
"prompt": "Extract the total and the currency: Invoice 4021, 1 249,90 EUR due 30 days.",
"format": "json",
"stream": false
}' | jq -r .response
# → {"total": 1249.90, "currency": "EUR"}
In an automation stack the same applies: n8n ships a dedicated Ollama node, and its OpenAI node accepts a custom base URL, so an existing workflow moves onto local inference by editing one credential. The n8n self-hosting guide covers the automation engine itself; run the two on separate boxes if you can, because an engine that idles at 700 MB and a model that wants five gigabytes are unhappy neighbours on an eight-gigabyte machine.
And the embeddings endpoint, which is where this box earns its keep. Embedding models are two orders of magnitude smaller than generation models and they do one pass per chunk with nothing to generate, so a CPU handles them at a rate that feels instant:
ollama pull nomic-embed-text # ~275 MB
curl -s http://127.0.0.1:11434/api/embed -d '{
"model": "nomic-embed-text",
"input": ["the first chunk of a document", "the second chunk"]
}' | jq '.embeddings | length'
Cap the context. The context length is the main dial on memory use, and the default is more generous than a small box wants. Set it globally to what your longest realistic prompt actually needs — most classification and extraction work is comfortable inside four thousand tokens — and raise it per request on the rare call that needs more:
Environment="OLLAMA_CONTEXT_LENGTH=4096"
curl -s http://127.0.0.1:11434/api/generate -d '{
"model": "llama3.1:8b",
"prompt": "…a long document…",
"options": { "num_ctx": 16384 },
"stream": false
}'
Decide how long the model stays in memory. A resident model is instant to call and holds several gigabytes hostage. On a machine dedicated to inference, keep it loaded forever; on a shared one, let it fall out after a while. The value travels per request too, so a nightly batch can pin the model for its run and release it at the end:
OLLAMA_KEEP_ALIVE=-1 # resident until the service restarts
OLLAMA_KEEP_ALIVE=30m # a sensible default on a shared box
OLLAMA_KEEP_ALIVE=0 # unload immediately after each request
ollama ps # what is resident right now, and how much it holds
Serialise the queue. Two concurrent requests against one model do not go twice as fast; they contend for the same saturated memory path and each one gets slower, and if Ollama decides to load a second copy to serve them the box runs out of RAM. On this class of hardware one request at a time, with a queue in front, is both the fastest and the only safe configuration — which is why OLLAMA_NUM_PARALLEL and OLLAMA_MAX_LOADED_MODELS were pinned to one back in step 02.
Watch the disk. Comparing four models leaves four models behind, and at five gigabytes each that is a disk filling quietly while nothing complains. Make the cleanup part of the comparison rather than a task for later:
ollama list
du -sh /var/lib/ollama/models
ollama rm mistral:7b qwen2.5:14b
Finally, back up what is not reproducible. The model weights are not — a pull brings them back. What is worth a repository is the configuration, the Caddyfile, the token, the prompts you refined over three weeks and, if you built one, the vector index behind your retrieval. The encrypted backups guide covers the mechanics; the include list for this box is short and worth writing down.
Everything above has been about generation, which is the part a CPU does slowly. Retrieval is the other half of most useful systems and it inverts the picture completely — which is why the cheapest tier on this page can do something genuinely valuable even if you never generate a token on it.
An embedding model turns a piece of text into a vector. It is measured in hundreds of megabytes rather than gigabytes, it does exactly one forward pass per chunk, and there is no token-by-token loop to be bound by memory bandwidth. On the same box where an 8B model writes three words a second, an embedding model will work through a document library at a rate that feels like file copying.
The shape of it. Split your documents into chunks of a few hundred words. Embed each chunk once and store the vector. At question time, embed the question, find the handful of nearest chunks, and hand those to a model along with the question. The storage does not need anything exotic: an SQLite database with a vector extension is enough for tens of thousands of chunks on a single VPS, and PostgreSQL with pgvector covers you well past that. A dedicated vector database is a fine thing to want later and an unnecessary dependency on day one.
Why this matters more than the speed. Look at what each half touches. The generation step sees one question and a few paragraphs. The embedding step sees <em>every document you own</em> — the whole archive, every contract, every note, every message, passed through the model one chunk at a time. If that step runs against a hosted endpoint, your entire corpus has been transmitted to a third party in order to be indexed. If it runs on your own machine, none of it moved.
That gives a genuinely good hybrid for anyone unwilling to give up frontier quality: index locally, retrieve locally, and send only the question and the three retrieved chunks to a hosted model when the answer needs to be excellent. The corpus stays home; a thin slice of it travels, and only on demand.
Two neighbours on this site make the pattern concrete. A self-hosted SearXNG gives the retrieval layer a private front-end for the open web rather than a corpus; a self-hosted document store gives it the corpus. Both of them, plus the model, fit on tiers that cost less per month than a single hosted seat.
People reason about model privacy as though the risk were in the output. It is not. The output is generic text; a thousand other people got something similar. The revealing artefact is the input — and a year of inputs is a remarkably complete portrait of an organisation.
Consider what a request log actually contains. The contract you pasted in to have summarised. The customer email you asked to be classified, with the customer in it. The medical letter, the salary figure, the resignation you were drafting, the code from a repository that is not public. Then the metadata around it: which questions, in what order, at what hour, from which address, across how many months. Nobody signs a document saying "here is our strategy" — but the sequence of questions is that document, assembled honestly, by you, one call at a time.
Layer one — what the endpoint keeps. Serious providers publish serious policies, and the good ones genuinely do not train on business API traffic. That is not the same promise as not retaining it. Requests are typically stored for some period for abuse monitoring, are reachable by staff under defined conditions, and are producible under legal process — which is exactly the right way to run a large platform and exactly the wrong place for text you would not email to a stranger. A model on your own machine has no such log unless you write one.
Layer two — the identity the log is joined to. Retention alone is only half the exposure. The other half is the key it joins to: an account, a company name, a billing address, a card. That join is what turns "someone asked about acquiring a competitor" into "this company asked, on that Tuesday". Removing the model from the equation removes the retention; removing the identity from the machine removes the join. A VPS opened without an email address or an identity document, in a Nordic jurisdiction, settled in Monero, is the second half of the same move.
Layer three — the logs you write yourself. Self-hosting moves the risk rather than deleting it, and it is worth being clear-eyed about where it lands. Your application probably logs its own prompts. Your reverse proxy logs every request line. A debugging session two months ago left verbose output in the journal. The model itself keeps nothing between calls, but the machinery around it can keep everything — so decide deliberately what is written down, set retention on it, and hold that log to the standard you were unwilling to accept from someone else.
None of which makes a local model a privacy guarantee on its own. It makes it the one architecture where the guarantee is yours to give: the text stays on hardware you rent, under a jurisdiction you chose, behind a token you issued, and it is answerable to nothing else.
The model plus its context cache exceeded RAM and the kernel began paging weights on every token. Fit in memory or drop a size — there is no middle setting.
OLLAMA_HOST was set to 0.0.0.0 to make a client work. The API has no authentication at all, and 11434 is scanned. Loopback plus a proxy that checks a token.
A 32B model was chosen for an interactive assistant. Match the size to the interaction: interactive wants small, quality wants asynchronous.
A growing conversation grows the cache, and the whole history is re-read each turn. Cap the context, and start a new one instead of appending forever.
The model stays resident after the last call by design. Set OLLAMA_KEEP_ALIVE to suit the box, and read ollama ps before blaming anything else.
Four candidates at five gigabytes each, none removed. Make ollama rm part of the comparison, and check the model directory when disk alerts fire.
Ten questions that decide whether a CPU-only model is the right tool for the job you have in mind.
Yes, with one honest caveat about speed. A quantised model runs perfectly well on ordinary server CPUs — the weights sit in RAM, the arithmetic is well within reach, and nothing about the process needs a graphics card. What a GPU buys is memory bandwidth, and bandwidth is what sets generation speed. So a CPU-only box will answer, correctly and completely, at somewhere between a few and a few dozen words per second depending on the model. That is slow for a chat window and entirely fine for the work most people actually automate: classifying a message, pulling structured JSON out of a document, summarising a page, embedding text for search, rewriting a paragraph. The question is never "can it" — it is "at what rate, and does that rate matter for this job".
Do the arithmetic rather than trusting anyone's benchmark, including ours. A dense model reads every one of its weights from memory to produce one token, so the ceiling is roughly memory bandwidth divided by model size. A 3B model quantised to about 2 GB, on a machine with an effective 20 GB/s, has a ceiling near 10 tokens a second; a 7B at 4.4 GB is near 4.5; a 32B at 20 GB is under 1. Real numbers land below the ceiling — call it 50 to 70 per cent — and vary with the host, the neighbours and the thread count. Prompt processing is a different matter entirely: it is compute-bound, runs many times faster, and is why summarising a long document is comfortable while chatting with a big model is not.
An 8B-class instruct model at Q4_K_M, which lands around 4.5 to 5 GB and leaves room for the operating system and the context cache. That size is the current sweet spot: good enough to follow instructions reliably, produce valid JSON when asked, summarise, classify and rewrite; small enough to stay responsive. Below it, a 3B model is genuinely useful for classification, tagging and routing and roughly twice as fast. Above it, a 14B is noticeably better at multi-step reasoning and roughly half the speed, which is a fair trade for batch work and a poor one for anything interactive. Start at 8B, measure, then move in whichever direction the measurement points.
The quantised file size, plus the context cache, plus room for the system — and the total must fit, because the failure mode is not slowness but collapse. When the model does not fit, the kernel starts swapping model weights to disk and a generation that should take four seconds takes four minutes while the load average climbs into the tens. As a working rule at Q4_K_M: a 3B model is about 2 GB, an 8B about 5 GB, a 14B about 9 GB, a 32B about 20 GB, a 70B about 40 GB. Add two gigabytes for Debian and its services, and one to four more for the key-value cache depending on how long a context you allow. Then buy the tier above the answer, not the tier that exactly matches it.
Ollama is llama.cpp, with a model registry, a REST API and a systemd service wrapped around it. Use Ollama unless you have a specific reason not to: one install command, ollama pull instead of hunting for GGUF files, an OpenAI-compatible endpoint, and sane defaults for thread count and memory. Reach for llama.cpp directly when you want a flag Ollama does not expose, when you want to pin an exact build for reproducibility, or when you are running one model with one configuration forever and would rather not have a daemon managing anything. Both run the same weights at the same speed; the difference is entirely in operations.
No, and pretending otherwise wastes your afternoon. A frontier model behind an API is larger than anything that fits on a virtual machine and it will be better at hard reasoning, long context and code. What a small local model is genuinely competitive at is the enormous middle of real work: deciding which of six categories an email belongs to, extracting five fields from an invoice, summarising a support thread, rewriting a description, tagging a document, judging whether two records are the same person. For that class of task the gap between an 8B and a frontier model is small, the cost difference is total, and the privacy difference is absolute. The productive posture is not "replace the API" but "stop sending it the ninety per cent that never needed to leave".
No — none at all, and this is the single most important operational fact in this guide. There is no password, no token, no user model. Anything that can open a TCP connection to port 11434 can generate text, list your models, pull new ones and delete existing ones. That port is routinely scanned, and unprotected instances are found and used as free compute by strangers, which shows up first as a mysterious load average and later as a bandwidth bill. Keep Ollama bound to 127.0.0.1, put a reverse proxy in front of it, terminate TLS there, and require a bearer token or client certificate at the proxy. If nothing outside the box needs the model, do not expose it at all.
Yes, and it usually takes one field. Ollama serves an OpenAI-compatible API at /v1, so any client that lets you set a base URL — the OpenAI SDKs, LangChain, the n8n OpenAI node, most agent frameworks — will talk to it by pointing at https://your-host/v1 and sending any non-empty string as the key. n8n also ships a dedicated Ollama node. The pattern that works well in practice is a hybrid one: route the bulk, boring, high-volume calls to the local model and reserve the hosted API for the handful of steps that genuinely need frontier reasoning, with a fallback if the local one refuses to produce valid JSON.
They are the best-value thing on this whole page. Embedding models are tiny — tens to hundreds of megabytes rather than gigabytes — and they run one forward pass per chunk with no token-by-token generation, so a CPU chews through them happily. That means the entire retrieval half of a RAG system — index your documents, embed the query, find the nearest chunks — runs comfortably on the cheapest tier, and it is also the half that touches every document you own. Even if you decide the generation step belongs on a hosted API, moving the embedding step onto your own machine keeps your corpus off someone else's.
Three reasons, in ascending order of how much they matter. Cost has a shape: an API is cheaper until it is not, and a workflow that classifies fifty thousand messages a month has a bill that grows while a $7.90 VPS does not. Availability: no rate limit, no deprecation of the model you built around, no outage on someone else's status page. And the one that decides it — your prompts are the most revealing text you produce. Not the answers, the questions. What you asked, about whom, on which day, in what order. A hosted endpoint sees all of it, joined to a billing identity. A model running on a machine you rented without an identity document, in a Nordic jurisdiction, paid in Monero, sees the same text and reports it to nobody.
Garrison (4 vCPU, 8 GB, 240 GB NVMe, $7.90/mo) runs an 8B model with room for the context cache and the system — the tier most people should start on. Ravelin doubles the memory for 14B and long contexts. No email at signup, no identity document, and no per-token bill.
Last reviewed · 2026-08-24 · Sources · Ollama documentation and API reference, llama.cpp documentation, Caddy reverse-proxy documentation, published model cards for the quantised builds referenced · Cadence · yearly
This guide is one spoke of a larger series. The pillar walks the three privacy layers end to end — the sibling spokes below dive into the specifics.
Three independent layers — signup, payment, network — explained, legal context included, common mistakes flagged.
Deploy your own MCP server on a no-KYC VPS — TLS, streamable HTTP, OAuth.
Host an MCP server with no ID — the privacy stack, crypto-paid.
Move the agent off your laptop — sizing, systemd, secrets, spend caps.
Docker Compose, PostgreSQL, working webhooks — automation you own.