What the self-hosted server is
LanguageTool ships an embedded HTTP server so that a local instance can check text sent to it, and it speaks the JSON API documented for the public service (/v2/check). Per the change log, 6.6 (27 March 2025) was the last versioned ZIP; releases now come as daily snapshots, each holding languagetool-server.jar.
- You get a basic server without the AI-based rules, which the docs reserve for the cloud.
- The desktop app for Windows cannot use your own server, and synonyms in the browser add-on are unsupported against it.
- There is no official Docker image. The GitHub README points to three community-contributed projects (meyayl/docker-languagetool, Erikvl87/docker-languagetool and silvio/docker-languagetool); they are unofficial.
If you run one of those community images anyway, publish its port on 127.0.0.1 only. A mapping without an address listens on every host address, 0.0.0.0 and [::] alike; that traffic bypasses ufw's rules on IPv4, and also on IPv6 if Docker's own IPv6 networking is on. On Docker Engine 28 or later, a 127.0.0.1 mapping is reachable from the machine itself only.
Sizing: disk first, then memory
The base server is small. The optional n-gram data, which catches confused words such as their/there, is large and wants an SSD. The project publishes no RAM or CPU figure.
| Item | Figure | Source |
|---|---|---|
| Java | 17 up to 21 | Change log on GitHub, 6.9 (unreleased); the 6.6 log on languagetool.org says Java 17 or later |
| Snapshot ZIP | about 263 MB; about 420 MB unpacked | Snapshot folder on languagetool.org; the unpacked total is our reading of the archive's file list |
| English n-gram ZIP | about 9 GB (8,961,853,713 bytes); the docs round it to ~8GB | ngram-data folder on languagetool.org |
| English n-grams unpacked | about 15.4 GB | Our reading of the ZIP's file list |
| German, Spanish, French n-gram ZIPs | about 1.7, 1.7 and 1.8 GB | ngram-data folder |
| Disk type for n-grams | SSD | n-gram page: without an SSD, LanguageTool can get much slower |
This part is our own arithmetic: unpacking English while the ZIP is still on disk needs roughly 24.4 GB free at the peak. For memory, the README's one piece of advice (written for the command-line tool) is to raise the Java heap with -Xmx if Java runs out of memory; the same flag goes in the ExecStart line below.
Choosing the machine
Read the Storage column on the dedicated server lines before the processor column. A line listed with SATA-SSD or NVMe-SSD storage matches the n-gram advice; a line listed with plain SATA disks, such as the Intel Atom C2750 cartridge as it stands, does not. Without n-grams, the smallest SSD cartridge has disk to spare.
- Whole machine, root, no second tenant on the hardware: the checker and whatever runs next to it get every core and all the memory.
- Every racked single-, dual- and quad-socket server has an out-of-band console (HPE iLO, Dell iDRAC or a Supermicro BMC). Of the cartridges, only the m510 and m710x carry one; the rest offer power control alone.
- Not the X-Gene ARM cartridge: it installs only Ubuntu 16.04 and 18.04, and the steps here rely on Ubuntu 24.04 packages for Java 21.
- A racked machine is handed over in about 30 minutes once payment is approved; a built-to-order line takes 4 to 24 hours.
The same machine can carry the documents you are checking. See BookStack on a dedicated server or the Outline wiki setup.
Installing the snapshot under a service user
On Ubuntu 24.04, as a sudo user: install a Java inside the 17-21 range, create a no-login languagetool account, unpack the latest snapshot into a dated release directory and point a current symlink at it. The empty server.properties is what the official page tells you to create when you skip fastText.
sudo apt update
sudo apt install -y openjdk-21-jre-headless unzip curl
sudo useradd --system --user-group --home-dir /opt/languagetool --shell /usr/sbin/nologin languagetool
sudo mkdir -p /opt/languagetool/releases /etc/languagetool
sudo touch /etc/languagetool/server.properties
cd /tmp
curl -fLO https://languagetool.org/download/snapshots/LanguageTool-latest-snapshot.zip
REL=/opt/languagetool/releases/$(date +%F)
sudo mkdir -p "$REL"
sudo unzip -q LanguageTool-latest-snapshot.zip -d "$REL"
sudo ln -sfn "$(ls -d "$REL"/LanguageTool-*)" /opt/languagetool/current
ls /opt/languagetool/current/languagetool-server.jar
The folder inside the ZIP carries the version (today LanguageTool-6.9-SNAPSHOT), hence ls -d for the symlink. Files stay owned by root; the service only reads them.
The official page strongly recommends fastText on Linux for good language detection. Follow its two fastText links to build the binary and fetch lid.176.bin, then put fasttextModel= and fasttextBinary= lines with those paths in /etc/languagetool/server.properties.
Running it under systemd
The unit file goes in /etc/systemd/system/languagetool.service. The command line is the one from the official page, with absolute paths. The explicit Java 21 path keeps the server on a supported Java even if a newer default is installed later. On the Ampere Altra ARM line the same package lives under /usr/lib/jvm/java-21-openjdk-arm64/bin/java.
[Unit]
Description=LanguageTool HTTP server
After=network.target
[Service]
User=languagetool
Group=languagetool
WorkingDirectory=/opt/languagetool/current
ExecStart=/usr/lib/jvm/java-21-openjdk-amd64/bin/java -cp /opt/languagetool/current/languagetool-server.jar org.languagetool.server.HTTPServer --config /etc/languagetool/server.properties --port 8081 --allow-origin
Restart=on-failure
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
- Load and start it:
sudo systemctl daemon-reload && sudo systemctl enable --now languagetool. - Test with the documented call:
curl -d "language=en-US" -d "text=a simple test" http://localhost:8081/v2/check. A JSON reply means it works. - Read the log with
journalctl -u languagetool. The server prints a warning when it runs as root (seen in its source,Server.java); under this unit it should not.
--allow-origin with no value sets the CORS header to *, i.e. any website, per the server's own help text and HTTPServerConfig.java. The docs say it is needed for the browser add-on; drop it if you only call the API from scripts. Leave --verbose off if the texts are private: when a check hits an exception, it logs up to 500 characters of the input.
Adding the n-gram data
The docs list n-gram data for English, German, French and Spanish (the download folder also holds a Dutch file). Each ZIP unpacks to a folder named for its language, and the setting points at the folder that contains it.
sudo mkdir -p /opt/languagetool/ngrams && cd /opt/languagetool/ngramssudo curl -fL -o en.zip https://languagetool.org/download/ngram-data/ngrams-en-20150817.zipsudo unzip -q en.zip && sudo rm en.zipleavesen/1grams,en/2gramsanden/3grams.echo 'languageModel=/opt/languagetool/ngrams' | sudo tee -a /etc/languagetool/server.properties, thensudo systemctl restart languagetool.
The docs give a test sentence that only the n-gram rule flags: "Don't forget to put on the breaks."
Keeping the API private
Started without --public, the server binds to localhost and accepts requests only from 127.0.0.1 and ::1 (the address list is in Server.java). With --public it prints that the API can be accessed without restrictions (the warning is in HTTPServer.java). The basic server asks callers for no login, so anything that can reach the port can use it. To reach it from elsewhere without --public, use a tunnel (step 1) or a proxy (steps 2 to 4):
- Just for you: a tunnel over your own SSH account,
ssh -N -L 8081:127.0.0.1:8081 you@your-server. Your laptop's port 8081 is now the server's. - For a team: nginx on the same host with a password and HTTPS, as the official page recommends for TLS. Firewall first:
sudo sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw, thensudo ufw allow OpenSSH,sudo ufw allow 80,443/tcpandsudo ufw --force enable. nginx is an ordinary process on the host, so without that rule it is blocked on IPv4 and IPv6 alike. - Install
nginx,apache2-utils,certbotandpython3-certbot-nginx, create users withsudo htpasswd -c /etc/nginx/languagetool.htpasswd alice, thensudo chown root:www-dataandsudo chmod 640that file. - Save the site below in
/etc/nginx/sites-available/languagetool, link it intosites-enabled, runsudo nginx -t && sudo systemctl reload nginx, thensudo certbot --nginx -d lt.example.combefore you hand out any password.
server {
listen 80;
listen [::]:80;
server_name lt.example.com;
location / {
auth_basic "LanguageTool";
auth_basic_user_file /etc/nginx/languagetool.htpasswd;
proxy_pass http://127.0.0.1:8081;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Because nginx connects from 127.0.0.1, the server keeps its default local-only mode. If you set requestLimit in server.properties (with requestLimitPeriodInSeconds, without which no limit applies, per Server.java), also set trustXForwardForHeader=true so the limit counts the callers' addresses rather than the proxy's.
Add-on, updates, licence
- Browser add-on: the cog icon leads to its options; under Experimental settings, choose Local server. With the tunnel above running, that means your server. The docs note that Safari needs HTTPS, set as Other server with a URL ending in
/v2. - Updates: unpack a newer snapshot into a new dated directory, move the
currentsymlink withln -sfn, thensudo systemctl restart languagetool. The old release stays for a rollback. - Backups: copy
/etc/languagetool, the unit and the nginx files off the machine yourself; the n-gram data can be fetched again. - Licence: LGPL 2.1 or later for the core (README and COPYING.txt). Some bundled libraries and data carry their own terms; the README in the download points to them.
For a notes app that pairs with it, see Joplin Server on bare metal.
Questions
Is there an official LanguageTool Docker image?
No. The GitHub README points to three community-contributed Docker projects instead. The official route is the snapshot ZIP run with Java.
Does a self-hosted server include the AI-based rules?
No. Per the official server page, the download is a basic server and the AI-based rules stay cloud-only. Rule-based checks, spelling and the optional n-gram rules run locally.
Which Java version should I install?
The current snapshot's change log says Java 17 up to 21. The server page still mentions Java 8 or later, which is older text. Install Java 21 and name it in the unit.
Can other machines reach the server on port 8081?
Not by default: without --public it accepts only 127.0.0.1 and ::1. Use an SSH tunnel for yourself, or a password-protected HTTPS proxy on the same host for a team.
How much disk do the English n-grams take?
The ZIP is about 9 GB and its file list adds up to about 15.4 GB unpacked, so keep around 25 GB free while unpacking, on SSD.