LanguageTool 6.8 Docker Compose 部署实战

在 Ubuntu 22.04 服务器用 Docker Compose 落地 LanguageTool 6.8 实测:meyay/languagetool:6.8 镜像选型、JVM 堆压至 512MB 的 ps 验证、fastText/ngram 数据卷持久化、部署验证与常见问题排障实录。

LanguageToolLanguageTool2026/10/28

LanguageTool 6.8 Docker Compose 部署实战

目标:在一台 Ubuntu 服务器上用 Docker Compose 从零落地 LanguageTool v6.8 HTTP 校对服务,完成镜像选型、内存约束(JVM 堆压至 512MB 并验证生效)、数据持久化(fastText 模型与 ngram 语言模型卷)、部署后功能验证与常用运维操作。全文命令与回显均在 Ubuntu 22.04 服务器实测(端口 8130),照抄实录。

一、环境信息

  • 操作系统:Ubuntu 22.04(x86_64)
  • Docker Engine 27.x + Docker Compose v5.6.1(docker compose version → Docker Compose version v5.6.1)
  • LanguageTool 版本:6.8(官方 2026-05-05 发布,见 GitHub Releases)
  • 服务镜像:meyay/languagetool:6.8(约 288 MB,内置 Temurin 21 JRE 与 fastText,见下文选型说明)
  • 监听端口:宿主机 8130 → 服务 8081
  • 部署目录:/opt/languagetool/(含 docker-compose.yml、ngrams/、fasttext/)
  • 自定义网络:languagetool-6.8-stack 项目内网 languagetool-68-stack_lt-net
  • Java 进程约束:-Xms256m -Xmx512m(本篇第五节验证生效)

⚠️ 注意:语言规则模块已内置于镜像;fastText 语言识别模型(lid.176.bin,约 131MB)首次启动时自动下载并落盘到卷;ngram 语言模型为可选项,单语言数 GB,按需下载(见第八节)。

二、镜像选型说明

LanguageTool 官方仓库 README 明确列出三个社区维护的 Docker 发行镜像(2026-10-28 查证 Docker Hub Registry API):

镜像最新标签(查询日)更新时间压缩体积特点
meyay/languagetool6.8 / latest(6.8-13)2026-10-11288 MB直接从官方仓库 tag 构建;内置 fastText 与按需 ngram 下载器;支持只读根文件系统与非 root 运行
erikvl87/languagetool6.82026-06-15432 MB环境变量丰富,文档成熟
silviof/docker-languagetool—长期未更—久未跟进新版本

选型依据与关键事实:

  1. 不存在 languagetool/languagetool 官方镜像。用 Registry API 查询 hub.docker.com/v2/repositories/languagetool/languagetool/tags/ 返回 404 {"message":"object not found"}——网上教程常见这个写法,照抄会拉取失败。官方 README 指向的三个社区镜像才是可用发行物。
  2. v6.6 之后官方不再发独立 ZIP 包(官方 CHANGES.md 记载,社区镜像 README 引用),新版本落地主流路径即 Docker 镜像。
  3. 本文实测选择 meyay/languagetool:6.8:跟进官方版本最快(官方 v6.8 于 2026-05-05 发布,镜像 6.8-13 于 2026-10-11 更新)、体积最小、entrypoint 由 Go 编写(单文件、无 shell 依赖),并且是唯一默认内置 fastText 二进制的发行物。
  4. 该镜像把 JVM 堆参数暴露为 JAVA_XMS(默认 256m)/JAVA_XMX(默认 1536m)环境变量,本文将堆上限压到 512m(JVM 内存约束见第六节与《LanguageTool 配置参数详解》篇)。

镜像固定标签优先于 latest:升级由人工触发,避免服务在无人感知时被重建。

三、前置条件

  1. 服务器已安装 Docker Engine 与 Docker Compose 插件,当前用户在 docker 组(或以 root 执行):
