Writing clear, error-free documentation at scale is one of the hardest problems for engineering teams. Relying on cloud-based grammar checkers like Grammarly means sending your internal documents — API specs, runbooks, architecture decision records — through third-party servers. For teams with compliance requirements or privacy concerns, self-hosted grammar and style checking is not optional.

In this guide, we compare three leading open-source tools for self-hosted writing quality: LanguageTool, Vale, and textlint. Each takes a fundamentally different approach — from full grammar checking to style guide enforcement to pluggable text linting — and the right choice depends on your workflow, language needs, and team size.

For teams already enforcing code quality with megalinter or reviewdog, adding prose linting to your pipeline is the natural next step. Similarly, if you manage a documentation site with Hugo or Docusaurus, these tools catch errors before they reach production.

Why Self-Host Your Grammar and Style Checker

Cloud-based writing assistants are convenient, but they come with tradeoffs that matter for technical teams:

  • Privacy: Your internal documentation, API specs, and confidential reports are sent to external servers. For healthcare, finance, and government teams, this violates data residency policies.
  • Cost: Enterprise plans for Grammarly Business start at $15/user/month. For a 50-person engineering team, that’s $9,000/year — for a service that mostly checks spelling and comma placement.
  • Offline access: Self-hosted tools work without internet connectivity, critical for air-gapped environments and CI pipelines in isolated networks.
  • Customization: Open-source tools let you define your own style guides, terminology lists, and domain-specific rules that no commercial product will ever support.
  • Integration: Self-hosted servers expose APIs you can wire into your existing CI/CD, IDE, and editor workflows without vendor lock-in.

LanguageTool is the most widely used open-source grammar and style checker. Written in Java, it supports 30+ languages with deep grammatical analysis — not just spelling corrections, but syntax errors, wrong word choices, punctuation rules, and style suggestions.

With 14,418 GitHub stars and active development (last commit April 2026), LanguageTool is the closest open-source equivalent to Grammarly you can run on your own infrastructure.

How LanguageTool Works

LanguageTool uses a rule-based engine with pattern matching and statistical analysis. Each language has hundreds of XML-defined rules covering:

  • Spelling and typos
  • Grammatical errors (subject-verb agreement, tense consistency)
  • Punctuation (missing commas, incorrect quotation marks)
  • Style (passive voice, wordiness, jargon)
  • Typography (smart quotes, em dashes, non-breaking spaces)

The server exposes a REST API that accepts text and returns structured error reports with suggestions, positions, and rule categories.

Docker Compose Setup

The community-maintained erikvl87/languagetool Docker image (5.1M+ pulls) provides the easiest deployment:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# docker-compose.yml — LanguageTool Server
services:
  languagetool:
    image: erikvl87/languagetool:latest
    container_name: languagetool
    restart: unless-stopped
    ports:
      - "8010:8010"
    environment:
      - Java_Xms=512m
      - Java_Xmx=1024m
      - langLevel=8
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8010/v2/languages"]
      interval: 30s
      timeout: 10s
      retries: 3
    deploy:
      resources:
        limits:
          memory: 2G

Start the server:

1
2
3
4
5
6
7
docker compose up -d

# Test the API
curl -s http://localhost:8010/v2/check \
  --data-urlencode "language=en-US" \
  --data-urlencode "text=I has a spelling erorr and grammer mistake." \
  | python3 -m json.tool

The API returns structured results with error positions, descriptions, and replacement suggestions.

IDE Integration

LanguageTool has plugins for VS Code, IntelliJ, Vim, and Emacs. For VS Code:

1
2
3
4
5
6
// .vscode/settings.json
{
  "languagetool.language": "en-US",
  "languagetool.serverUri": "http://localhost:8010",
  "languagetool.enabled": true
}

When to Choose LanguageTool

  • You need grammar checking (not just style), especially for non-native English speakers
  • You work in multiple languages — LanguageTool’s 30+ language support is unmatched
  • You want a Grammarly-like experience with your own server
  • Your team writes emails, reports, and documentation that needs grammatical correctness

