EU AI sovereignty: self-hosted LLMs and the AI Act in practice

What it takes to run LLMs where the data may not leave the EU, and what the AI Act changes for the engineering team.

The problem

When a product sends a prompt to a hosted model API, the prompt travels with everything attached to it: the user's question, the documents retrieved to answer it, and often a customer's personal data. If that API runs outside the EU, the product's customers have a data transfer to account for. If a company subject to US law operates it, they have a second question even when the servers are in the EU.

Two legal facts frame the conversation. Under GDPR (DSGVO in German), personal data may go to a third country only when the conditions of Chapter V are met (Article 44). And the US CLOUD Act of 2018 says a provider's duty to preserve or disclose data under US legal process covers data in its "possession, custody, or control, regardless of whether such communication, record, or other information is located within or outside of the United States" (18 U.S.C. § 2713). That is the sovereignty concern: it is about who can be compelled to hand data over, not where the disk sits.

The questions in a data protection review are predictable, and engineering has to answer most of them:

The last three can only be answered from your own records. No provider contract answers them for you.

The quiet leak is the one teams do not think of as inference. An embedding model called over a US API sees every document you index, the same exposure as sending the documents themselves. So do hosted error trackers that capture request bodies.

The levers that actually work

Self-hosted open-weight models

Running open-weight models such as Llama, Qwen or Mistral on hardware you control removes the transfer question for the prompt. A distinction worth making to customers: the origin of the weights matters far less than where inference runs. Weights running on your hardware send data nowhere.

The common split: Ollama on developer machines, because a model runs with one command; vLLM for serving, because continuous batching and paged attention let many concurrent requests share a GPU instead of queuing behind each other; llama.cpp where there is no GPU, including air-gapped installs. All three expose an OpenAI-compatible API, which is what makes the gateway below possible.

The costs are real. On nCloud, the sovereign cloud platform I run through Napradha, a 1.5B model at 4-bit ran at 13.9 tokens per second on CPUs without AVX-512: enough to prove a pipeline, not to serve users. The GPU tier is a single Tesla T4, passed through to one VM, serving a 7B model at 4-bit through vLLM. One T4 is one T4: a single popular workload would saturate it. Read the licenses too: some carry use restrictions that matter once you ship software into a customer's data center. Measure quality on your own tasks, the same way you would decide whether the cheaper model is good enough.

A gateway in front of every model

Put one entry point in front of every model: local, EU-hosted and customer-supplied. The decisions live there, so no caller can skip them:

On nCloud the gateway enforces tenant authorization, a model allowlist, jurisdiction and per-tenant token budgets, with a request-rate limit per client in front; per-tenant rate limits and redaction are not built yet. On an earlier production Claude platform for an EU FinTech start-up, I built the other half: PII masking, per-tenant rate budgets and routing with model fallback, with zero PII incidents in production logs after rollout.

Audit trails

Record every call, refusals and failures included: who asked, for which tenant, which model and provider answered, what went in and out, token counts, latency and outcome. Make the log tamper-evident: when each record carries its predecessor's hash, editing one breaks verification from that point on. Archive it somewhere the writer cannot quietly overwrite, record deletions in the log itself, and make it exportable in a form an auditor can verify without trusting your software.

A log that answers "what did the model see" contains personal data, so GDPR's data minimization and storage limitation apply to the log as well. Retention is a decision to take with the DPO, not a default in code.

I built this first on nCloud, before choosing any model. The record format, archive, retention and what I would change are in an audit trail for every LLM call. Telemetry belongs beside the audit log, not in place of it; see agent observability.

No US-vendor dependencies in the data path

A self-hosted model still leaks if the install pulls images from a foreign registry at runtime, phones home for a license check, or ships telemetry to a hosted backend. nCloud is built from Apache-licensed open source on rented servers in Germany, under one rule: no US-vendor code in the data path.

The test is installing with the internet severed. On nCloud, the AI platform installed onto a fresh Kubernetes cluster with the internet blocked, from one bundle with every artifact hash-verified: 11 checks passed, 0 failed, 1 skipped. The skipped check was the audit gateway, whose secrets store was outside that test's scope. Air gap means no internet, not no network: DNS, time, a registry and the management API still have to be reachable inside the facility. More on the platform side under Kubernetes for AI.

What the AI Act asks of engineering teams

These dates come from the AI Act on EUR-Lex as amended by the Digital Omnibus on AI, Regulation (EU) 2026/1744, published in the Official Journal on 24 July 2026 and in force since 27 July 2026. This is not legal advice, and whether a system is high-risk is a legal classification.

For most teams shipping an LLM feature, Articles 4 and 50 are the ones already in force. Build toward the logging duties now, because logs cannot be produced retroactively.

How to measure it

Share of requests that stayed in the perimeter. From the gateway's records: calls served by an in-perimeter route divided by all calls, refusals included. Count embeddings and tool calls, not only chat completions, and break it down per tenant. Anything below 100% should trace to a decision someone recorded.

Cost per request against the hosted API. Self-hosted cost is mostly fixed, so utilization decides it:

self-hosted cost per request = (monthly server cost + operations time) / requests served that month
hosted cost per request      = input tokens x input price + output tokens x output price

Use the token counts the gateway recorded for both sides. An idle GPU is the most expensive line on the bill, so design for attaching GPU capacity only while there is work for it. The general method is under LLM cost engineering, with a worked production example in cutting Claude inference costs by 38%.

Whether an audit question can be answered from the logs alone. Run a drill: "show every call that touched customer X's data last quarter, which models answered, and prove the records are unaltered." The bar I set on nCloud is that someone who did not build the system answers it from the export within an hour. Watch the evidence pipeline too, and alert on a job that did not run, not only on one that failed: a scheduled job stuck waiting for a volume produces no failed run and no alert.

Common mistakes

Every post on EU AI sovereignty & self-hosted LLMs