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.
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.
{"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 checklists4. 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.
# 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"}]}'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.
-- 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.
Real projects behind this
Try it yourself
Sources and official guides
- Azure Well-Architected Framework: AI workloads
- Azure Well-Architected Framework: Security pillar
- AWS Well-Architected: Generative AI Lens
- NIST AI Risk Management Framework
- OWASP Top 10 for Large Language Model Applications
- vLLM documentation
- pgvector: vector similarity search for Postgres
- Microsoft Presidio: data protection and de-identification
More articles
- 3,025 pull requests in ten months: an AI-assisted delivery process, from the inside
- The EU AI Act for mid-sized companies: what applies to you, and when
- FinOps for AI spend: tokens, caching, routing and budgets that hold
- Making a restaurant network visible in AI answers: a GEO project, step by step
- How this site is built: static where it can be, streaming where it must
- Kubernetes cost on EKS and GKE: the same playbook, different levers
- A landing zone for mid-sized companies, in Terraform or Bicep: the parts that matter
- The legacy migration playbook: waves, parallel runs, and identical row counts and checksums
- MCP security in depth: how to let AI assistants into your product without opening the doors
- Monitoring on business outcomes: green dashboards, empty checkout
- Phone AI agents: the architecture, and the pitfalls nobody shows in the demo
- Restore tests: a backup you have never restored is only a hope
- Streaming AI answers on Azure Static Web Apps: the one header that buffered everything
- Building an AI assistant for WhatsApp, end to end: what it takes beyond the demo
- Air-gapped systems: running critical software with no internet
- Production AI on AWS: Amazon Bedrock and AgentCore in a private setup
- Production AI on Microsoft Foundry: a private reference setup for agents
- /goal, /loop or routines: stop babysitting your coding agent
- Claude's new plans: Max, Ultra and what they mean for teams
- OpenAI Codex at the end of 2026: goal mode, dots and how to lock it down
- GitHub Copilot, Claude Code or Codex? How I'd choose for a company
- GEO basics: how to show up in AI answers
- Production AI on Google Cloud: from Vertex AI to Gemini Enterprise Agent Platform
- Local LLMs in an air-gapped network: AI for your most sensitive data
- I want to run a local LLM: which server should I buy?
- MiniMax M3: an open frontier model you can run yourself?
- Prompt research at scale with an open model
- SEO and GEO together: a practical audit in 6 steps
- Right-size Kubernetes: the playbook that took a cluster from 7 to 4 nodes
- From AI prototype to production: the eight layers I add, in order