Vale: The Style Guide Enforcer

Vale takes a fundamentally different approach. Instead of checking grammar rules, Vale enforces custom style guides on your prose. Think of it as ESLint for writing — you define the rules, and Vale flags violations.

Written in Go, Vale is fast, cross-platform, and integrates directly into CI/CD pipelines. With 5,363 GitHub stars, it’s the go-to choice for documentation teams at companies like Google, Red Hat, and HashiCorp.

How Vale Works

Vale reads configuration from a .vale.ini file in your project root. You define which style guides to load (Google, Microsoft, Red Hat, or custom), and Vale checks your Markdown, AsciiDoc, HTML, and plain text files against those rules.

Rules cover:

  • Terminology consistency (e.g., “use ‘sign in’, not ’log in’”)
  • Readability metrics (Flesch-Kincaid grade level)
  • Forbidden words and phrases
  • Required heading styles
  • Code block formatting in prose

Installation and Setup

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Install via package manager
# macOS
brew install vale

# Linux (Debian/Ubuntu)
sudo apt install vale

# Or download the binary directly
curl -sL https://github.com/vale/vale/releases/download/v3.9.2/vale_3.9.2_Linux_64-bit.tar.gz | tar xz
sudo mv vale /usr/local/bin/

# Initialize in your project
vale init

Configuration Example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# .vale.ini
StylesPath = .vale-styles
MinAlertLevel = suggestion
Packages = Google, Microsoft, Readability
Vocab = Docs

[*.md]
BasedOnStyles = Vale, Google, Microsoft
Google.WordList = NO
Google.Passive = NO
Microsoft.GeneralURL = YES
Microsoft.OxfordComma = YES

Create a custom vocabulary file for your team’s terminology:

1
2
3
4
5
# .vale-styles/Vocab/Docs/accept.txt
Kubernetes
Docker Compose
PostgreSQL
API gateway
1
2
3
4
# .vale-styles/Vocab/Docs/reject.txt
k8s (use Kubernetes)
docker-compose (use Docker Compose)
postgres (use PostgreSQL)

CI/CD Pipeline Integration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# .github/workflows/vale.yml
name: Vale Linting
on: [pull_request]

jobs:
  vale:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Vale
        run: |
          curl -sL https://github.com/vale/vale/releases/download/v3.9.2/vale_3.9.2_Linux_64-bit.tar.gz | tar xz
          sudo mv vale /usr/local/bin/
      - name: Run Vale
        run: vale --minAlertLevel=error docs/

Docker Setup

While Vale is primarily a CLI tool, you can containerize it for shared CI environments:

1
2
3
4
5
6
7
8
# Dockerfile for Vale CI runner
FROM alpine:3.19

RUN apk add --no-cache curl bash
RUN curl -sL https://github.com/vale/vale/releases/download/v3.9.2/vale_3.9.2_Linux_64-bit.tar.gz | tar xz -C /usr/local/bin/

WORKDIR /docs
ENTRYPOINT ["vale"]

When to Choose Vale

  • You need style guide enforcement across a large documentation set
  • Your team has brand-specific terminology that must be consistent
  • You want CI/CD integration with fast, lightweight linting
  • You write primarily in English and need readability scoring

textlint: The Pluggable Text Linter

textlint is a Node.js-based pluggable linter for natural language text. Like ESLint revolutionized JavaScript code quality, textlint aims to do the same for prose. With 3,114 GitHub stars, it’s popular in Japanese and English technical writing communities.

How textlint Works

textlint’s architecture mirrors ESLint: a core engine loads plugins (rules) defined in your .textlintrc file. Each plugin checks specific aspects of your text — grammar, terminology, formatting — and reports violations.

Key differences from Vale:

  • Plugin ecosystem: 200+ plugins on npm covering grammar, terminology, spacing, and more
  • AST-based: Parses Markdown and HTML into abstract syntax trees, enabling precise rule targeting (e.g., “check spelling in paragraphs but not in code blocks”)
  • Japanese support: First-class support for Japanese text analysis with MeCab integration

