Skip to content

GUIDES

Self-Hosted AI Coding: What Going Private Actually Removes

Self hosted ai coding is sold as swapping one endpoint for a weight file. We audited every outbound service in this repository: a local model removes 1 of 7.

Self hosted ai coding is sold as a swap: replace the model endpoint with a weight file on your own hardware and the data stops leaving. That framing counts one dependency. We enumerated every outbound service this repository actually depends on — at author time, install time, build time, runtime and publish time — and there are seven. A local model removes one of them. The other six have nothing to do with the model vendor, and the one that stops you from running a local model here is not model quality. It is memory. Measured on this machine on 2026-08-26.

npm run check:selfhost reads the tree and no network:

Terminal
npm run check:selfhost
# → 7 outbound services: 1 model · 5 substitutable · 1 structural
# → 37 host literals in source: 13 egress · 4 outbound-link · 10 citation · 5 identifier
# → 18 names in package.json resolve to 548 packages from 1 registry
# → instruction set: 164,717 tokens across 3 files
# → 2 of 7 local models hold it; 0 of those 2 fit the memory

Key takeaways

  • A local model closes 1 of 7 outbound services. The other six are the npm registry, Google Fonts, Cloudflare D1, R2 read, R2 write and GitHub — none of them the model vendor's.
  • Five of the seven are substitutable and one is structural. Every substitution is a named piece of software with a real operator burden, not a checkbox.
  • The binding constraint is memory, not the context window. Two of seven local models declare a window big enough for this project's instruction set; their weights alone are 84% and 109% of this machine's RAM.
  • Eighteen dependency names resolve to 548 packages. That install path is a bigger unreviewed surface than every prompt this project has ever sent.
  • The instruction set is 164,717 tokens, and 148,602 of them are one file — which is what a private stack would have to hold before it does any work.

The short answer

Running the model locally removes one outbound service out of seven. It is a real removal — it is the one carrying your source — but self-hosted AI coding is not the same thing as a private stack, and the audit that tells them apart takes an afternoon rather than a migration.

The distinction matters because the two are bought differently. The model endpoint is replaced by a decision: pick a weight file, run a server, accept the quality difference. The other six are replaced by operating software you now own — a registry mirror, an object store, a database, a git host. Anyone comparing hosted agents on their data handling is reading only the first column; our comparison of the major AI coding assistants covers what each vendor's own controls do and do not reach, and this article measures what sits outside all of them.

There is a second answer underneath it. On this machine, the reason to stay hosted is not that the local models are worse. It is that the two with a large enough context window do not fit in RAM.

What self hosted ai coding actually replaces

The check classifies each service by what a local weight file does to it. Three classes, and the sizes of them are the argument:

  • model — a local weight file removes it. There is exactly one, and it carries every prompt, file excerpt and command output the agent reads.
  • substitutable — a self-hostable replacement exists, is named, and costs work. There are five.
  • structural — removing it means not doing the thing. There is one: a public site serves images to readers, and readers are the point.

Each entry is verified against evidence in the tree on every run rather than asserted from a list. If lib/d1.ts stops containing a Cloudflare hostname, the check fails and says so, because a service that quietly disappears from a catalogue is indistinguishable from a catalogue that has gone stale.

The seven services this project depends on

ServicePhaseWhat a local model does
Anthropic Messages APIauthorremoved
npm registryinstallsubstitutable — Verdaccio
Google Fontsbuildsubstitutable — next/font/local
Cloudflare D1 HTTP APIruntimesubstitutable — Postgres or SQLite
R2 public readruntimestructural
R2 S3 API writepublishsubstitutable — MinIO
GitHub origin remotepublishsubstitutable — Forgejo

Read the phase column before the substitution column, because it is what makes self-hosted AI coding a smaller change than it sounds. Four of the seven have nothing to do with the agent at all — they are properties of a Next.js site that stores images and subscribers, and they would be there if every line had been typed by hand.

That is the part the framing hides. "Self-hosted" attaches to the model because the model is what you just bought, but the audit sorts by when data leaves, and only one of the five phases is the one the agent is in. The git remote alone carries strictly more of this project than the model endpoint ever did: every commit contains the full text of every article and every policy file, including the three the agent reads.

The substitutions are all real software with real operators. Verdaccio mirrors the registry on first fetch. MinIO speaks the same S3 API that scripts/upload-r2.mjs already uses, so that one is a hostname change. Forgejo replaces the remote. None is exotic, and none is free of the thing you were buying when you paid someone else to run it.

A URL in a file is not a request

The check also scans every source file for host literals, so a service nobody wrote down still gets caught. That scan found 37 hosts, and its first version reported nine of them as unrecorded egress. All nine were wrong.

The classifier now sorts each occurrence by what it is, and the distribution is the useful part:

Occurrence kindHostsWhat it means
catalogued5already in the table above
egress13fetched by author-time research scripts
outbound-link4rendered into HTML; the reader's browser goes there
citation10recorded so a figure can be re-checked
identifier5a namespace or this site's own origin

