Deployment guide

LanguageTool

Overview

LanguageTool is an open-source proofreading engine that checks grammar, style and spelling in more than twenty languages. Send it a block of text and it returns a structured list of issues. Each issue includes the rule that fired, a human-readable message, the offending span and suggested replacements.

LanguageTool combines a large library of hand-written linguistic rules with statistical and machine-learning models. It runs entirely on your own machine, so the text you check never leaves your infrastructure. That makes it a natural building block for writing assistants, editors, content and documentation pipelines, and any application that wants proofreading without sending user text to a third party.

This SecureAnyCloud image installs the official LanguageTool 6.6 standalone distribution at /opt/languagetool. It runs as the languagetool service under OpenJDK 17, bound to loopback 127.0.0.1:8081, behind nginx on port 80.

Secure by default, no default login. The LanguageTool server ships with no authentication, and its /v2/check endpoint is CPU-intensive and open to abuse if the port is exposed. This image never exposes it. The server binds to loopback only, because the public mode that opens it to the network requires an explicit flag that is never passed. nginx is the only public listener and fronts the whole REST surface with HTTP Basic Auth.

On each VM's first boot, a languagetool-firstboot.service oneshot generates a unique per-VM password and writes a bcrypt .htpasswd and a root-only info note. It then proves the auth gate with a real grammar-check round trip and disables itself. No two VMs share a password, and none is baked into the image.

Note on the interface. LanguageTool Server is an API-first service with no bundled web interface. You drive it through its REST API, the official LanguageTool clients and browser add-ons (pointed at your server), or your own application. This guide uses curl against the API.

What you get:

  • LanguageTool 6.6 (languagetool-server) on OpenJDK 17
  • languagetool.service bound to 127.0.0.1:8081 and run as a dedicated non-root languagetool user under a hardened systemd unit
  • An nginx reverse proxy on port 80 with per-VM HTTP Basic Auth, ready for TLS
  • An unauthenticated /healthz endpoint for load balancer and probe checks
  • languagetool-firstboot.service, which sets the per-VM password on first boot and proves the auth gate
  • A unique per-VM password generated on first boot and stored in a root-only 0600 file
  • All base language models bundled in the distribution, so checking works immediately
  • Ubuntu 24.04 LTS base, patched at build time
  • 24/7 SecureAnyCloud support with a 24-hour response SLA

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a subnet.

LanguageTool is CPU- and memory-bound while checking, and the JVM heap is sized for the recommended VM. Use Standard_B2s (2 vCPU, 4 GB RAM) for evaluation and light workloads. Use a Standard_D2s_v5 or larger for production throughput and higher concurrency.

1

Deploy from the Azure Portal

Search the Azure Marketplace for LanguageTool on Ubuntu 24.04 and choose your VM size.

Attach a network security group (NSG) that allows TCP 22 (SSH) from your management network. Also allow TCP 80 / 443 (the API) from the networks and applications that need to submit text. In production, front port 80 with TLS (see Step 9).

2

Deploy from the Azure CLI

RG="languagetool-prod"; LOCATION="eastus"; VM_NAME="languagetool-01" IMAGE_URN="sac1647283583153:languagetool-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:languagetool-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 Standard
3

Connect via SSH

ssh azureuser@<vm-ip>
4

Verify the services are running

The LanguageTool server listens on loopback 127.0.0.1:8081, and nginx fronts the API on port 80. The /healthz endpoint is public. Every other endpoint requires your per-VM password.

sudo systemctl is-active languagetool nginx
sudo ss -tln | grep -E ':80 |:8081' curl -s http://127.0.0.1/healthz

You should see both services active, the two listening sockets, and ok from the health endpoint.

5

Read your per-VM password

The password was generated on this VM's first boot and stored in a root-only file. Read it and keep it secret, because it authenticates every API request. Then confirm that unauthenticated requests are rejected.

sudo ls -l /root/languagetool-credentials.txt echo "unauthenticated /v2/languages -> HTTP $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/v2/languages)"

The info note holds LT_USERNAME (admin) and LT_PASSWORD. It is owned by root:root with mode 0600.

An unauthenticated call to /v2/languages returns 401, because the nginx Basic Auth gate enforces authentication.

6

Check some text

Checking text is the core job of LanguageTool. Load the per-VM password into a shell variable, then POST text to the /v2/check endpoint. Pass the text and the language code as form fields. The response is a JSON matches array with one entry per issue.