Installation

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Requires Node.js 18+
npm install -g textlint

# Install plugins
npm install -g \
  textlint-rule-no-todo \
  textlint-rule-common-misspellings \
  textlint-rule-max-comma \
  textlint-rule-terminology \
  textlint-rule-write-good

Configuration Example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
  "rules": {
    "no-todo": true,
    "common-misspellings": true,
    "max-comma": {
      "max": 3
    },
    "terminology": {
      "preferred": {
        "e-mail": "email",
        "open-source": "open source",
        "github": "GitHub"
      }
    },
    "write-good": {
      "passive": true,
      "so": true,
      "thereIs": true,
      "weasel": true
    }
  },
  "filters": {
    "comments": true
  }
}

Docker Compose Setup

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# docker-compose.yml — textlint CI Runner
services:
  textlint:
    image: node:20-alpine
    container_name: textlint-runner
    working_dir: /docs
    volumes:
      - ./content:/docs/content:ro
      - ./.textlintrc:/docs/.textlintrc:ro
      - ./package.json:/docs/package.json:ro
      - node_modules:/docs/node_modules
    entrypoint: ["sh", "-c"]
    command:
      - |
        npm install
        npx textlint --rule no-todo --rule common-misspellings --rule write-good content/**/*.md
    profiles:
      - ci

volumes:
  node_modules:

Run the linter:

1
docker compose --profile ci up --abort-on-container-exit

When to Choose textlint

  • Your team already uses the Node.js/npm ecosystem
  • You need fine-grained rule customization through plugins
  • You write in Japanese or other languages with strong textlint support
  • You want an ESLint-like experience for your documentation

Comparison Table

FeatureLanguageToolValetextlint
Primary purposeGrammar & spell checkingStyle guide enforcementPluggable text linting
LanguageJavaGoTypeScript/Node.js
GitHub stars14,4185,3633,114
Languages supported30+English-focused10+ (strong Japanese)
Server modeREST API (port 8010)CLI onlyCLI only
Docker supportCommunity image (5M+ pulls)Binary in containerNode.js container
CI/CD integrationVia API callsNative CLInpm script
Custom rulesXML rule definitionsYAML style filesJavaScript plugins
Readability scoringNoYes (Flesch-Kincaid)Via plugins
Markdown awarenessPartialFull ASTFull AST
Memory usage~1-2 GB (JVM)~50 MB~200 MB
Best forGrammar checking, multi-languageStyle consistency, docs teamsPlugin ecosystem, Node.js shops

Integration Patterns

Combining All Three Tools

For comprehensive writing quality, use all three tools together:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# .github/workflows/docs-quality.yml
name: Documentation Quality
on:
  pull_request:
    paths: ["docs/**/*.md"]

jobs:
  grammar:
    runs-on: ubuntu-latest
    services:
      languagetool:
        image: erikvl87/languagetool:latest
        ports: ["8010:8010"]
    steps:
      - uses: actions/checkout@v4
      - name: Check grammar with LanguageTool
        run: |
          for file in docs/**/*.md; do
            curl -s "http://localhost:8010/v2/check" \
              --data-urlencode "language=en-US" \
              --data-urlencode "text=$(cat "$file")" \
              | python3 -c "
import json, sys
data = json.load(sys.stdin)
for match in data.get('matches', []):
    print(f'{match[\"rule\"][\"category\"][\"id\"]}: {match[\"message\"]}')
"
          done

  style:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Vale
        run: |
          curl -sL https://github.com/vale/vale/releases/download/v3.9.2/vale_3.9.2_Linux_64-bit.tar.gz | tar xz
          sudo mv vale /usr/local/bin/
      - name: Check style with Vale
        run: vale --minAlertLevel=error docs/

  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Lint with textlint
        run: |
          npm install textlint textlint-rule-common-misspellings
          npx textlint docs/**/*.md

