Skip to content
All articles

Private AI for sensitive data: a local LLM setup that passes the security review

Health records, customer contracts, unreleased financials: some data must never reach a public AI API, and your security team is right to insist. That usually ends the AI conversation, and the people who would benefit most keep copying text by hand. It doesn't have to. This is the architecture I use for private AI inside your own network: a test set first, the smallest model that passes it, retrieval that respects access rules, an anonymizer at the edge and an audit trail for everything.

Updated 11 min read

1. Classify the data and the task

Start with two short lists. The first names the data classes involved: personal data, health data, trade secrets, regulated financial data. The second names the tasks: classification, extraction from documents, summaries, drafting replies, answering questions over internal documents. Together they decide how strict the setup must be and how big the model needs to be.

The task matters more than people expect. Classification, extraction and summaries work well with small open-weight models, often at a quality that surprises the business. Multi-step reasoning across many long documents needs a bigger model and more hardware. Frameworks like the NIST AI Risk Management Framework and the Azure Well-Architected guidance for AI workloads both start here for a reason: map the use case and its risks before you pick technology.

Tip from practice

Write the data classes into the system design, not just into a policy PDF. Then the gateway can route by class: public data may go to a hosted model, confidential data never does.

Private AI inside your network: identity at the door, permissions in retrieval, an anonymizer at the edge and an audit log for every request.
Architecture of private AI inside your network: users sign in through an SSO gateway, a PII anonymizer sits at the edge, a local LLM runs on GPU nodes with vLLM or Ollama, retrieval uses local embeddings and pgvector with access rules, every request goes to an audit log, and there is no egress to public AI APIs.

2. Build the test set before choosing a model

Collect 30 to 50 real examples with the answer you expect, and have someone from the business review them. Include the hard cases on purpose: scanned PDFs, mixed languages, tables, the contract with the odd clause, the email thread where the answer is in the third reply. Store them as simple JSON lines, one case per line, with the source file and the expected answer.

Score every candidate model and prompt against that set: accuracy, latency and cost per request. That set decides the model, not a leaderboard, because public benchmarks rarely look like your documents. And it keeps deciding: every time someone changes a prompt, a retrieval setting or the model itself, the same set runs again. Without it, every change is a matter of opinion; with it, a change is a number on a dashboard.

json
{"id": "contract-017", "file": "contracts/017.pdf",
 "question": "What is the notice period?",
 "expected": "3 months to the end of a quarter",
 "must_cite": "section 9.2", "data_class": "confidential"}

3. Size the model to your hardware

GPU memory decides what runs. As a rule of thumb, the weights need parameters times bytes per weight: a 14-billion-parameter model is about 28 GB at 16-bit, 14 GB at 8-bit and 7 GB at 4-bit. On top come the KV cache, which grows with context length and the number of parallel users, and some runtime overhead. Try the calculator below with your own numbers before you talk to a hardware vendor.

Quantization is the lever that makes this affordable. 4-bit models lose a little accuracy on most tasks, sometimes none that your test set can detect, and they run on a single workstation GPU. For prototypes, a desktop GPU or a Mac with enough unified memory is fine. For production, plan a GPU node with headroom for peak concurrency, because a model that fits exactly will queue requests as soon as three people use it at once.

Tip from practice

Test the quantized model you will actually run, not the full-precision one from the model card. Published benchmarks are almost always measured at 16-bit.

Interactive

Which GPU does your model need?

Pick a model size and precision. Weights take parameters × bytes per parameter; about 20% on top covers the KV cache for normal context lengths.

Model size (billion parameters)

Precision

≈ 19 GB

GPU memory needed

1×

fits on 24 GB (RTX 4090)

  • 1× 24 GB (RTX 4090)
  • 1× 32 GB (RTX 5090)
  • 1× 48 GB (L40S)
  • 1× 80 GB (H100)
  • 1× 96 GB (RTX PRO 6000 Blackwell)
  • 1× 141 GB (H200)

Rule of thumb for planning, not a benchmark. Long contexts and many parallel users need more KV cache; 4-bit costs a little quality.

All calculators and checklists

4. Serve it like any other internal API

For a handful of users, Ollama or llama.cpp is the quickest path to a working endpoint. As soon as many people or agents hit the same model, switch to vLLM: continuous batching and paged attention get far more throughput out of the same GPU, and it exposes an OpenAI-compatible API, so your applications don't care which model sits behind it. Swapping models later becomes a configuration change instead of a project.

Run the server in your own network or private cloud, behind the same gateway, TLS, monitoring and patching routine as every other service. Pin the model version and the container image, store the weights in your own registry or object storage, and treat a model upgrade like a database upgrade: test set first, then staging, then production. The AWS Well-Architected Generative AI Lens describes the same operating discipline for hosted models; it applies just as much on your own hardware.

