GuidesBusiness and monitoringGrammar with LanguageTool

Self-host LanguageTool, a Grammarly alternative, with rootless Podman

LanguageTool on Debian 13, the open-source grammar and spell checker, in rootless Podman behind Caddy, for Swedish and English, so your team's text is checked on your own server from the browser extension, LibreOffice or the API.

Tested on LanguageTool 6.8 on Debian 13 (trixie) on a Melonslab server Updated October 14, 2026

Recommended server for this guide

SSD RAID 1 · ECC DDR4 · 10 Gbit

VC-P Alloy · 2 vCPU · 8 GB Memory · 60 GB Storage

Month to month, no lock-in 7-day money-back guarantee

€8.99/mo

Deploy now
On this page

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 curl with 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.com pointing 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.8 tag 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 as 6.8-13, so a numbered tag can disappear; 6.8 stays.
  • User=783:783 starts the container as the image's own user instead of root, and ReadOnly=true keeps its files unchanged. :U gives that user ownership of the volume.
  • The languagetool-fasttext volume holds the fastText model, which recognises the language of a text. The image downloads it, 126 MB, on the first start.
  • JAVA_XMX=1g lets 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:

  1. Open the extension's settings, and choose Advanced settings (only for professional users) at the bottom.
  2. Under LanguageTool server:, choose Other server—requires LanguageTool server running there, and enter https://lt.example.com/v2.
  3. 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:

  1. Open Tools > Options > Languages and Locales > LanguageTool Server. On macOS, Tools is under LibreOffice > Preferences.
  2. Tick Enable LanguageTool.
  3. 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.
  4. 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-gramsWith English n-grams
Download–8.4 GB, about 7 minutes on our server
Disk–15 GB
English text of 228 words0.2–0.3 seconds1.3–2.0 seconds
Swedish text of 184 words0.3–0.6 seconds0.3–0.6 seconds
Checks a minute, 10 users at once267132

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:

SentenceMistakeFound
Vi har skickat en offfert till kunnden.two misspellingsyes, both
Mötet börjar klockan klockan nio.repeated wordyes
Det var ett stor misstag.ett storyes, but suggested "en stor misstag"
Vi ses på Måndag.capital letter on a weekdayyes
Hej , hur mår du?space before the commayes
jag skickar fakturan i morgon.lower-case startyes
Ring kund tjänst om du har frågor.särskrivningno
Dem kom sent till mötet.dem for deno
Jag kom sent eftersom att bussen var försenad.eftersom attno
Jag har inte hunnit läst rapporten.läst for läsano
Jag har en hus på landet.en husno
Bilarna är röd.röd for rödano
Vi har mycket saker att göra.mycket for mångano

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 limitAfter the startAfter checks in both languages
JAVA_XMX=1g, as in step 2550 MB1.2 GB, 1.4 GB with n-grams
JAVA_XMX=1536m, the image's default500 MB1.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.

Run it on your own server

SSD RAID 1 · ECC DDR4 · 10 Gbit

VC-P Alloy

€8.99/mo

vCPU
2
Memory
8 GB
Storage
60 GB
Transfer
10 TB
Performance
SSD · RAID 1
  • Full root access
  • Native /64 IPv6
  • RAID-protected storage
  • Malmö, Sweden
  • Month to month, no lock-in
  • 7-day money-back guarantee
All guides