Self-hosting LibreTranslate
Deploy LibreTranslate on Fly.io and point the API at it.
The simplest path to a private translation service. Total time: ~10 minutes.
On Fly.io
fly launch --image libretranslate/libretranslate \
--name rustpulse-libretranslate \
--region iad \
--no-deploy
Edit the generated fly.toml:
[env]
LT_LOAD_ONLY = "ar,bg,cs,da,de,el,en,eo,es,et,fa,fi,fr,he,hi,hu,id,it,ja,ko,lt,lv,ms,nb,nl,pl,pt,ro,ru,sk,sl,sq,sv,th,tl,tr,uk,ur,vi,zh"
LT_DISABLE_FILES_TRANSLATION = "true"
LT_DISABLE_WEB_UI = "true"
[[services]]
internal_port = 5000
protocol = "tcp"
[[services.ports]]
port = 80
handlers = ["http"]
LT_LOAD_ONLY matches the language code list Rust Pulse validates against (see Supported languages). Loading only what you use cuts memory by ~60% vs the default all-languages set.
Deploy:
fly deploy
Wait for the machine to come up (~3 minutes the first time — models download on first boot). Then:
curl -s "https://rustpulse-libretranslate.fly.dev/languages" | jq length
# → 40 (or however many you LT_LOAD_ONLY'd)
With Docker Compose locally
services:
libretranslate:
image: libretranslate/libretranslate
ports: ['5000:5000']
environment:
LT_LOAD_ONLY: "en,es,fr,de,ru,zh"
LT_DISABLE_WEB_UI: "true"
Then in your rustplus-api .env:
LIBRETRANSLATE_URL=http://localhost:5000
Wire-up the API
In your rustplus-api .env (production):
LIBRETRANSLATE_URL=https://rustpulse-libretranslate.fly.dev
Or, for internal-only Flycast (recommended):
LIBRETRANSLATE_URL=http://rustpulse-libretranslate.flycast
Restart the API. The !translate command starts working immediately.
Costs
A shared-cpu-1x Fly machine with 1GB RAM costs roughly $1.94/month at the time of writing. The translation roundtrip averages 60-120ms per request. A heavy Rust Pulse user community fits comfortably in a single machine.
For HA add a second machine in the same region (fly scale count 2) — Fly load-balances within the app.