docker --version && docker compose version
# Docker version 27.5.1, build 9f9e854
# Docker Compose version v5.6.1
  1. 出网能力检查(拉取镜像走 Docker Hub;首次启动还要从 languagetool.org 下载 fastText 模型约 131MB)。内网环境可给 Docker 配置 HTTP 代理;本文实测环境直接可达,未发生下载降级。
  2. 端口规划:本篇使用 8130(ss -ltn 确认未被占用):
ss -ltn | grep 8130 || echo "8130 空闲"
# 8130 空闲
  1. 创建部署目录:
mkdir -p /opt/languagetool/ngrams /opt/languagetool/fasttext
cd /opt/languagetool

⚠️ 注意:ngrams/ 与 fasttext/ 是挂载点,服务进程在实例内以 UID 783(镜像默认 MAP_UID/MAP_GID)运行,目录属主会被启动过程自动修正(DISABLE_FILE_OWNER_FIX=false 时);若关闭该修正,需手动 chown -R 783:783 /opt/languagetool。

四、部署步骤

4.1 编写 docker-compose.yml

在 /opt/languagetool/docker-compose.yml 写入以下内容(实测可用原样文件):

name: languagetool-6.8-stack

networks:
  lt-net:
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.56.0/24

services:
  languagetool:
    image: meyay/languagetool:6.8
    container_name: languagetool-6.8-server
    restart: unless-stopped
    ports:
      - "8130:8081"
    environment:
      JAVA_XMS: 256m
      JAVA_XMX: 512m
      JAVA_GC: G1GC
      LISTEN_PORT: 8081
      download_ngrams_for_langs: none
      MAP_UID: 783
      MAP_GID: 783
      LOG_LEVEL: INFO
    tmpfs:
      - /tmp:exec
    volumes:
      - ./ngrams:/ngrams
      - ./fasttext:/fasttext
    networks:
      - lt-net

逐项说明:

  • name: languagetool-6.8-stack:显式 Compose 项目名。多服务共存的服务器务必显式命名,否则项目名默认取目录名,不同项目目录同名时 docker compose down 会误删他人在跑的实例。
  • networks.lt-net:显式指定内网网段(10.89.56.0/24),避免与服务器上其他实例网段冲突;无特殊诉求也可省略整个 networks 块交由默认网段。
  • image:固定 6.8 标签,升级人工触发。
  • ports: "8130:8081":宿主 8130 映射服务默认监听口 8081(LISTEN_PORT 可改服务内监听口,一般不必动)。
  • JAVA_XMS/JAVA_XMX/JAVA_GC:JVM 堆下限 256m、上限 512m、GC 用 G1GC(镜像默认 ShenandoahGC,两种在小堆场景均可,本文实测 G1GC 生效形态)。
  • download_ngrams_for_langs: none:启动时不自动下载 ngram 模型;需要时改为 en,de 等逗号分隔语言列表(可选 en、de、es、fr、nl),见第八节。
  • tmpfs: /tmp:exec:entrypoint 在 /tmp 生成 logback.xml 与 config.properties,官方要求 /tmp 必须可写且带 exec。
  • volumes:/ngrams 存 ngram 语言模型、/fasttext 存语言识别模型,两个卷落宿主目录实现持久化。

4.2 拉取镜像

cd /opt/languagetool
docker pull meyay/languagetool:6.8

实测回显(输出节选):

6.8: Pulling from meyay/languagetool
d198e4572c85: Pull complete
c9ef8c784232: Pull complete
Digest: sha256:062924c66c649d706d60aed80af6bab207bb50a053733e7ac1ca0d8446d150f1
Status: Downloaded newer image for meyay/languagetool:6.8
docker.io/meyay/languagetool:6.8

4.3 启动服务

cd /opt/languagetool
docker compose up -d

实测回显(逐行):

 Network languagetool-68-stack_lt-net Created
 Container languagetool-6.8-server Creating
 Container languagetool-6.8-server Created
 Container languagetool-6.8-server Starting
 Container languagetool-6.8-server Started

💡 说明:网络名 languagetool-68-stack_lt-net 由「项目名 + 网络名」拼接生成,项目名中的 . 被自动去除,属正常现象。

4.4 观察启动日志(首次启动含 fastText 模型下载)

docker compose logs -f languagetool

首次启动日志实录(ANSI 色码已清除,输出节选):

INFO: Creating new LanguageTool config file.
INFO: Version Information:
  Alpine Linux v3.24.2
  openjdk 21.0.12 2026-07-21 LTS
  OpenJDK Runtime Environment Temurin-21.0.12+8 (build 21.0.12+8-LTS)
  OpenJDK 64-Bit Server VM Temurin-21.0.12+8 (build 21.0.12+8-LTS, mixed mode)
INFO: Using following LanguageTool configuration:
  fasttextBinary=/usr/bin/fasttext
  fasttextModel=/fasttext/lid.176.bin
  Downloaded 100 MB (79%)
  Downloaded 110 MB (87%)
  Downloaded 120 MB (95%)
INFO: Using JAVA_OPTS=-Xms256m -Xmx512m -XX:+UseG1GC -XX:+UseStringDeduplication
INFO: Starting Language Tool Standalone Server (or custom command)
--------------------------------------------------------------------
2026-10-27 17:18:37.792 GMT INFO  org.languagetool.server.DatabaseAccessOpenSource Not setting up database access, dbDriver is not configured
2026-10-27 17:18:37 +0000 WARNING: running in HTTP mode, consider running LanguageTool behind a reverse proxy that takes care of encryption (HTTPS)
2026-10-27 17:18:37 +0000 WARNING: running in public mode, LanguageTool API can be accessed without restrictions!
2026-10-27 17:18:37 +0000 Setting up thread pool with 10 threads
2026-10-27 17:18:38.418 GMT INFO  org.languagetool.language.identifier.DefaultLanguageIdentifier Started fastText process for language identification: Binary /usr/bin/fasttext with model @ /fasttext/lid.176.bin
2026-10-27 17:18:38.423 GMT INFO  org.languagetool.server.TextChecker A/B-Test enabled: []
2026-10-27 17:18:38 +0000 Starting LanguageTool 6.8 (build date: 1980-02-01 00:00:00 +0000, e807fcd) server on http://localhost:8081...
2026-10-27 17:18:38 +0000 Server started

日志解读(三个关键点):

  • Using JAVA_OPTS=-Xms256m -Xmx512m -XX:+UseG1GC:堆参数已按 compose 注入生效(第五节用进程级命令复核)。
  • WARNING: running in public mode:entrypoint 给服务端进程默认加了 --public(允许任意来源访问),因此访问控制必须在网络层/反代层完成,不能指望服务自身鉴权(服务无认证能力)。
  • Started fastText process ... model @ /fasttext/lid.176.bin:语言识别模型就绪,此前的 Downloaded 120 MB (95%) 是首次启动自动下载该模型的进度。

4.5 部署验证

  1. 实例状态(镜像内置健康检查):
docker compose ps

实测回显:

NAME                      STATUS                    PORTS
languagetool-6.8-server   Up 25 seconds (healthy)   0.0.0.0:8130->8081/tcp, [::]:8130->8081/tcp
  1. 版本与语言清单接口:
curl -s http://127.0.0.1:8130/v2/languages | head -c 200
# [{"name":"American English","code":"en-US","longCode":"en-US"},...(共 60 条)]
  1. 校对接口冒烟测试:
curl -s -d "language=en-US" -d "text=This are an test." http://127.0.0.1:8130/v2/check

实测回显(JSON 节选,matches 内三条规则命中):

{"software":{"name":"LanguageTool","version":"6.8",...,"premium":false,...},
 "language":{"name":"English (US)","code":"en-US","detectedLanguage":{"name":"English (US)","code":"en-US","confidence":0.561,"source":"fasttext"}},
 "matches":[{"message":"The singular demonstrative pronoun ‘this’ does not agree with the plural verb ‘are’. Did you mean “these”?","rule":{"id":"THIS_NNS",...}},
            {"message":"The verb ‘are’ is plural. Did you mean: “is”?...","rule":{"id":"PLURAL_VERB_AFTER_THIS",...}},
            {"message":"Use “a” instead of ‘an’ if the following word doesn’t start with a vowel sound...","rule":{"id":"EN_A_VS_AN",...}}],...}

版本号 "version":"6.8"、"premium":false 与规则命中同时正常,部署即算成功。

五、数据持久化说明

路径(宿主)路径(实例内)内容生成方式
/opt/languagetool/fasttext//fasttextlid.176.bin 语言识别模型(131266198 字节)首次启动 entrypoint 自动下载
/opt/languagetool/ngrams//ngramsngram 语言模型(按语言子目录,可数 GB)download_ngrams_for_langs 自动下载或手工放置(第八节)
实例可写层/languagetool/程序本体、各语言词典(jar 内)随镜像分发,不持久化,升级镜像即更新
tmpfs/tmp运行时生成的 logback.xml、config.properties每次启动重建,无需持久化

验证 fastText 模型已落盘:

ls -la /opt/languagetool/fasttext/
# -rw-r--r-- 1 783 783 131266198 Oct 28 01:18 lid.176.bin

⚠️ 注意:fastText 模型不落卷也能跑(会下到实例可写层),但重建实例后会重复下载 131MB;ngram 模型则必须落卷——数 GB 的下载不能每次重建重来。

六、JVM 内存约束(256–512MB)与验证

LanguageTool 是 JVM 服务,默认堆配置对小型服务器偏大:镜像默认 JAVA_XMX=1536m。本篇实测将堆约束在 256–512MB:

  1. compose 的 environment 中设 JAVA_XMS: 256m、JAVA_XMX: 512m(如 4.1),这是该镜像的官方参数入口,entrypoint 会将其拼入启动命令。
  2. 重建实例使配置生效:docker compose up -d --force-recreate。
  3. 从启动日志确认(见 4.4):Using JAVA_OPTS=-Xms256m -Xmx512m -XX:+UseG1GC -XX:+UseStringDeduplication。
  4. 进程级复核——镜像基于 jlink 裁剪的 JRE,不含 jps 工具(/opt/java/customjre/bin/ 下仅 java jdb jrunscript jwebserver keytool),因此用 ps 直接看进程参数:
ps -ef | grep '[H]TTPServer'

实测回显(进程参数完整照抄,UID 783 为容器内运行用户 language 在宿主上的映射):

783       323948  323872  1 09:55 ?        00:00:11 java -Xms256m -Xmx512m -XX:+UseG1GC -XX:+UseStringDeduplication -Djna.tmpdir=/tmp -Dlogback.configurationFile=/tmp/logback.xml -cp .:languagetool-server.jar org.languagetool.server.HTTPServer --port 8081 --public --allow-origin * --config /tmp/config.properties

-Xms256m -Xmx512m 即生效证明。若看到 -Xmx1536m,说明环境变量未写入或实例未重建。极端情况下若怀疑堆被其他途径覆盖,可再比对启动日志的 Using JAVA_OPTS= 行——entrypoint 每次启动都打印实际拼出的完整 JVM 参数。

⚠️ 注意:JAVA_XMX 不能低于 512m 太多——语言模块加载后常驻内存明显,实测 512m 上限 + G1GC 稳定;若启用 ngram 模型或高并发,需评估调高(代价是内存占用)。

七、配置文件挂载(可选:外置 server.properties)

compose 方式下,服务端配置由 entrypoint 根据 langtool_* 前缀环境变量自动生成(运行时产物位于 tmpfs 的 /tmp/config.properties,本次实测内容为):

fasttextBinary=/usr/bin/fasttext
fasttextModel=/fasttext/lid.176.bin

两种外置配置路径:

  1. 继续用环境变量(推荐):任意服务端配置键都能以 langtool_ 前缀注入,例如限制单请求文本长度与并发线程:
    environment:
      langtool_maxTextLength: "20000"
      langtool_maxCheckThreads: "10"

