Skip to content

hacklaen/opus-mt

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OPUS-MT Inference Server

Docker image: hacklaen/opus-mt

Self-hosted OPUS-MT (Helsinki-NLP) translation server with a LibreTranslate-compatible HTTP API, packaged as a small, CPU-only Docker image.

  • Drop-in backend for any client that speaks LibreTranslate's /translate and /languages endpoints.
  • Models are downloaded on demand from Hugging Face (Helsinki-NLP/opus-mt-{src}-{tgt}), converted to CTranslate2 INT8 on first use, and cached on a persistent volume.
  • Runs as a non-root user (UID 1100), exposes a single port (8000), and ships with a built-in HTTP healthcheck.
  • No GPU, optional API-key protection — designed to live on an internal Docker network behind your own application.

Standalone OPUS-MT service for private, self-hosted translation workloads.

Why this image

  • You want a small CPU-only translation service with no external SaaS dependency.
  • You already have a client that can talk to LibreTranslate-style endpoints.
  • You prefer on-demand model downloads over baking many language pairs into a larger image.

Supported tags

Tag Description
latest Latest stable release (currently identical to 1.1.0).
1.1.0 Adds optional API-key protection via OPUS_MT_API_KEYS.
1.0.0 First public release. Pin this if you need reproducibility.

Images are published as a multi-arch manifest for linux/amd64 and linux/arm64.


Quick start

docker run -d \
    --name opus-mt \
    -p 127.0.0.1:8000:8000 \
    -v opus_mt_models:/models \
    -e DEFAULT_PAIRS=de-en,en-de \
    hacklaen/opus-mt:latest

Translate something:

curl -s http://127.0.0.1:8000/translate \
    -H 'Content-Type: application/json' \
    -d '{"q":["Guten Morgen, Welt!"],"source":"de","target":"en","format":"text"}'
# {"translatedText":["Good morning, world!"]}

The first request for a new language pair triggers an automatic conversion (Helsinki-NLP/opus-mt-de-en → CTranslate2 INT8) and takes a few seconds. Subsequent requests are served from a hot in-memory cache.


docker compose

services:
  opus-mt:
    image: hacklaen/opus-mt:latest
    restart: unless-stopped
    environment:
      DEFAULT_PAIRS: "de-en,en-de"
      MAX_LOADED_PAIRS: "6"
    volumes:
      - opus_mt_models:/models
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8000/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 20s

volumes:
  opus_mt_models:

In production, keep the service on an internal Docker network and reach it from sibling containers via the service DNS name (http://opus-mt:8000). Do not publish port 8000 to the public Internet — see Security below.


HTTP API

The image speaks a subset of the LibreTranslate API. All endpoints return JSON.

POST /translate

Translate one or more strings.

Request

{
  "q": ["Guten Morgen", "Wie geht es dir?"],
  "source": "de",
  "target": "en",
  "format": "text"
}
Field Type Notes
q string or [string] Input(s). Strings >256 tokens truncate.
source string (ISO 639-1) Source language code.
target string (ISO 639-1) Target language code.
format "text" Only plain text is supported.

Response

{ "translatedText": ["Good morning", "How are you?"] }

GET /languages

Lists all language codes the running container can serve. Pairs defined by Helsinki-NLP that don't exist will return HTTP 400 on /translate.

GET /models

Returns the currently loaded (hot) pairs and the cache budget.

GET /health

Cheap liveness probe (HTTP 200 + {"status":"ok"}) used by the built-in Docker healthcheck.


Environment variables

Variable Default Purpose
DEFAULT_PAIRS (unset) Comma-separated src-tgt pairs to eager-load on startup.
OPUS_MT_API_KEYS false Set to true to require a valid API key on /translate, /languages and /models.
OPUS_MT_API_KEYS_LIST (unset) Comma-separated list of accepted API keys when OPUS_MT_API_KEYS=true.
MAX_LOADED_PAIRS 6 LRU cache size — how many converted models stay hot in RAM.
HF_HOME /models/.hf-cache Hugging Face cache root (Helsinki-NLP downloads land here).
TRANSFORMERS_CACHE /models/.hf-cache Mirror of HF_HOME for older transformers releases.

Any pair you don't preload still works — it just pays a one-time download+conversion cost on first request.

To enable API-key protection, for example:

docker run -d \
  --name opus-mt \
  -p 127.0.0.1:8000:8000 \
  -v opus_mt_models:/models \
  -e OPUS_MT_API_KEYS=true \
  -e OPUS_MT_API_KEYS_LIST=my-secret-key \
  hacklaen/opus-mt:latest

Clients can then pass the key as api_key in the JSON body for POST /translate, as a query parameter on GET /languages and GET /models, or via the X-API-Key header.


Persistent model volume

The container expects /models to be writable by UID 1100 (the non-root opusmt user). With a named volume this is automatic. With a bind-mount, prepare the host directory once:

sudo mkdir -p /srv/opus-mt-models
sudo chown -R 1100:1100 /srv/opus-mt-models
docker run ... -v /srv/opus-mt-models:/models hacklaen/opus-mt:latest

A fully populated volume with the 13 default European languages (see below) ends up around 3–5 GB.


Supported language pairs

The container is bidirectionally configured for the following 13 ISO 639-1 codes:

de · en · fr · es · it · nl · pt · ru · et · pl · sv · da · fi

Any combination {src}-{tgt} for which Helsinki-NLP publishes a model on Hugging Face works on-demand — so e.g. de-fr, en-ru, pl-de etc. are all available without any image rebuild.

For the full catalogue of OPUS-MT pairs, see https://huggingface.co/Helsinki-NLP?search_models=opus-mt.


Resource sizing

Item Typical value
Compressed image size ~600 MB
On-disk image size ~1.2 GB
RAM per loaded pair (INT8) ~150–300 MB
Cold start (first pair) 5–15 s (download)
Warm translation latency 50–200 ms / sentence

CPU is the only requirement. On 2 modern cores the server comfortably serves dozens of concurrent translation requests.


Security

By default the image does not authenticate requests. There is no built-in rate limit and no TLS termination. By design it is meant to run as an internal service:

  • Bind it to a private Docker network (no ports: mapping).
  • Or, if you must publish it, bind to 127.0.0.1 only and put it behind a reverse proxy (nginx, Caddy, Traefik) that adds auth and TLS.

If you need a lightweight in-container guard without a reverse proxy, set OPUS_MT_API_KEYS=true and configure one or more keys via OPUS_MT_API_KEYS_LIST.

If you need a publicly reachable instance, deploy LibreTranslate itself instead — it ships proper API-key handling.


Typical use cases

  • Private application backends on a Docker network
  • Internal tools that need machine translation without a cloud API
  • Reverse-proxied localhost services for desktop or homelab setups
  • Batch jobs that translate plain text through a simple HTTP endpoint

License & attribution

This image is distributed under the GNU GPL v2 or later. See the bundled LICENSE file for the full text and a summary of the third-party components included in the runtime layer.

The model weights downloaded at runtime are published by the Helsinki-NLP OPUS-MT project under CC-BY 4.0. When you build a user-facing product on top of them, please carry the attribution forward:

Translations powered by Helsinki-NLP OPUS-MT models (CC-BY 4.0).


Implementation notes

  • Dockerfile, requirements.txt and app/ are the full runtime source bundle for published releases.
  • The HTTP API intentionally stays close to LibreTranslate so existing clients can switch with minimal transport changes.
  • The image is intended for private networks or reverse-proxied localhost bindings, not direct public exposure.

About

Lightweight FastAPI wrapper around OPUS-MT CTranslate2 models with optional API-key authentication

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors