LibreTranslate: Private, Offline Translation at Home
A self-hosted translation API that keeps your text off Google's servers and runs without a GPU

Contents
Every time I paste a paragraph into a free online translator, I’m handing a stranger a sentence I couldn’t read and telling them exactly what I was curious about. Usually it’s harmless — a recipe in Italian, a forum post in German. Occasionally it’s a letter from a foreign tax office, a contract, or a message from a relative that I’d rather not stream through an advertising company’s servers to decode. LibreTranslate fixes the privacy hole by doing the translation on my own hardware, and the pleasant surprise is that it needs no GPU to do it.
LibreTranslate is a self-hosted machine-translation service: a web page you can paste into, and a REST API that other software can call, both backed by open translation models that run entirely on your box. It’s built on Argos Translate, which uses OpenNMT models run through CTranslate2 — a translation engine tuned to be fast on ordinary CPUs. That last detail is what makes it a genuinely easy homelab addition: unlike the language and image models elsewhere in a local-AI setup, this one is happy on a cheap mini-PC with no graphics card at all.
Why self-host translation
The privacy case is the obvious one and it’s strong. The text you most want translated is frequently text you’d least like to leak — legal documents, medical letters, private correspondence in a language you don’t speak. Pasting it into a hosted translator sends the whole thing to a third party, and the free services are free because your text is the product. Running LibreTranslate locally means the sentence is translated by software that has no route off your network, which I can prove by pulling the cable and watching it keep translating.
The second reason is integration without a bill. LibreTranslate speaks a simple HTTP API that a growing list of self-hosted apps already knows how to call — feed readers that translate foreign headlines, fediverse servers that offer a “translate post” button, document pipelines that need to normalise everything to one language. Point those at your own LibreTranslate instance and the feature works with no API key from a commercial provider, no per-character charge, and no rate limit beyond what your hardware can chew through. For anything that translates in bulk, a hosted API’s metering makes the feature too expensive to leave on; a local instance makes it free to use everywhere.
The third reason is that it works with the internet unplugged, which matters more than it sounds. Offline translation is genuinely useful on a laptop with a local copy of the models when you’re travelling somewhere with no signal, and it means your translation feature never breaks because a provider changed their pricing, deprecated an endpoint, or throttled you mid-job.
Standing it up
LibreTranslate ships as a single container. The only real decision at setup is which languages to load, and it’s an important one: by default the container downloads translation models for every supported language on first run, which is several gigabytes and takes a while. The LT_LOAD_ONLY setting restricts it to the languages you actually use, cutting the download and the memory footprint dramatically.
| |
The volume is the setting people forget, and forgetting it hurts. The downloaded models live under the Argos Translate data directory, and if you don’t persist that directory, every container restart re-downloads several gigabytes of models before the service will answer. Mount it as a named volume once and restarts become instant.
Bring it up and give it a minute on first run to fetch the models you asked for:
| |
Open http://mylab.local:5000 and you get a translation web page immediately: source box, target box, language dropdowns, and automatic language detection. That’s enough for the paste-a-paragraph use. The interesting part is the API underneath it.
Using the API
The whole service is one main endpoint. A translation is a POST with the text, a source language (or auto to detect it), and a target:
| |
Setting source to auto runs language detection first and returns what it guessed, which is what you want when you’re translating unknown incoming text — a foreign email, a scanned letter, an RSS headline. There’s a companion /detect endpoint if you only need the language, a /languages endpoint listing what’s loaded, and a format: "html" option that translates HTML while preserving the tags, which is what the feed-reader and fediverse integrations lean on.
Because it’s plain HTTP with a tiny surface, wiring LibreTranslate into your own scripts is trivial. I have a small job that watches a folder, detects the language of anything dropped in, and translates non-English documents to English before they go into my archive — a dozen lines of Python around this endpoint, running against a service that costs nothing per call.
Keeping it locked down
A translation endpoint looks harmless, and mostly it is, but an open one on a reachable port lets anyone who finds it burn your CPU translating their text for free. Setting LT_API_KEYS=true, as the compose file does, makes the API require a key that you generate and hand only to the services that should call it. Keep the instance on your LAN or behind a VPN rather than publishing it to the internet; there’s no reason the wider world needs to reach your translator, and a public one is an open invitation to have your box conscripted as somebody’s free translation service. If you do open the web UI to a household, the LT_CHAR_LIMIT setting caps how much text a single request can submit, which stops one enormous paste from tying up every CPU thread you own. The threat here is mundane abuse of your compute, and a key plus a private network handles it comfortably.
Choosing which models to load
The quality and the resource cost both come down to which language packages you install, so it’s worth being deliberate. Each LT_LOAD_ONLY entry is a language code, and LibreTranslate loads the packages that translate between the languages you list. List five languages and you get the pairs among them; list thirty and both the download and the RAM usage balloon, because every loaded model sits in memory ready to serve.
A subtlety worth knowing: many pairs translate through English as a pivot. There may be no direct Portuguese-to-Polish model, so the engine goes Portuguese to English to Polish under the hood. This mostly works and occasionally shows — a phrase that survives one hop can get mangled on two. If a specific non-English pair matters to you, test it directly rather than assuming a direct model exists.
Keep the list to the languages you genuinely encounter. My instance loads five European languages plus English and uses well under a gigabyte of RAM, which is why it lives on a small always-on box rather than the GPU machine.
Where it plugs into the rest of the homelab
LibreTranslate earns its keep as a component other services call. If you run a document archive, translation slots in naturally: Paperless with a local LLM for auto-tagging scans sorts your documents, and a translation pass in front of it means a foreign-language letter arrives already rendered into English before it’s OCR’d, tagged, and filed. If you’ve built self-hosted search, translation opens up cross-lingual retrieval — running embeddings locally for self-hosted search finds documents by meaning within one language, and translating a query or your foreign documents into that language lets a single embedding model reach across the whole multilingual corpus. In both cases the translation stays local, so adding the feature adds no new place for your data to leak.
Troubleshooting: the rough edges
First run takes forever or seems hung. It’s downloading models. Watch the logs — until the packages for your LT_LOAD_ONLY languages have finished fetching, the service won’t answer, and on a slow connection that’s minutes. Once you’ve mounted the models volume this only happens once.
Every restart re-downloads gigabytes. You didn’t persist the Argos Translate data directory, so the models vanish with the container. Mount the volume shown above and the problem disappears. This is the single most common LibreTranslate complaint and it’s a one-line fix.
The first translation after startup is slow, then it’s quick. Models are loaded into memory lazily on first use, so the initial request for a given pair pays the load cost while subsequent ones are fast. For a service that must feel instant from the first hit, warm it with a throwaway translation of each pair at startup.
A language pair you expected is missing. Either you didn’t include both languages in LT_LOAD_ONLY, or there’s no model for that direct pair and it’s pivoting through English. Check the /languages endpoint to see what’s actually loaded, and add the missing code to the load list.
Translations are noticeably rougher than a big service. This is real and worth being honest about — see the verdict. Quality varies a lot by pair; well-resourced pairs like English-Spanish are solid, while rarer pairs are weaker, and anything pivoting through English loses a little on each hop. For gist and privacy it’s excellent; for publication-grade output in a hard pair, temper expectations.
Memory creeps up as you add languages. Every loaded model holds memory. If the box is tight, trim LT_LOAD_ONLY to the pairs you truly use, or move the instance to a machine with more RAM. This is the direct cost of loading many languages, and pruning the list is the cure.
Is it worth it, and who for
Self-host LibreTranslate if you translate anything sensitive, if you want translation features in your self-hosted apps without a commercial API key and its metering, or if you value a translator that keeps working offline. It runs on a CPU, it costs nothing per translation, and it slots into a document pipeline or a search stack as one more local component with no new data-leak surface.
Be clear-eyed about quality. The big commercial engines are trained on far more data and produce smoother output, especially on difficult language pairs and idiomatic text, so for a legal translation that has to read perfectly you’ll still want a professional or a top-tier service. LibreTranslate is superb for understanding foreign text, for bulk and automated work where privacy and cost rule out a hosted API, and for the reassurance that a stranger isn’t reading everything you couldn’t.
For me it settled into the “always on, never think about it” tier of the homelab within a week. It sorts out foreign documents before they hit my archive, powers a translate button in my feed reader, and asks nothing of the GPU — a small container quietly earning its keep with the network cable exactly as connected as I choose it to be.