重启后 /tmp/config.properties 会多出对应键,与手工配置文件等价。全部可用键的语义见 《LanguageTool 6.8 配置参数详解》。

  1. 挂载手工配置文件:把 properties 文件放到部署目录并改变启动形态——镜像暂未直接暴露 --config 覆盖入口,需通过 JAVA_OPTS 环境变量接管完整启动参数(设了 JAVA_OPTS 后其他 JAVA_* 变量失效),适合需要完全控制命令行的场景;一般部署用方案 1 足够。

八、ngram 语言模型:卷挂载与下载

ngram 数据(1/2/3gram 的 Lucene 索引)用于激活 confusion rule(易混淆词对建议),是可选项;官方支持的语言为 en、de、es、fr、nl,数据源为 dl.languagetool.org/stable-ngram-data/,单语言包体积为 GB 量级(nl 最小、约 1GB+;en 约 6GB+)。

两种下载方式:

  1. 启动时自动下载:compose 中把 download_ngrams_for_langs: none 改为 download_ngrams_for_langs: nl,重建实例。entrypoint 检查 /ngrams 下对应语言数据缺失时从官方源下载并解压,日志出现 Downloading ngrams for nl 与进度行;已存在的语言自动跳过。
  2. 手工下载放置:服务器上直接获取 zip 并解压到 /opt/languagetool/ngrams/ 下(目录结构为 <语言代码>/1grams、2grams、3grams)。