bash
# OpenAI-compatible endpoint on an internal GPU node (example model;
# pick yours with the test set)
vllm serve Qwen/Qwen2.5-14B-Instruct-AWQ \
  --quantization awq \
  --max-model-len 32768 \
  --gpu-memory-utilization 0.90 \
  --host 0.0.0.0 --port 8000

# Any OpenAI client can now talk to it, inside your network only
curl http://llm.internal:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model": "Qwen/Qwen2.5-14B-Instruct-AWQ",
       "messages": [{"role": "user", "content": "Summarise clause 9.2"}]}'
What is vLLM? Efficient AI Inference for Large Language Models, IBM Technology (YouTube). Loads from YouTube only after you click.

5. Bring your documents, with their access rules

Retrieval gives the model the right pages for each question instead of training it on your data. Split documents into chunks, create embeddings with a local embedding model, and store them in pgvector inside the Postgres you probably already run. Keep each chunk's source, version and access list right next to the vector, because you'll need all three at query time.

The crucial detail is the order of operations. Filter by what the user is allowed to see before you rank by similarity, so the model never even receives a paragraph the person couldn't open in the original system. Then cite the source in every answer, with a link, so people can check instead of trust. OWASP lists sensitive information disclosure among the top risks for LLM applications, and retrieval without permissions is the most common way it happens.

sql
-- Chunks with their source and who may read them
create extension if not exists vector;
create table doc_chunks (
  id bigserial primary key,
  source text not null,
  allowed_groups text[] not null,
  content text not null,
  embedding vector(1024)
);

-- Nearest chunks the user is allowed to see
select source, content
from doc_chunks
where allowed_groups && $1      -- the user's groups
order by embedding <=> $2       -- cosine distance
limit 8;

Tip from practice

Index access rules, not just text. Retrieval that ignores permissions is a data leak with extra steps.

6. Anonymize at the edge

Sometimes one step really benefits from a large hosted model, for example polishing a final text or a hard reasoning task on a summary. Then the local side does the sensitive work first: it removes names, addresses, account numbers and case IDs, replaces them with placeholders, sends only the anonymized text out, and puts the real values back into the answer locally.

Tools like Microsoft Presidio detect the common entity types out of the box; add your own recognizers for customer numbers, contract IDs or internal codes, and test them with the same discipline as the model. Log every request that leaves the network: when, which model, which data class and how many entities were replaced. If the log ever shows a confidential class going out, you have a bug, and you'll find it the same day instead of in an audit a year later.

7. Identity, audit and no surprise egress

Put the model behind your single sign-on, so every request carries a person or a service identity. Write an audit log with user, model, data class and the sources that were retrieved, and keep it as long as your compliance rules require. That log answers the two questions every auditor asks: who saw what, and did anything leave?

Then close the doors you don't need. Block outbound traffic from the GPU nodes by default, except to the registries you use for updates, and make every exception a reviewed change. Give AI agents their own service identities with read-only access first; write access comes later, one tool at a time, with a human approval step. The security review gets short when you can show the architecture diagram, the audit log and the firewall rules in one meeting, which is exactly what the security pillar of the Azure Well-Architected Framework asks for: defence in depth, least privilege and evidence.

Tip from practice

Run one deliberate egress test before go-live: from a GPU node, try to reach a public AI API. If it works, your firewall rule doesn't.

8. Measure, then scale

Run the test set on every model or prompt change and track accuracy, latency and cost per request on a dashboard next to your other services. Watch GPU utilization: as a rough guide, far below a third means you are paying for idle silicon, and constantly above 85 percent means queues and growing latency. Both are signals to resize, not to panic.

Once quality holds and people use it daily, move from the prototype box to a small GPU node pool in your own data center or private cloud, with autoscaling on queue length rather than CPU. Add a second model only when the test set shows a task the first can't handle. The result is boring in the best way: an internal service with an owner, a test suite, an audit trail and a cost per request you can explain to finance.

Takeaway

Private AI is mostly engineering discipline: a test set, the smallest model that passes it, permissions in retrieval and an audit log. Get those right and sensitive data and AI get along fine.

More articles

    Get in touch

    Tell me what you're building. I'll reply within 1–2 business days.

    Dominik Heinrich
    Company
    MADO Consulting

    Available worldwide, remote. On-site on request, depending on the country.

    I reply within 1–2 business days. Free slots: see the calendar

    Opens Calendly's booking calendar here. Loading it sends data to Calendly (see Impressum).