export
LT_PW=$(sudo grep '^LT_PASSWORD=' /root/languagetool-credentials.txt | cut -d= -f2-)
# A sentence with a deliberate agreement error curl -s -u "admin:$LT_PW" \
  --data-urlencode 'text=This are a test of the LanguageTool server.' \
  --data-urlencode 'language=en-US' \
  http://127.0.0.1/v2/check | python3 -m json.tool

The response lists each issue in matches. The agreement error on "This are" appears with a message, the character offset and length, and a set of suggested replacements.

To check your own text, send it to the same endpoint.

7

Automatic language detection and other languages

Omit the language field, or set language=auto, to let LanguageTool detect the language automatically. To pick a language yourself, use any supported language code such as de-DE, fr or es.

export
LT_PW=$(sudo grep '^LT_PASSWORD=' /root/languagetool-credentials.txt | cut -d= -f2-)
# Auto detect the language curl -s -u "admin:$LT_PW" \
  --data-urlencode 'text=Ceci est une phrase avec une faute daccord.' \
  --data-urlencode 'language=auto' \
  http://127.0.0.1/v2/check | python3 -c 'import sys,json; d=json.load(sys.stdin); print("detected:", d["language"]["detectedLanguage"]["name"]); print("issues:", len(d["matches"]))'
# List every supported language and its code curl -s -u "admin:$LT_PW" http://127.0.0.1/v2/languages | python3 -c 'import sys,json; d=json.load(sys.stdin); print(len(d), "languages"); [print(" ", x["name"], x["longCode"]) for x in d[:8]]'

When you use auto, /v2/check reports the detected language. /v2/languages returns the full catalogue of supported languages and their codes.

8

The check API and its options

You drive everything through POST /v2/check. The most useful fields are:

  • text: the plain text to check (or use data for a JSON annotated document)
  • language: a language code (for example en-US, de-DE or fr) or auto to detect it
  • motherTongue: your first language, used to catch false friends and interference errors
  • enabledRules / disabledRules: comma-separated rule IDs to turn specific checks on or off
  • enabledCategories / disabledCategories: the same, at the category level
  • level=picky: enables additional, stricter style checks

These introspection endpoints sit behind the Basic Auth gate:

  • GET /v2/languages: every supported language and its code
  • POST /v2/check: the checking endpoint itself

For example, run a stricter "picky" check:

export
LT_PW=$(sudo grep '^LT_PASSWORD=' /root/languagetool-credentials.txt | cut -d= -f2-)
curl -s -u "admin:$LT_PW" \
  --data-urlencode 'text=The report was authored by the team in a very unique way.' \
  --data-urlencode 'language=en-US' --data-urlencode 'level=picky' \
  http://127.0.0.1/v2/check | python3 -c 'import sys,json; d=json.load(sys.stdin); print("issues:", len(d["matches"])); [print(" -", m["rule"]["id"], ":", m["message"]) for m in d["matches"]]'

Optional: add n-gram data for fewer false positives

LanguageTool can use large n-gram data sets to catch confused words (for example "their" and "there") with fewer false positives. These data sets are several gigabytes per language, so they are not bundled, to keep the image small.

To add them, download the language pack from LanguageTool and extract it to a directory. Then point the server at it by adding languageModel=/opt/languagetool/ngrams to /opt/languagetool/server.properties, and run sudo systemctl restart languagetool. See the LanguageTool documentation for the current download location and disk requirements.

9

Enable HTTPS

nginx fronts the API on port 80 and is ready for TLS. Point a DNS record at the VM. Then obtain a certificate with certbot and let it configure nginx for port 443.

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com

After issuance, restrict the NSG so that only 443 (and 22 from your management network) is reachable. Then submit text over https://your-domain.example.com/v2/check.

Persistence, first boot and updates

LanguageTool Server is stateless. It keeps no data between requests, so there is no data volume to manage. The distribution and all bundled language models live on the OS disk and are captured into the image, so a VM launched from this image is ready immediately.

The first-boot service generates the per-VM Basic Auth password, proves the auth gate, then disables itself, so later reboots are unaffected. The OS is patched at build time and ships with unattended security upgrades enabled.

To change the password later, run sudo htpasswd -B /etc/nginx/.languagetool.htpasswd admin. Then reload nginx with sudo systemctl reload nginx.

Support

Every SecureAnyCloud deployment includes 24/7 support with a 24-hour response SLA. Contact sales@secureanycloud.com for help with deployment, configuration or scaling.

This is a repackaged open-source software product, with additional charges for SecureAnyCloud support services. LanguageTool is licensed under the GNU Lesser General Public License (LGPL). All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.

Deploy on Azure

Launch LanguageTool 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

Deploy on Azure