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.servicebound to127.0.0.1:8081and run as a dedicated non-rootlanguagetooluser under a hardened systemd unit- An nginx reverse proxy on port 80 with per-VM HTTP Basic Auth, ready for TLS
- An unauthenticated
/healthzendpoint 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
0600file - 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.
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).
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 StandardConnect via SSH
ssh azureuser@<vm-ip>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/healthzYou should see both services active, the two listening sockets, and ok from the health endpoint.
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.
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.toolThe 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.
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.
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 usedatafor a JSON annotated document)language: a language code (for exampleen-US,de-DEorfr) orautoto detect itmotherTongue: your first language, used to catch false friends and interference errorsenabledRules/disabledRules: comma-separated rule IDs to turn specific checks on or offenabledCategories/disabledCategories: the same, at the category levellevel=picky: enables additional, stricter style checks
These introspection endpoints sit behind the Basic Auth gate:
GET /v2/languages: every supported language and its codePOST /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.
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.comAfter 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
Overview
LanguageTool is an open-source proofreading engine that checks grammar, style and spelling in dozens of 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 inside your own account, 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 cloud service.
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. Only the open-source LanguageTool core is shipped. The premium and paid features, the hosted LanguageTool cloud API, and the optional multi-gigabyte n-gram datasets are not included.
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 instance's first boot, a languagetool-firstboot.service oneshot generates a unique per-instance 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 instances 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.servicebound to127.0.0.1:8081and run as a dedicated non-rootlanguagetooluser under a hardened systemd unit- An nginx reverse proxy on port 80 with per-instance HTTP Basic Auth, ready for TLS
- An unauthenticated
/healthzendpoint for load balancer and probe checks languagetool-firstboot.service, which sets the per-instance password on first boot and proves the auth gate- A unique per-instance password generated on first boot and stored in a root-only
0600file - All base language models bundled in the distribution, so checking works immediately
- Ubuntu 24.04 LTS base, fully patched
- 24/7 SecureAnyCloud support
Connecting to your instance
Connect over SSH on port 22 as the default login user for your operating system variant. Replace <public-ip> with your instance's public IP or DNS name, and use the private key for the key pair you launched with.
- Ubuntu 24.04 LTS: login user ubuntu
ssh -i /path/to/key.pem ubuntu@<public-ip>Prerequisites
You need an AWS account, an EC2 key pair, and a VPC with a subnet.
LanguageTool is CPU- and memory-bound while checking, and the JVM heap is sized for the recommended instance type. Use m5.large (2 vCPU, 8 GB RAM) for evaluation and light workloads. Use a larger m5/c5 instance for production throughput and higher concurrency.
Launch from the AWS Console
In the EC2 console, choose Launch instance from AMI, select the SecureAnyCloud LanguageTool image, and pick an m5.large (or larger) instance type.
Attach a security group that allows TCP 22 (SSH) from your management network and TCP 80 (the API) from the networks and applications that need to submit text. Restrict port 80 to those clients. In production, front it with TLS (see the Enable HTTPS section below).
Launch from the AWS CLI
aws ec2 run-instances \
--image-id ami-xxxxxxxxxxxxxxxxx \
--instance-type m5.large \
--key-name my-key \
--security-group-ids sg-xxxxxxxx \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=languagetool-01}]'Verify the services are running
Once connected over SSH, confirm both services are active.
systemctl is-active languagetool nginxBoth services should report active. The LanguageTool server listens on loopback 127.0.0.1:8081, and nginx is the public listener on port 80.
Read your per-instance password
The first-boot service generated a unique HTTP Basic Auth password for this instance and wrote it to a root-only file. Read it with:
sudo cat /root/languagetool-credentials.txtThe file records the API URL, the user name (admin) and the generated password. The examples below read the password straight from that file, so it never appears on your screen.
Check some text
Send a sentence with a deliberate agreement error to /v2/check. The JSON response lists each issue with its message and suggested replacements.
PW=$(sudo grep '^LT_PASSWORD=' /root/languagetool-credentials.txt | cut -d= -f2-)
curl -sS -u "admin:$PW" \
--data-urlencode 'text=This are an test.' \
--data-urlencode 'language=en-US' \
http://localhost/v2/check \
| python3 -c 'import json,sys; d=json.load(sys.stdin); s=d["software"]; print(s["name"], s["version"]); print(len(d["matches"]), "issue(s):"); [print("-", m["message"], "->", ", ".join(r["value"] for r in m["replacements"][:3])) for m in d["matches"]]'LanguageTool 6.6 flags the singular/plural disagreement (this ... are) and the article before test, each with suggested corrections.
List the supported languages
PW=$(sudo grep '^LT_PASSWORD=' /root/languagetool-credentials.txt | cut -d= -f2-)
curl -sS -u "admin:$PW" http://localhost/v2/languages \
| python3 -c 'import json,sys; d=json.load(sys.stdin); print(len(d), "languages"); [print("-", x["name"], x["longCode"]) for x in d[:12]]'Automatic language detection
To let LanguageTool detect the language of the submitted text, omit the language parameter or pass language=auto.
PW=$(sudo grep '^LT_PASSWORD=' /root/languagetool-credentials.txt | cut -d= -f2-)
curl -sS -u "admin:$PW" \
--data-urlencode 'text=Ceci est un test' \
--data-urlencode 'language=auto' \
http://localhost/v2/check \
| python3 -c 'import json,sys; d=json.load(sys.stdin); print("detected:", d.get("language",{}).get("detectedLanguage",{}).get("name"))'The open health endpoint
/healthz is unauthenticated and returns ok. Use it for load balancer target checks and readiness probes.
curl -sS http://localhost/healthzCall the API from your workstation or application
From a client that can reach port 80, point at the instance's public IP or DNS name and authenticate with the admin user and the generated password. Restrict the security group so that only your clients can reach port 80.
curl -u admin:<password> \
--data-urlencode 'text=This are an test.' \
--data-urlencode 'language=en-US' \
http://<public-ip>/v2/checkYou can also point the official LanguageTool browser add-ons and editor plugins at http://<public-ip>/ (or your TLS endpoint) as a custom server.
Optional: add n-gram data for fewer false positives
The base language models are bundled, so checking works out of the box. LanguageTool can optionally load large n-gram datasets to reduce false positives on confused words. These datasets are several gigabytes per language and are not shipped in the image.
To add them, download the dataset for your language from the LanguageTool project and extract it to a dedicated data disk. Then set languageModel in /opt/languagetool/server.properties to its parent directory, and restart the service. See the LanguageTool documentation for details.
Enable HTTPS
In production, terminate TLS in front of nginx. Either place the instance behind an AWS Application Load Balancer with an ACM certificate and forward to port 80, or add a certificate directly to the nginx server block. A minimal in-place approach uses certbot:
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.comPoint your DNS at the instance first. Keep the HTTP Basic Auth gate in place, so the API stays authenticated over TLS.
Persistence, first boot and updates
LanguageTool is stateless. It keeps no data between requests, so there is no database and no data volume to manage.
languagetool-firstboot.service generates the per-instance password once, on first boot, and then disables itself. Rebooting the instance does not regenerate the password. To rotate it, generate a new bcrypt entry with htpasswd -B /etc/nginx/.languagetool.htpasswd admin and restart nginx.
Keep the base operating system patched with sudo apt-get update && sudo apt-get upgrade.
Support
Every SecureAnyCloud deployment is backed by 24/7 support from SecureAnyCloud engineers, by email at sales@secureanycloud.com and by live chat, with a one-hour average response for critical issues. Support covers deployment and sizing, JVM heap and per request tuning, the nginx front door, TLS termination and source restriction, rotating the Basic Auth credential, adding the optional n-gram datasets, and LanguageTool upgrades and troubleshooting.
Deploy on AWS
Launch LanguageTool Grammar and Style Checker with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com