The thirteen are worth naming precisely, because they are the honest surprise. They are hosts that this project's own measurement scripts fetch — vendor pricing pages, a model registry, a package download API — to write articles. They are real requests leaving a real machine, and a local model does not touch a single one of them, because they were never the model's requests. They are the author's.

The four outbound-link hosts leave nothing at all. They are href values in a footer and a share button: the reader's browser makes that request, from the reader's network, and no amount of self-hosting changes it.

What one npm install reaches

Eighteen names appear in package.json. The lockfile resolves 548 packages, all from one registry.

Terminal
npm run check:selfhost
# → names in package.json            18
# → resolved entries in lockfile    548
# →   from registry.npmjs.org       548
# → MCP servers                       2 local process(es), 0 remote

That ratio is 30 to 1, and it is the largest body of code in this project that nobody read. It is also, on any honest accounting of exposure, larger than the model endpoint: a package runs an install script on your machine with your permissions, which is a strictly stronger capability than reading a file and answering about it. Our audit of AI coding security risks reached the same 548 from the other direction, and neither audit found a way to make that number smaller by choosing a different model.

The two MCP servers are the one place this project already went local by default. Both are stdio — a node process on this machine — so a tool call never leaves. That was not a privacy decision; it is what .mcp.json describes, and the check fails if a remote transport ever appears in it.

What a private ai coding stack has to hold

The second half of the measurement is the one that changed the recommendation.

Before a local model does any work here, it has to hold the instructions the workflow requires. Three files:

FileTokensWhen it is read
CLAUDE.md2,409every session, in full
content-rules.md13,706before drafting
roadmap.md148,602to choose the article, and to record it
instruction set164,717

Token counts use 3.4 bytes per token — the ratio scripts/check-context-fill.mjs derived by differencing two headless turns on 2026-08-18, not the folklore 4, which undercounts Markdown in this repository by about 18%.

One file is 90% of the total. roadmap.md is 493 kB of editorial history, and it is not optional context: it decides which article is next and it is rewritten when the article ships. A stack that cannot hold it cannot run this workflow, whatever its benchmark scores say. The mechanics of why a full window degrades a coding agent are covered separately; what matters here is the floor, and the floor is 164,717 tokens.

The window is not the wall

We priced this against the seven local coding models our review of local LLMs for coding measured on 2026-08-18, on this machine — 16 GB of RAM, 1 GB of dedicated VRAM.

ModelContext windowQ4 weights, as share of RAM
Qwen2.5-Coder 7B32,76827%
DeepSeek-Coder-V2-Lite 16B163,84061%
gpt-oss 20B131,07268%
Devstral Small 2 24B393,21684%
Qwen3-Coder 30B-A3B262,144109%
Qwen2.5-Coder 32B32,768116%

Two of the seven hold the instruction set. Both are the ones that do not fit in memory. Devstral's 4-bit weights take 84% of this machine's RAM before a single token of context is cached; Qwen3-Coder's exceed total RAM outright. Every share in that column is a floor, because the KV cache for a 262k-token window sits on top of the weights and is not derivable from the model card.

And zero of the seven were ever executed here. The earlier review recorded three blockers on this machine — a native module that fell back to a source build with no toolchain, a prebuilt binary quarantined by Defender as a machine-learning heuristic false positive, and a second binary missing its Visual C++ runtime. None of the three was the model. The pattern held: on every measurement this project has taken of a private stack, the thing that stopped it was the harness around the model, which is the same thing the model-plus-harness split predicts.

What did not work

