LibreTranslate
Overview
LibreTranslate is a free, open-source machine translation API that you run entirely inside your own Azure subscription. It is powered by the offline Argos Translate neural models, so it translates text between languages with no third-party service in the loop and no data leaving your virtual machine. It exposes a REST API and a simple web interface.
This SecureAnyCloud image installs LibreTranslate (verified at 1.9.6) into a dedicated Python virtual environment at /opt/libretranslate/venv. It runs CPU-only as the non-root libretranslate service, bound to loopback 127.0.0.1:5000, behind nginx on port 80. nginx is ready for your TLS certificate.
Offline models are built in. The Argos language models for a subset of common languages are pre-installed, so translation works the moment the machine boots, with no download and no GPU. The bundled languages are Arabic, Chinese, English, French, German, Italian, Portuguese, Russian and Spanish. LibreTranslate pivots pairs that have no direct model through English. You can add more languages at any time (see Step 8).
Secure by default, with no open endpoint. An open translation endpoint is a compute abuse risk, so this image enforces API keys. Every translation request requires a key, including requests from the web interface, where you paste the key once.
A libretranslate-firstboot.service oneshot generates a unique API key on each VM's first boot. It writes the key to a root-only file, resolves the instance URL, writes a root-only info note, and then hands off to the service. No two VMs share a key, and none is baked into the image. Request rate and character limits guard against abuse, and LibreTranslate binds only to loopback, so nginx is the single public listener.
What you get:
- LibreTranslate (verified at 1.9.6) in a dedicated Python virtual environment, CPU-only
libretranslate.servicebound to127.0.0.1:5000, behind nginx on port 80, ready for TLS- Offline Argos models for 9 common languages, pre-installed for out-of-the-box translation
libretranslate-firstboot.servicefor the first-boot API key, URL resolution and info note- A unique per-VM API key generated on first boot and stored in a root-only
0600file - An Ubuntu 24.04 LTS base, patched at build time, with unattended security upgrades enabled
- 24/7 SecureAnyCloud support, 24h response SLA
Prerequisites
You need an active Azure subscription, an SSH key pair, and a VNet with a subnet.
LibreTranslate runs on CPU alone, and translation throughput scales with CPU and memory. Use Standard_B2s (2 vCPU, 4 GB RAM) for evaluation and light use. Use Standard_D2s_v5, Standard_F4s_v2 or larger for heavier translation volume.
The models live on the OS disk, so expand the disk before you load many additional languages.
Deploy from the Azure Portal
Search the Marketplace for LibreTranslate on Ubuntu 24.04 and choose your VM size.
Attach an NSG that allows TCP 22 (SSH) from your management network. Also allow TCP 80 / 443 (the translation service) from the networks and applications that need to reach it. In production, front port 80 with TLS (see Step 10).
Deploy from the Azure CLI
Create the network resources and the VM, and accept the Marketplace terms once per subscription.
RG="libretranslate-prod"; LOCATION="eastus"; VM_NAME="libretranslate-01" IMAGE_URN="sac1647283583153:libretranslate-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION" az network vnet create -g "$RG" --name lt-vnet --address-prefix 10.90.0.0/16 --subnet-name lt-subnet --subnet-prefix 10.90.1.0/24 az network nsg create -g "$RG" --name lt-nsg az network nsg rule create -g "$RG" --nsg-name lt-nsg --name allow-ssh --priority 100 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp az network nsg rule create -g "$RG" --nsg-name lt-nsg --name allow-web --priority 110 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 80 443 --access Allow --protocol Tcp
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:libretranslate-ubuntu-24-04:default:latest
az vm create -g "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
--size Standard_B2s --storage-sku StandardSSD_LRS \
--admin-username azureuser --ssh-key-values "$SSH_KEY" \
--vnet-name lt-vnet --subnet lt-subnet --nsg lt-nsg --public-ip-sku StandardConnect over SSH
Connect to the VM as azureuser.
ssh azureuser@<vm-ip>Verify the services
LibreTranslate and nginx start automatically after the first-boot service has generated the per-VM key. Confirm that the stack is healthy:
systemctl is-active libretranslate.service nginx.service ss -tlnp | grep -E ':80 |:5000 'Both services should report active. nginx listens on port 80 for external traffic. LibreTranslate listens only on 127.0.0.1:5000 and is never exposed directly.
Retrieve your per-VM API key
Every translation request needs the API key that was generated for this VM on its first boot. The key is stored in a root-only file:
sudo cat /root/libretranslate-info.txtThe LIBRETRANSLATE_API_KEY value is unique to your VM. No other VM has it, and it is not present in the Marketplace image. Keep it secret. LIBRETRANSLATE_URL is the address of your instance.
Translate from the web interface
Open http://<vm-ip>/ in a browser. The LibreTranslate interface loads immediately.
The service is secured, so select the key icon (Set API Key) in the top navigation bar. Paste the LIBRETRANSLATE_API_KEY from Step 5. The browser stores your key and sends it with every translation.
Pick your languages and type into the left pane. The translation appears on the right. For example, English to Spanish returns "Hola mundo".
The same interface handles every bundled language. A full sentence, for example, can be translated from English into French entirely offline on the VM.
Translate through the REST API
The same translation is available over HTTP. First confirm that the endpoint is closed without a key, which is the secure default. Then send an authenticated request that reads the per-VM key from the info file:
# Without a key: the request is rejected (no open translation endpoint)
curl -s -X POST http://127.0.0.1/translate \
--data 'q=Hello world&source=en&target=es&format=text' | jq .
# With your per-VM key: a real offline translation
API_KEY=$(sudo grep '^LIBRETRANSLATE_API_KEY=' /root/libretranslate-info.txt | cut -d= -f2-)
curl -s -X POST http://127.0.0.1/translate \
--data "q=Hello world&source=en&target=es&format=text&api_key=$API_KEY" | jq .The unauthenticated call returns {"error": "Please contact the server operator to get an API key"}. The authenticated call returns {"translatedText": "Hola mundo"}.
From your own application, send the key as the api_key field. Replace <your-api-key> with the value from Step 5:
curl -s -X POST http://<vm-ip>/translate \
--data 'q=Good morning&source=en&target=fr&format=text&api_key=<your-api-key>' | jq -r '.translatedText'List the bundled languages and add more
List the languages that are loaded and ready to translate offline. The keyRequired flag in the frontend settings confirms that the service is secured:
curl -s http://127.0.0.1/languages | jq -r '.[] | .code + " " + .name'
curl -s http://127.0.0.1/frontend/settings | jq '{apiKeys, keyRequired, charLimit}'To add another language, install its Argos model as the libretranslate service user and widen the service's --load-only list. This example adds Japanese (ja):
# Install the model (installs into the service's offline model store)
sudo -u libretranslate env HOME=/var/lib/libretranslate \
ARGOS_PACKAGES_DIR=/var/lib/libretranslate/argos-packages \
/opt/libretranslate/venv/bin/python -c \
"from argostranslate import package; package.update_package_index(); \
p=[x for x in package.get_available_packages() if x.from_code in ('en','ja') and x.to_code in ('en','ja')]; \
[package.install_from_path(x.download()) for x in p]"
# Add ja to the --load-only list in the service unit, then restart
sudo sed -i 's/--load-only ar,de,en,es,fr,it,pt,ru,zh/--load-only ar,de,en,es,fr,it,ja,pt,ru,zh/' \
/etc/systemd/system/libretranslate.service
sudo systemctl daemon-reload && sudo systemctl restart libretranslate.serviceRemoving a language from --load-only unloads it without deleting the model. This lowers memory use.
Explore the built-in API documentation
Open /docs for the interactive REST API documentation. It covers /translate, /detect, /languages and file translation, with request and response schemas.
Enable HTTPS
nginx fronts LibreTranslate on port 80 and is ready for your TLS certificate. For a public hostname, install a Let's Encrypt certificate with certbot:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.comcertbot obtains the certificate, rewrites the nginx server block for TLS, and sets up automatic renewal. Point port 443 at your management and application networks in the NSG, and restrict port 80 to the redirect.
Managing the service
systemctl status libretranslate.service
# service health
sudo journalctl -u libretranslate.service -f
# live logs
sudo systemctl restart libretranslate.serviceConfiguration lives in the systemd unit /etc/systemd/system/libretranslate.service (host, port, --load-only, --req-limit, --char-limit). The API keys database is /var/lib/libretranslate/api_keys.db.
Support
This image is backed by 24/7 SecureAnyCloud support with a 24 hour response SLA. LibreTranslate is licensed under the GNU AGPL-3.0. For product questions, consult the LibreTranslate documentation.
Deploy on Azure
Launch LibreTranslate on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
Overview
This image runs LibreTranslate 1.9.6, a free, open-source machine translation engine that you host yourself. It is installed from the project's official PyPI wheel into a dedicated Python virtualenv.
LibreTranslate exposes a small REST API and a simple web interface. It translates entirely on the instance using the offline Argos Translate neural models. Nothing you submit is sent to a third-party service, and once the models are on disk the instance can translate with no internet access at all.
The engine is served by nginx over HTTPS on port 443, with LibreTranslate itself bound to the loopback interface only. Port 80 answers a plain HTTP liveness check at /health and 301-redirects everything else to port 443. The API key travels in the request body, so it should not cross a network in clear text.
The API ships key-protected, not open. An open translation endpoint on the public internet is a compute abuse risk, because anyone who finds it can spend your CPU. Key enforcement is therefore switched on, and it applies to the web interface as well as to programmatic callers.
No usable key is baked into the image. On the first boot of every deployed instance, a one-shot service generates a fresh random API key and a fresh self-signed TLS certificate, unique to that instance. It writes the key to /root/libretranslate-info.txt with mode 0600.
Nine languages ship as offline models: Arabic, Chinese, English, French, German, Italian, Portuguese, Russian and Spanish. This is a deliberate subset, because the full upstream Argos catalogue is one hundred model packages and several further gigabytes. LibreTranslate pivots non-English pairs through English, so all 72 cross-language directional pairs across those nine languages resolve. See "Adding more languages" for how to add more.
The engine runs on CPU only. This image does not provide GPU acceleration, and LibreTranslate does not require it.
Storage is split across two dedicated EBS volumes, so neither the models nor the runtime sit on the operating system disk. The Argos model store is at /var/lib/libretranslate and the virtualenv is at /opt/libretranslate. Both are mounted by filesystem UUID, and each can be resized independently.
LibreTranslate is a machine translation engine. Its output suits understanding content, powering search, pre-translating a catalogue, or product features where an automatic translation is appropriate. It is not human translation, and it is not certified or sworn translation. Have a competent speaker review the output before you rely on it in a legal, medical or safety context.
What you get:
- LibreTranslate 1.9.6 in a dedicated Python virtualenv, CPU-only
- nginx over HTTPS on port 443, with LibreTranslate bound to loopback only
- Offline Argos models for 9 languages, with all 72 cross-language pairs resolved through English
- A unique per-instance API key and self-signed TLS certificate generated on first boot
- Two dedicated EBS volumes for the model store and the virtualenv, mounted by filesystem UUID
- 24/7 SecureAnyCloud support
Prerequisites
Before you deploy this image, you need:
- An Amazon Web Services account where you can launch EC2 instances
- IAM permissions to launch instances, create security groups and subscribe to AWS Marketplace products
- An EC2 key pair in the target Region for SSH access to the instance
- A security group that allows inbound TCP 22 from your own address, and TCP 443 from wherever your callers are
The recommended instance type is m5.large. Translation is CPU-bound, so read "Sizing for throughput" before you commit to a size in production.
Connecting to your instance
Connect over SSH on port 22 using the key pair you selected at launch. The login user depends on the operating system variant you launched.
For Ubuntu 24.04 LTS, the SSH login user is ubuntu. Example: ssh -i your-key.pem ubuntu@<public-ip>
Replace <public-ip> with your instance's public IPv4 address and your-key.pem with the private key file for your EC2 key pair.
Confirm the service is running
Two systemd units make up the running stack: libretranslate (the engine) and nginx (the TLS reverse proxy).
systemctl is-active libretranslate nginxactive activeBoth are enabled, so they start automatically after a reboot:
systemctl is-enabled libretranslate nginxenabled enabledConfirm the version and licence of the installed software:
/opt/libretranslate/venv/bin/pip show libretranslate | grep -E '^(Name|Version|License):'Name: libretranslate Version: 1.9.6 License: GNU AFFERO GENERAL PUBLIC LICENSEThe plain HTTP liveness endpoint answers without touching the application, so it is a good target for a load balancer health check:
curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1/healthHTTP 200Check what is listening. LibreTranslate is on loopback only, and nginx owns the public ports:
sudo ss -tlnp | grep -E ':(80|443|5000)\b'LISTEN 0 1024 127.0.0.1:5000 0.0.0.0:* users:(("libretranslate",pid=5521,fd=7)) LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=5527,fd=5),("nginx",pid=5526,fd=5),("nginx",pid=5525,fd=5)) LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=5527,fd=7),("nginx",pid=5526,fd=7),("nginx",pid=5525,fd=7)) LISTEN 0 511 [::]:80 [::]:* users:(("nginx",pid=5527,fd=6),("nginx",pid=5526,fd=6),("nginx",pid=5525,fd=6)) LISTEN 0 511 [::]:443 [::]:* users:(("nginx",pid=5527,fd=8),("nginx",pid=5526,fd=8),("nginx",pid=5525,fd=8))Your per-instance API key
The key was generated on this instance's first boot and is stored in a root-only file. List the field names in that file without revealing any value:
sudo grep -oE '^[A-Z0-9_]+=' /root/libretranslate-info.txtLIBRETRANSLATE_API_KEY= <set-at-first-boot>= LIBRETRANSLATE_API_URL= NOTE= NOTE2=To read the key itself, run sudo cat /root/libretranslate-info.txt on the instance. You can then paste the key into the web interface or your own client. The file also records the instance URL and an example curl command.
Treat the key as a secret. Do not commit it, and do not echo it in shell scripts or CI logs. Every example below reads the key into a shell variable instead of printing it.
The endpoint is closed without a key
Verify this yourself. A translation request with no key is refused:
curl -ks -o /dev/null -w 'unauthenticated POST /translate -> HTTP %{http_code}\n' -X POST https://127.0.0.1/translate --data "q=Hello world&source=en&target=es&format=text"unauthenticated POST /translate -> HTTP 400LibreTranslate answers 400 when a key is required but absent, and 403 for a key it does not recognise. The web interface follows the same rule. If you open it without setting a key, it tells you to contact the server operator for one.
Your first translation
Read the key into a variable and post a translation. This round trip proves that the instance works:
KEY=$(sudo grep "^LIBRETRANSLATE_API_KEY=" /root/libretranslate-info.txt | cut -d= -f2-); curl -ks -X POST https://127.0.0.1/translate --data "q=Hello world&source=en&target=es&format=text&api_key=$KEY" | jq .{ "translatedText": "Hola mundo" }The -k flag is needed because the instance ships a self-signed certificate. Once you install a certificate for your own domain, you can drop the flag.
Language detection works the same way:
KEY=$(sudo grep "^LIBRETRANSLATE_API_KEY=" /root/libretranslate-info.txt | cut -d= -f2-); curl -ks -X POST https://127.0.0.1/detect --data "q=Bonjour tout le monde&api_key=$KEY" | jq .[ { "confidence": 100.0, "language": "fr" } ]From your own machine, replace 127.0.0.1 with your instance's address and pass the key you read above:
curl -k -X POST https://<public-ip>/translate --data "q=Hello world&source=en&target=es&format=text&api_key=<your-api-key>"The web interface
Browse to https://<public-ip>/ and accept the one-time certificate warning. Choose your source and target languages and type into the left-hand box. The translation appears on the right.
Key enforcement also covers the interface. The first time you use it, select Set API Key in the header and paste the key from /root/libretranslate-info.txt. Until you do, the page loads but translation is refused.
Non-Latin scripts work in the same way.
The instance also serves an interactive REST API reference at https://<public-ip>/docs. It lists every endpoint with its parameters and response shape.
Which languages ship
Ask the instance, because /languages is the authoritative list:
curl -ks https://127.0.0.1/languages | jq -r '.[] | "\(.code)\t\(.name)"'en English ar Arabic zh-Hans Chinese fr French de German it Italian pt Portuguese ru Russian es SpanishNine languages ship, and every one is reachable from every other:
curl -ks https://127.0.0.1/languages | jq '{languages: length, cross_language_pairs: (([.[].targets|length]|add) - length)}'{ "languages": 9, "cross_language_pairs": 72 }The code for Chinese is zh-Hans, the script-qualified form. LibreTranslate also accepts the plain zh on /translate, but /languages reports zh-Hans. If your client builds its language menu from that response, match on the prefix.
Those 72 pairs come from 18 Argos model packages: English to and from each of the other eight languages, plus two direct Spanish and Portuguese pairs. LibreTranslate pivots anything else through English. This is why French to German works without a French to German model:
sudo find /var/lib/libretranslate/argos-packages -maxdepth 2 -name metadata.json -type f | wc -l18A manifest of exactly which packages are installed is written onto the instance at /var/lib/libretranslate/argos-manifest.txt.
Adding more languages
The full upstream Argos catalogue is one hundred packages. To add one, install it with argospm as the service user. Then widen the set that the service loads, and restart. This example adds Japanese. The model volume is sized for the entire catalogue, so you will not run out of room.
sudo -u libretranslate env HOME=/var/lib/libretranslate ARGOS_PACKAGES_DIR=/var/lib/libretranslate/argos-packages /opt/libretranslate/venv/bin/argospm install translate-en_jaThe service starts with a --load-only list that names the languages it loads into memory. Edit that list in the unit file to include your new language code, then reload and restart:
sudo sed -i 's/--load-only ar,de,en,es,fr,it,pt,ru,zh/--load-only ar,de,en,es,fr,it,pt,ru,zh,ja/' /etc/systemd/system/libretranslate.service && sudo systemctl daemon-reload && sudo systemctl restart libretranslateEach loaded language costs memory, so add only what you need and size the instance accordingly. Confirm the result with /languages again.
Issuing additional API keys
The keys live in a small SQLite database at /var/lib/libretranslate/api_keys.db, with a per-key request limit. A freshly launched instance holds exactly one key, the one that first boot generated:
sudo sqlite3 /var/lib/libretranslate/api_keys.db "SELECT count(*) AS keys, min(req_limit) AS req_limit FROM api_keys;"1|80Issuing a second key is a single insert. Use a separate key for each application, so that you can revoke one without disturbing the other. This command writes the new key straight into the root-only file instead of printing it:
NEWKEY=$(python3 -c 'import uuid;print(uuid.uuid4())');
sudo sqlite3 /var/lib/libretranslate/api_keys.db "INSERT INTO api_keys (api_key, req_limit) VALUES ('$NEWKEY', 80);" && sudo sh -c "umask 077; printf 'SECOND_API_KEY=%s\n' '$NEWKEY' >> /root/libretranslate-info.txt" && echo "second key issued and appended to /root/libretranslate-info.txt"second key issued and appended to /root/libretranslate-info.txtRead the key back from /root/libretranslate-info.txt when you need it. To revoke a key, delete the row for the key you are retiring. Replace <your-api-key> with the key itself:
sudo sqlite3 /var/lib/libretranslate/api_keys.db "DELETE FROM api_keys WHERE api_key = '<your-api-key>';"Changes take effect on the next request; no restart is needed.
Replacing the self-signed certificate
The certificate generated on first boot is self-signed, which is why browsers warn and curl needs -k. Replace it with a certificate for a domain you control. Point a DNS A record at the instance and open port 80 to the internet so that the HTTP-01 challenge can complete. Then run:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d translate.your-domain.exampleCertbot rewrites the nginx server block and installs a renewal timer. To use a certificate you already hold, replace these two files and reload nginx. These are the paths that the shipped configuration references:
sudo install -m 0644 -o root -g root your-fullchain.pem /etc/nginx/ssl/libretranslate.crt
sudo install -m 0600 -o root -g root your-private-key.pem /etc/nginx/ssl/libretranslate.key
sudo nginx -t && sudo systemctl reload nginxStorage layout
Neither the models nor the runtime live on the operating system disk:
df -h --output=target,source,size,used,avail / /opt/libretranslate /var/lib/libretranslateMounted on Filesystem Size Used Avail / /dev/root 38G 3.7G 35G /opt/libretranslate /dev/nvme1n1 9.8G 474M 8.8G /var/lib/libretranslate /dev/nvme2n1 40G 3.9G 34GBoth data volumes are referenced in /etc/fstab by filesystem UUID, so the layout is the same on every instance launched from this image and survives device renaming. To grow either one, modify the EBS volume in the console. Then run sudo resize2fs against the device shown above.
The only state worth backing up is /var/lib/libretranslate, which holds the models and your API keys. An EBS snapshot of that volume is sufficient.
Rate limits and tuning
The service starts with a request limit of 80 per minute per key and a 5000-character cap per request, and it runs two translation threads. All three are arguments in the unit file:
grep -E 'req-limit|char-limit|threads' /etc/systemd/system/libretranslate.service--req-limit 80 --char-limit 5000 --threads 2If you are throughput-bound and have memory to spare, raise --threads towards the instance's vCPU count. Then run sudo systemctl daemon-reload && sudo systemctl restart libretranslate. Raise --char-limit if you submit long documents. A single large request occupies a thread for its whole duration.
Sizing for throughput
Translation here is CPU-bound, with no GPU involved. Two properties follow, and both matter more than the choice of instance family:
- Throughput scales with vCPU count and with
--threads. To get more translations per second, add vCPUs and raise the thread count together. Raising only one does not help. - Memory scales with the number of languages loaded, not with request volume. Each language in
--load-onlyis held in memory. Loading the full catalogue on a small instance is the most common way to run out of RAM.
For bulk work, send many short requests concurrently to a larger instance. This is more efficient than sending one very long request, because a single request cannot use more than one thread.
For horizontal scale, run several instances behind a load balancer that checks the /health endpoint on port 80. The service holds no shared state, so instances are interchangeable. Each instance has its own key database, so issue the same key on each.
Licensing
LibreTranslate is licensed under the GNU AGPL-3.0. The complete corresponding source of the installed version is shipped on the instance to satisfy AGPL Section 13:
ls /opt/libretranslate/src/libretranslate-1.9.6/LICENSE/opt/libretranslate/src/libretranslate-1.9.6/LICENSEThe Argos Translate library is separately dual-licensed under MIT or CC0. The Argos model packages are distributed by the Argos Open Tech project through its package index. That index publishes no per-package licence field, so this page makes no licence claim about the model weights themselves. The manifest on the instance identifies which packages are installed and where they came from.
SecureAnyCloud is not affiliated with, endorsed by or sponsored by the LibreTranslate or Argos Open Tech projects.
Support
SecureAnyCloud provides 24/7 technical support for this image by email and live chat at sales@secureanycloud.com. We aim to respond within one business day.
We can help with deployment and initial configuration, retrieving and rotating the per-instance API key, and issuing additional keys with their own rate limits. We can also help with adding and loading further language models, replacing the self-signed certificate and serving the API on your own domain, rate limit and thread tuning, growing the storage volumes, and sizing for throughput.
Deploy on AWS
Launch LibreTranslate on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com