挂载生效机制(官方镜像 README 明确、本文实测印证):entrypoint 只在 /ngrams 目录非空时才把 languageModel=/ngrams 写入 config.properties;目录为空则整个键省略。这个设计对应一个重要坑:LanguageTool 服务端在配置了 languageModel 但该目录没有对应语言数据时,会静默禁用该语言的拼写类规则并返回空 matches(镜像 issue #179 记录的行为)——所以「挂了空目录」比「不挂」更糟,保持默认(none 且空目录)反而是安全的。

💡 技术拓展:ngram 模型的「统计语言模型」路线不止 Lucene 索引一种载体——

载体原理在本方案的角色
Lucene ngram 索引按语言存 1/2/3gram 出现次数索引LanguageTool 官方形态,confusion rule 数据源
fastText 词向量子词级向量相似度仅用于语言识别(lid.176.bin),不参与纠错建议
LLM 重排大模型对建议重打分仅云端 Premium 提供,开源版无

本文自建方案实际采用官方 Lucene ngram 索引(可选)+ fastText 识别(内置)。

ngram 挂载后的实测结论(本机 nl 数据 zip 1.3GB、解压后 2.4GB 挂载):挂载使 languageModel=/ngrams 写入配置、服务正常启动,但 7 句荷兰语混淆句(如 Ik ben groter als jij.)实测仍 0 检出——confusion 规则命中依赖词对规则与文本场景,挂载本身不保证混淆检出;更关键的实测发现是 A/B 陷阱复现:仅挂 nl 数据时(languageModel=/ngrams 生效),en-US 的拼写规则被静默禁用(teh 不再检出 MORFOLOGIK_RULE_EN_US),移除 languageModel 配置后拼写检出恢复(完整对照实测与参数解释见 《LanguageTool 6.8 配置参数详解》5.4 节,API 侧验证见 《LanguageTool API 调用与运维实战(核心篇)》)。

九、等价 docker run

与 4.1 compose 等价的单命令形态(参数一一对应,便于理解 compose 各项的实际含义):

docker run -d \
  --name languagetool-6.8-server \
  --restart unless-stopped \
  --publish 8130:8081 \
  --env JAVA_XMS=256m \
  --env JAVA_XMX=512m \
  --env JAVA_GC=G1GC \
  --env LISTEN_PORT=8081 \
  --env download_ngrams_for_langs=none \
  --env MAP_UID=783 \
  --env MAP_GID=783 \
  --tmpfs /tmp:exec \
  --volume /opt/languagetool/ngrams:/ngrams \
  --volume /opt/languagetool/fasttext:/fasttext \
  meyay/languagetool:6.8

生产建议仍用 compose:参数变更可追溯、down/up 语义明确,且官方镜像 README 的加固形态(read_only: true + cap_drop: ALL + cap_add 四项能力 + 非 root user: 783:783)在 compose 中表达更清晰。加固改造时注意:非 root 运行模式下实例内无法修正挂载目录属主,需自行保证目录属主为 783。

十、常用运维命令

cd /opt/languagetool

# 状态与日志
docker compose ps                          # 实例状态(含健康检查)
docker compose logs -f languagetool        # 跟随日志(Ctrl+C 退出,不影响服务)
docker compose logs --since 10m languagetool   # 最近 10 分钟日志

# 配置变更后重建(保留数据卷)
docker compose up -d --force-recreate

# 重启与停止
docker compose restart languagetool
docker compose down                        # 移除实例与网络;ngrams/fasttext 目录保留
docker compose up -d                       # 再次拉起

# 升级版本:改 compose 中镜像标签(如 6.8 -> 新版本号)后
docker pull meyay/languagetool:6.8 && docker compose up -d --force-recreate

升级注意:数据卷中的 fastText/ngram 模型与版本无关,升级不重下;规则词典随镜像更新。回滚即把镜像标签改回旧值重建。

十一、常见问题(FAQ)

Q1:拉取 languagetool/languagetool:6.8 报 not found? 该镜像不存在(Docker Hub Registry API 返回 404,2026-10-28 查证)。官方 README 认可的发行镜像是 meyay/languagetool、erikvl87/languagetool、silviof/docker-languagetool;本文用 meyay/languagetool:6.8。

Q2:启动后 curl /v2/languages 连接被拒? 先 docker compose ps 看状态是否 healthy(首次启动含 fastText 模型下载,约需 1~3 分钟);再 docker compose logs languagetool | grep -E "Server started|ERROR" 确认 Server started 行出现。若日志停在 Downloaded xx MB,说明模型下载中,等待即可。

Q3:日志出现 running in public mode, LanguageTool API can be accessed without restrictions!,有风险吗? 这是镜像 entrypoint 默认加 --public(服务要被宿主端口映射访问就必须的),服务本身无鉴权。公网暴露时务必:防火墙限源、或置于带认证/限流的反代之后(Nginx 配置示例见 《LanguageTool 6.8 配置参数详解》5.6 节)。

Q4:挂载了 ngrams 目录但拼写检查全部失效? 见第八节的空目录陷阱:languageModel 指向无数据的目录时服务端静默禁用拼写类规则(matches 为空且无报错)。要么补齐该语言 ngram 数据,要么清空 /ngrams 让 entrypoint 不写 languageModel 键。

Q5:内存占用超预期? 核对 JAVA_XMX 是否被实际应用(第四节日志行 + ps -ef | grep '[H]TTPServer' 回显);镜像默认 1536m,未改 compose 时按 1.5GB 规划。同时注意堆外内存(语言词典 mmap、fastText 进程另计),物理内存按堆的 1.5~2 倍预留。

Q6:宿主端口 8081 被占 / 想换端口? 改 compose 的 ports 左值即可(如 "8130:8081",本文实测即 8130);LISTEN_PORT 是实例内监听口,与宿主映射无关,一般不动。

十二、参考链接

  1. 官方自建服务说明:https://dev.languagetool.org/http-server
  2. 官方仓库(README 含镜像清单):https://github.com/languagetool-org/languagetool
  3. 本篇所用发行镜像 meyayl/docker-languagetool(环境变量表与加固形态):https://github.com/meyayl/docker-languagetool
  4. 该镜像 Docker Hub:https://hub.docker.com/r/meyay/languagetool
  5. ngram 语言模型官方说明:https://dev.languagetool.org/finding-errors-using-n-gram-data
  6. ngram 空目录陷阱 issue:https://github.com/meyayl/docker-languagetool/issues/179
  7. 平台内后续阅读:《LanguageTool 介绍与选型指南》(认知与选型)、《LanguageTool 6.8 配置参数详解》(配置与反代)、《LanguageTool API 调用与运维实战(核心篇)》(参数实测与错误处理),均在本专栏内。