LibreTranslate

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.