IDE Setup for Real-Time Checking

Create a shared IDE configuration so every team member gets the same quality checks:

1
2
3
4
5
6
7
// .vscode/extensions.json
{
  "recommendations": [
    "languagetool.language-tool-integration",
    "vale.vale-server"
  ]
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// .vscode/settings.json
{
  "vale.valeCLI.config": ".vale.ini",
  "vale.valeCLI.path": "/usr/local/bin/vale",
  "[markdown]: {
    "editor.codeActionsOnSave": {
      "source.fixAll.textlint": "explicit"
    }
  }
}

Pre-Commit Hook

Catch writing errors before they’re committed:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/bin/bash
# .husky/pre-commit

echo "Running grammar and style checks..."

# LanguageTool check (requires running server)
python3 scripts/check-grammar.py || exit 1

# Vale style check
vale docs/ --minAlertLevel=error || exit 1

# textlint
npx textlint docs/**/*.md || exit 1

echo "All writing quality checks passed."

Choosing the Right Tool

Your choice depends on three factors:

1. What kind of errors do you need to catch?

  • Grammatical errors (wrong tense, agreement, spelling) → LanguageTool
  • Style inconsistencies (terminology, tone, readability) → Vale
  • Custom rule violations (formatting, structure, domain-specific) → textlint

2. What is your team’s language situation?

  • Multiple languages, non-native English speakers → LanguageTool
  • English-only, established style guide → Vale
  • Japanese or Asian languages → textlint

3. What is your existing toolchain?

  • Java ecosystem, need a server API → LanguageTool
  • Go/CLI tools, CI-first workflow → Vale
  • Node.js/npm, ESLint culture → textlint

For most engineering teams writing technical documentation in English, Vale provides the best balance of speed, customization, and CI integration. For teams with multilingual content or non-native speakers, LanguageTool’s grammar checking is indispensable. And for Node.js teams already invested in the npm ecosystem, textlint’s plugin architecture fits naturally into existing workflows.

FAQ

Can I run LanguageTool without Docker?

Yes. LanguageTool is a Java application that runs on any system with Java 11+. Download the standalone JAR from languagetool.org and run it with java -cp languagetool-server.jar org.languagetool.server.HTTPServer --port 8010. However, Docker simplifies dependency management and makes it easy to run alongside other services.

Does Vale support languages other than English?

Vale’s core engine is language-agnostic, but most available style guides (Google, Microsoft, Red Hat) are English-focused. You can write custom style rules for any language, but you’ll need to define the terminology, grammar patterns, and readability formulas yourself. For non-English grammar checking, LanguageTool is a better choice.

How much memory does a self-hosted LanguageTool server need?

The Java-based server requires a minimum of 512MB heap (Java_Xms=512m) but performs best with 1-2GB. For a team of 10-20 users sending concurrent API requests, allocate 2GB total memory. The server caches language models in memory, so the first request after startup is slower than subsequent ones.

Can I use Vale with Markdown files that contain code blocks?

Yes. Vale parses Markdown into an AST and automatically skips content inside code blocks, fenced code, and inline code. It only checks prose content. This is one of Vale’s key advantages over simple regex-based checkers — it understands document structure and won’t flag variable names in code blocks as spelling errors.

How do I add custom terminology rules to LanguageTool?

LanguageTool supports custom dictionaries and rule files. Add words to org/languagetool/resource/en/added.txt in the data directory, or create custom XML rules in the rules/en/ directory. For Docker deployments, mount a volume with your custom files: -v ./custom-rules:/languagetool/custom-rules. Restart the server to load new rules.

Is textlint suitable for large documentation sites with thousands of files?

textlint handles large codebases well, but its Node.js-based architecture means startup time scales with the number of plugins loaded. For sites with 1,000+ Markdown files, consider running textlint with --cache to skip unchanged files, or split checks across parallel CI jobs using file-path glob patterns like textlint docs/api/**/*.md and textlint docs/guides/**/*.md.