Three things, and the third is the one that changed the article.

  • The host scan's first run reported nine unrecorded egress points, and all nine were the classifier. Four were href values — two social profiles, a share intent and an ad-settings link — which the reader's browser follows and this server never requests. Five were citations: a documentation URL on a // comment line, and four hosts in scripts/prices.json, a ledger recording where each price was read. A directory rule for scripts/fixtures/ never saw the ledger because it is not in that directory.
  • Fixing that hid the opposite error. The line-level rule counted only URLs sitting on the same line as a fetch( call, so a URL assigned to a const and fetched later was invisible. The corrected file-level rule took the egress count from 3 to 13 — a four-fold undercount, in the direction that would have flattered the article. The rule is deliberately coarse now: in a module that calls fetch at all, a bare URL literal is a destination. It can over-count. It cannot hide.
  • The residency budget was written down before it was measured, and it was wrong. The check records an accepted state and fails when reality moves off it. We recorded modelsHoldingSet: 0 — the shape the article wanted, where no local model can hold the instruction set. The measurement returned 2. The budget is now 2, and the argument moved from the window is too small to the two models with a big enough window do not fit in memory, which is both true and a different recommendation.

That third one is the same failure this project found in a security audit that confirmed a Content-Security-Policy from the comment explaining its absence. A check whose expected value is authored from the conclusion will confirm the conclusion.

Where an open source ai coding stack earns its cost

The decision rule that falls out of the numbers, in order:

  • Name the boundary first. "Source must not reach a third party" is a boundary you can audit. "More private" is not, and it produces the swap that closes one service out of seven. Every open source ai coding stack worth building starts from a boundary someone can state in a sentence.
  • Enumerate by phase, not by vendor. Author, install, build, runtime, publish. This project's model endpoint sits in one phase; four of its seven services sit in phases the agent never enters.
  • Take the structural ones off the table early. A public site serves images publicly. Counting that as a privacy gap makes the total look worse and the plan look longer.
  • Price memory before quality. Weights plus KV cache, against the RAM you have. On a 16 GB machine, that eliminates the models with the useful windows before benchmark scores are relevant.
  • Do the cheap substitutions whether or not you go local. A registry mirror and self-hosted fonts remove two services, need no GPU, and survive the decision to stay hosted.

If the boundary you named is only crossed by the model endpoint, a local weight file is the whole project and it is worth doing. If it is crossed by the git remote, this is a migration and the model is its smallest part.

Common mistakes when going private

Every one of these cost us time on this audit, and the first is the one that makes self hosted ai coding look finished when it has barely started.

  • Counting the model as the surface. It is one of seven here, and the git remote carries strictly more of this repository than the model endpoint does. The tempting version of this mistake is that the model is the only one with a vendor to evaluate.
  • Reading a context window off a model card and stopping. Two models here declare windows of 262k and 393k tokens. Both need more RAM for their weights alone than a 16 GB machine has for everything, and the KV cache is not in that figure.
  • Treating an outbound link as egress. A href in a footer costs the reader a request, not you. Four hosts in this scan were that, and counting them inflated the surface the article was about by more than half.
  • Assuming a self-hosted registry is the hard part. The 548 packages behind 18 names are unread either way. Mirroring changes who serves them, not who has read them.
  • Writing the expected result into the check. We did it here, in one line, and it pointed at the wrong conclusion until the measurement contradicted it.

What we are not claiming

No local model was run. Nothing here measures output quality, and the model comparison is context and memory only. The three execution blockers are this machine's, recorded on 2026-08-18, and a machine with a working toolchain and more RAM would clear all three.

The classifier is lexical, not a call graph. It decides that a URL on a comment line is a citation and that a URL next to href is a link, by reading the line. It cannot follow a hostname assembled at runtime — which is exactly why the R2 public hostname is catalogued by hand rather than found by the scan.

The catalogue is enumerated, not discovered. A service with no literal in source and no evidence file is invisible to this check, which is the same limitation as the control-plane audit's surface list. The scan exists to catch what the catalogue misses; it caught nothing new on this tree, which is weaker evidence than it looks.

One repository, one stack, one operator. Seven services is a property of a Next.js site with images, a database and a git remote, not a constant of self-hosted AI coding. A stack with telemetry, error reporting, analytics and a CI provider would count higher, and the ratio would move further against the model.

Conclusion

Do the enumeration before you buy hardware for private ai coding. List every outbound service by phase, mark the one the model endpoint owns, and look at the ratio: here it is one of seven, and the two substitutions worth doing first — a registry mirror and local fonts — need no GPU and survive either decision. If the boundary you can name is crossed only by that one service, run a local model and accept the quality trade. If it is crossed by your git remote, the model was never the project. Read what open-source AI coding tools actually give you next, then price the memory.

Frequently asked questions

Does self-hosting an AI coding model make my stack private?
It closes one dependency. We enumerated every outbound service in this repository and found seven: the model API, the npm registry, Google Fonts at build time, Cloudflare D1 at runtime, R2 for image reads and writes, and GitHub as the origin remote. A local weight file removes exactly one of the seven. Five have self-hostable replacements that cost real work, and one is structural — a public site serves images publicly by definition.
What hardware do you need for a self-hosted AI coding stack?
More than the model card suggests, because the context window and the weights compete for the same memory. Of seven local coding models priced in this project, only two declare a window large enough to hold this repository's instruction set. Both are 24B and 30B: their 4-bit weights alone are 84% and 109% of a 16 GB machine's RAM, before any of the KV cache that a 262k-token window needs.
Is an open source AI coding stack cheaper than a hosted one?
It moves the cost rather than removing it. The measurable substitutions here are a registry mirror, self-hosted fonts, a Postgres or SQLite swap behind an existing choke point, an S3-compatible object store and a self-hosted git remote. Each is a known piece of software with a known operator burden. None of them is the model, which is the one people mean when they say self-hosted.
What actually leaves your machine when you use a hosted coding agent?
Every prompt, file excerpt and command output the agent reads. That is the one thing a local model stops. It does not stop the package names in your install, the commits pushed to a remote, the SQL your runtime issues, or the font request in your build. Those are separate services with separate operators, and none of them appears in a vendor's privacy documentation because none of them is the vendor's.
Should I switch to a private AI coding setup?
Switch if a specific class of data must not leave a specific boundary and you can name it. Do not switch to reduce a vague exposure, because the audit that produces the number usually finds the model endpoint is not the largest surface. Run the enumeration first: list every outbound service by phase, and see how many of them a local weight file touches.

Muhammad Kashif

Founder and editor of Devventa, covering AI coding assistants, Next.js and the modern AI development stack.