What you will set up
LanguageTool checks spelling, grammar and punctuation in more than 20 languages, Swedish and English among them. It underlines mistakes as you type in the browser, in LibreOffice and in other apps, and explains each one. Here it runs as a server of your own, so the text your team writes is checked on that server and never sent to an outside service.
LanguageTool is made by LanguageTooler GmbH in Hamburg, Germany. Its core, which is what runs here, is open source under the LGPL 2.1 or later, as its source repository says (checked 14 October 2026). Version 6.8 came out on 5 May 2026.
LanguageTool as a Grammarly alternative. Grammarly checks text in its own cloud only. Its list of languages (checked 14 October 2026) gives Swedish spelling and basic grammar, while its advanced suggestions for clarity, fluency and tone cover English, Spanish, French, German, Portuguese and Italian. Its privacy policy, effective 6 July 2026 and now published by Superhuman Platform Inc. (formerly Grammarly), says your data may be processed outside the country where you live, and that transfers from the EU to the United States rely on the EU-U.S. Data Privacy Framework. With LanguageTool on your own server, the text stays on it.
What the open-source server does not have. LanguageTool Premium, the paid service, adds to what runs here. Its Premium page (checked 14 October 2026) lists more than 20,000 additional checks for English, German, French, Spanish, Dutch, Polish and Portuguese, unlimited sentence rephrasing with AI (three a day without Premium), a personal style guide, and texts of up to 150,000 characters. LanguageTool's server documentation says that "the AI-based rules are only available in the cloud", and that the server does not support synonyms. LanguageTool's Daniel Naber wrote on its forum that the public code is "the Open Source version without premium features", and that an on-premise Premium version exists for larger business customers. Swedish is not among the languages that get the additional checks, so for Swedish the open-source rules are close to all that LanguageTool has.
LanguageTool's browser extension now needs Premium to check text on LanguageTool's own servers. Its announcement says the extension "will continue to work as it currently does for users who use it with their own server", which is what this guide sets up.
LanguageTool publishes no container image of its own; its repository lists three made by the community. This guide uses meyay/languagetool: it is rebuilt from LanguageTool's own release tags, it updated its base system and Java libraries for security fixes three days before our test, it runs as an unprivileged user with a read-only file system, and it includes fastText for language detection and a fix for a memory leak in the spell checker that Swedish uses. The most downloaded one, erikvl87/languagetool, had its last update in June 2026. LanguageTool itself stopped publishing release ZIP files after version 6.6, so every image builds its own.
Here LanguageTool runs in Podman under a user of its own called languagetool, behind Caddy from the Podman guide, which lets your office in and asks everyone else for a password.
Every step below was run on a fresh Melonslab VC-P Alloy (2 vCPU, 8 GB) with Debian 13:
- LanguageTool 6.8 answered 23 seconds after the first start, which downloaded the 537 MB image and the 126 MB fastText model. Later restarts took 6 seconds.
- 13 Swedish and 13 English sentences with known mistakes, and four correct ones, were checked. It found the mistakes in 6 of the Swedish sentences and flagged 11 of the English ones, one with the wrong suggestion, and none of the correct sentences (step 8 has the list).
- LanguageTool's own browser extension, in Chromium, underlined mistakes in Swedish and English through this server. It worked from the office address and failed behind the password, which the extension cannot send.
- LibreOffice 25.2 checked Swedish and English documents through the server, with the password in its address.
- The API answered
curlwith the password, and with "401" without it. - English n-gram data made LanguageTool catch three more mixed-up words, at the cost of 15 GB of disk and slower English checks (step 7).
- A word list made it accept company names in both languages.
- A capture of the server's traffic showed no outside connections while LanguageTool started and checked text, and its log does not contain the text it checks.
- An image update went through
podman auto-update, the setup was restored from a backup, and everything came back by itself after a reboot.
LanguageTool used about 550 MB of memory just after starting, and about 1.2 GB once both languages had been used, with Java limited to 1 GB.
Before you start
You need:
- a server set up as in the Podman guide, with Caddy running;
- an A record and an AAAA record for
lt.example.compointing at your server; - the public IPv4 address of your office, and its IPv6 range if it has one. Search for "what is my IP" from a computer there.
The examples use lt.example.com for LanguageTool, 198.51.100.7 and 2001:db8:7::/48 for your office, and 10.8.0.0/24 and 2001:db8:1f::/64 for devices on a WireGuard VPN, as in the WireGuard guide. Replace them throughout.
LanguageTool sends no email, so there is no email step.
1. Create the user
As root:
useradd -m -s /bin/bash languagetool
loginctl enable-linger languagetool
machinectl shell languagetool@
Everything up to step 3 runs as languagetool.
2. Define the container
Create a word list, which step 9 explains. For now, add your company's name:
echo "Examplecorp" > ~/words.txt
mkdir -p ~/.config/containers/systemd
Create ~/.config/containers/systemd/languagetool.container:
[Unit]
Description=LanguageTool
[Container]
ContainerName=languagetool
Image=docker.io/meyay/languagetool:6.8
# LanguageTool runs as the image's own unprivileged user, on a read-only file system.
User=783:783
ReadOnly=true
Tmpfs=/tmp:exec
Volume=languagetool-fasttext:/fasttext:U
Volume=%h/words.txt:/languagetool/org/languagetool/resource/en/hunspell/spelling_custom.txt:ro
Volume=%h/words.txt:/languagetool/org/languagetool/resource/sv/hunspell/spelling_custom.txt:ro
Environment=JAVA_XMS=256m JAVA_XMX=1g
# Only Caddy, on this server, can reach LanguageTool: the port is not open to the internet.
PublishPort=127.0.0.1:8140:8081
AutoUpdate=registry
[Service]
Restart=always
[Install]
WantedBy=default.target
What the settings do:
- The
6.8tag follows every rebuild of LanguageTool 6.8, with security fixes, but never moves to a new version of LanguageTool by itself. The image only keeps its latest numbered tags, such as6.8-13, so a numbered tag can disappear;6.8stays. User=783:783starts the container as the image's own user instead of root, andReadOnly=truekeeps its files unchanged.:Ugives that user ownership of the volume.- The
languagetool-fasttextvolume holds the fastText model, which recognises the language of a text. The image downloads it, 126 MB, on the first start. JAVA_XMX=1glets Java use up to 1 GB for its data. Step 10 has the details.
Start it:
systemctl --user daemon-reload
systemctl --user start languagetool
systemctl --user enable --now podman-auto-update.timer
The first start downloads the image and the fastText model. It is ready when this prints 200, after about 25 seconds:
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8140/v2/languages
3. Put Caddy in front
LanguageTool's server has no logins: anyone who reaches it can use it, and its log warns that it runs "in public mode". So Caddy decides who gets in:
- Your office and devices on your VPN get in directly. LanguageTool's browser extension cannot send a password (step 5), so this is how it gets in.
- Everyone else must log in with a user name and a password, which LibreOffice and the API can send.
Go back to root with exit, and switch to machinectl shell caddy@. Make a password for the login. The address of the server will hold it in LibreOffice (step 6), so use one with only letters and digits:
openssl rand -hex 16
Keep it in your password manager. Make its hash, typing the password twice:
podman exec -it caddy caddy hash-password
It prints a line that starts with $2a$14$. Add this block at the end of ~/Caddyfile, with that line in place of HASH:
lt.example.com {
# Your office and VPN get in directly; everyone else must log in.
@outside not remote_ip 198.51.100.7 2001:db8:7::/48 10.8.0.0/24 2001:db8:1f::/64
basic_auth @outside {
team HASH
}
reverse_proxy 127.0.0.1:8140
}
team is the user name. Add your office's IPv6 range if it has one: computers there prefer IPv6, and would otherwise get the login. Restart Caddy:
systemctl --user restart caddy
4. Check text from the command line
From any computer, with your password in place of PASSWORD:
curl -s -u team:PASSWORD https://lt.example.com/v2/check \
-d language=sv-SE --data-urlencode 'text=Det var ett stor misstag.' \
| python3 -c 'import json, sys; [print(m["message"]) for m in json.load(sys.stdin)["matches"]]'
Adjektivet är böjt i positiv utrum (stor) och bör då föregås av ordet "en" istället för "ett", alternativt böjas i positiv neutrum. Menade du: "en stor misstag"?
Without -u, from outside the office, the answer is 401. LanguageTool answers in JSON, and the last line only prints the messages: each item under matches also has where in the text the mistake is (offset and length), and suggestions under replacements. Use language=en-US or en-GB for English, or language=auto to let LanguageTool tell. With auto, fastText recognised short Swedish and English sentences correctly in our tests, with 96 to 99 % certainty.
The API is the same as LanguageTool's public one, so scripts and apps that let you change LanguageTool's address work with yours. It took a text of 36,000 characters in one request.
5. Use the browser extension
Install LanguageTool's extension for Chrome or Firefox. Then, from the office or the VPN:
- Open the extension's settings, and choose Advanced settings (only for professional users) at the bottom.
- Under LanguageTool server:, choose Other server—requires LanguageTool server running there, and enter
https://lt.example.com/v2. - Choose Save.
You can only choose your own server when you are logged out of a LanguageTool account. The extension has no Swedish interface, but it checks Swedish text, and LanguageTool's explanations of Swedish mistakes are in Swedish.
What we saw in Chromium with extension 11.4.0:
- From the office address, it underlined mistakes in English and in Swedish, and the server's log showed each check from
webextension-chrome-ng. - From an address outside the list, the extension showed "Sorry, something went wrong while checking your text" with
401. It sends its requests without a login, so the password does not help, and a user name and password in the address,https://team:PASSWORD@lt.example.com/v2, gave "Cannot connect".
So people working from home use the VPN. The text you check goes only to your server, but the extension itself still talks to LanguageTool: when it was installed, before we had typed anything, it opened LanguageTool's welcome page, fetched its settings, sent one request to LanguageTool's own checking service and a usage ping to LanguageTool's statistics service. Its rephrasing, synonyms and translation are LanguageTool's cloud services, which we did not use.
6. Use LibreOffice
LibreOffice has a LanguageTool checker built in, so no extension is needed. In LibreOffice 25.2:
- Open Tools > Options > Languages and Locales > LanguageTool Server. On macOS, Tools is under LibreOffice > Preferences.
- Tick Enable LanguageTool.
- Set Base URL to
https://team:PASSWORD@lt.example.com/v2, with your password. Leave Username and API key empty: they are for LanguageTool Premium. - Choose OK. Under Writing Aids in the same dialog, check that LanguageTool Remote Grammar Checker is ticked.
LibreOffice sends the user name and password in the address as a normal login, so it works from anywhere. In our test, a Swedish document had "Det var ett stor misstag" and a misspelled word underlined, and an English one "He go to the office". With the wrong password, LibreOffice shows no error: it simply finds no mistakes. LibreOffice keeps the address, with the password, in your user profile.
7. Better English with n-gram data
LanguageTool can use n-gram data, counts of how often words appear together in a very large amount of English text, to catch words that are spelled right but mixed up, such as "accept" and "except". The data exists for English, German, French, Spanish and Dutch, but not for Swedish.
It is large, and makes English checks slower:
| Without n-grams | With English n-grams | |
|---|---|---|
| Download | – | 8.4 GB, about 7 minutes on our server |
| Disk | – | 15 GB |
| English text of 228 words | 0.2–0.3 seconds | 1.3–2.0 seconds |
| Swedish text of 184 words | 0.3–0.6 seconds | 0.3–0.6 seconds |
| Checks a minute, 10 users at once | 267 | 132 |
Of 15 English sentences with mixed-up words, LanguageTool's normal rules caught 10. The n-grams added three: "accept" for "except", "see" for "sea" and "borrow" for "lend". Two were missed either way. Use them if you write mostly English and have the disk. LanguageTool recommends a fast SSD for them.
As languagetool, add these two lines to languagetool.container, under the other Volume and Environment lines:
Volume=languagetool-ngrams:/ngrams:U
Environment=download_ngrams_for_langs=en
systemctl --user daemon-reload
systemctl --user restart languagetool
The image downloads and unpacks the data before LanguageTool starts, so the server is away for the 5 to 10 minutes this takes. journalctl --user -u languagetool -f shows the progress. It is not downloaded again after that.
8. How well it checks Swedish and English
We checked sentences with known mistakes in both languages, with English n-grams on. For Swedish, LanguageTool found:
| Sentence | Mistake | Found |
|---|---|---|
| Vi har skickat en offfert till kunnden. | two misspellings | yes, both |
| Mötet börjar klockan klockan nio. | repeated word | yes |
| Det var ett stor misstag. | ett stor | yes, but suggested "en stor misstag" |
| Vi ses på Måndag. | capital letter on a weekday | yes |
| Hej , hur mår du? | space before the comma | yes |
| jag skickar fakturan i morgon. | lower-case start | yes |
| Ring kund tjänst om du har frågor. | särskrivning | no |
| Dem kom sent till mötet. | dem for de | no |
| Jag kom sent eftersom att bussen var försenad. | eftersom att | no |
| Jag har inte hunnit läst rapporten. | läst for läsa | no |
| Jag har en hus på landet. | en hus | no |
| Bilarna är röd. | röd for röda | no |
| Vi har mycket saker att göra. | mycket for många | no |
For English, it found "He go", "could of", "a importent", "The the", "Its a nice day", "I have went", "I didn't now that", "your advise", "will effect our prices" and "accept Tom", the last with n-grams only. It flagged "send me you're invoice", but suggested "invoiced" instead of "your". It missed "Their going to the meeting" and "the prize of the new plan". Four correct sentences, two in each language, got no warnings.
So for Swedish, LanguageTool is a good spell checker that also catches some grammar and typing mistakes, but it misses many of the grammar mistakes Swedes make. For English it catches far more.
9. Words of your own
LanguageTool marks names it does not know, such as your company's or your products', as misspellings. The words.txt from step 2 is read for both English and Swedish: one word per line. In our test, "Melonslab" and "Hypefox" were accepted in both languages once they were in the list, while "Melonslabb" was still marked. After a change, as languagetool:
systemctl --user restart languagetool
The browser extension also has a personal dictionary of its own, under Personal dictionary in its settings, which stays in that browser.
10. Memory and speed
LanguageTool is a Java program. JAVA_XMS is the memory Java takes at the start, and JAVA_XMX the most it may use for its data; Java grows towards that limit as LanguageTool loads the rules for each language, and keeps it. fastText adds about 140 MB.
| Java limit | After the start | After checks in both languages |
|---|---|---|
JAVA_XMX=1g, as in step 2 | 550 MB | 1.2 GB, 1.4 GB with n-grams |
JAVA_XMX=1536m, the image's default | 500 MB | 1.4 GB, 1.8 GB with n-grams |
The figures include fastText. 1 GB was enough for all our tests, also with ten users at once. Raise the limit if LanguageTool's log shows OutOfMemoryError, for example with more languages or many users. With n-grams, Linux also keeps parts of the data in its file cache, which it gives back when other programs need the memory.
The first check after a start takes longer while LanguageTool loads a language: 5 to 10 seconds for the first English text in our tests, and about a second for Swedish. After that, a short sentence took 0.05 to 0.3 seconds. The extension checks a paragraph when you stop typing, so a team of ten rarely checks at the same time. When ten users sent texts without a pause, the two vCPUs were the limit (step 7 has the numbers).
11. Outside connections
We captured the server's DNS lookups and outgoing connections while LanguageTool started, checked text in both languages and sat idle: there were none. Its log records the length and language of each check, but not the text.
On the first start, the image downloads the fastText model from dl.fbaipublicfiles.com, run by Meta, and with step 7, the n-gram data from languagetool.org. The image itself comes from Docker Hub. The browser extension's own connections to LanguageTool are in step 5.
12. Back up
LanguageTool keeps nothing that your team writes: it checks the text and forgets it. The fastText model and the n-grams are downloaded again when they are missing. What you set up yourself is the container file and the word list. As languagetool:
mkdir -p ~/backup
tar -czf ~/backup/languagetool.tar.gz -C ~ .config/containers/systemd/languagetool.container words.txt
Copy ~/backup to another machine, for example with restic, together with Caddy's ~/Caddyfile, which has the password hash. The extension's personal dictionaries stay in each browser.
To restore on a new server, create the user as in step 1, copy the backup to ~/backup, and as languagetool:
tar -xzf ~/backup/languagetool.tar.gz -C ~
systemctl --user daemon-reload
systemctl --user start languagetool
systemctl --user enable --now podman-auto-update.timer
Then add the Caddy block as in step 3.
13. Update
podman auto-update checks once a day for a new build of the 6.8 image and restarts LanguageTool when there is one. To update now:
podman auto-update
A new version of LanguageTool, such as 6.9, gets a tag of its own. When it is out, change 6.8 in languagetool.container to the new version, then daemon-reload and restart. LanguageTool lists its changes in its change log.
Troubleshooting
The container stops at once, and its log says directory "/fasttext" is owned by 0:0. The :U at the end of a Volume line is missing. Add it, then daemon-reload, systemctl --user reset-failed languagetool and start it again. The same goes for /ngrams.
The extension says "Sorry, something went wrong while checking your text" with 401. You are not at an address in the Caddy block: from home, connect to the VPN. If you are at the office, check that its IPv6 range is in the list.
LibreOffice finds no mistakes. Check the password in the Base URL, and that it ends in /v2. curl as in step 4, with the same password, shows whether the server answers.
The first check after a restart is slow. LanguageTool loads each language on its first check. It takes a few seconds, once.