LanguageTool 6.8 Docker Compose 部署实战
在 Ubuntu 22.04 服务器用 Docker Compose 落地 LanguageTool 6.8 实测:meyay/languagetool:6.8 镜像选型、JVM 堆压至 512MB 的 ps 验证、fastText/ngram 数据卷持久化、部署验证与常见问题排障实录。
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/languagetool | 6.8 / latest(6.8-13) | 2026-10-11 | 288 MB | 直接从官方仓库 tag 构建;内置 fastText 与按需 ngram 下载器;支持只读根文件系统与非 root 运行 |
erikvl87/languagetool | 6.8 | 2026-06-15 | 432 MB | 环境变量丰富,文档成熟 |
silviof/docker-languagetool | — | 长期未更 | — | 久未跟进新版本 |
选型依据与关键事实:
- 不存在
languagetool/languagetool官方镜像。用 Registry API 查询hub.docker.com/v2/repositories/languagetool/languagetool/tags/返回404 {"message":"object not found"}——网上教程常见这个写法,照抄会拉取失败。官方 README 指向的三个社区镜像才是可用发行物。 - v6.6 之后官方不再发独立 ZIP 包(官方 CHANGES.md 记载,社区镜像 README 引用),新版本落地主流路径即 Docker 镜像。
- 本文实测选择
meyay/languagetool:6.8:跟进官方版本最快(官方 v6.8 于 2026-05-05 发布,镜像 6.8-13 于 2026-10-11 更新)、体积最小、entrypoint 由 Go 编写(单文件、无 shell 依赖),并且是唯一默认内置 fastText 二进制的发行物。 - 该镜像把 JVM 堆参数暴露为
JAVA_XMS(默认 256m)/JAVA_XMX(默认 1536m)环境变量,本文将堆上限压到 512m(JVM 内存约束见第六节与《LanguageTool 配置参数详解》篇)。
镜像固定标签优先于 latest:升级由人工触发,避免服务在无人感知时被重建。
三、前置条件
- 服务器已安装 Docker Engine 与 Docker Compose 插件,当前用户在
docker组(或以 root 执行):
docker --version && docker compose version
# Docker version 27.5.1, build 9f9e854
# Docker Compose version v5.6.1
- 出网能力检查(拉取镜像走 Docker Hub;首次启动还要从 languagetool.org 下载 fastText 模型约 131MB)。内网环境可给 Docker 配置 HTTP 代理;本文实测环境直接可达,未发生下载降级。
- 端口规划:本篇使用 8130(
ss -ltn确认未被占用):
ss -ltn | grep 8130 || echo "8130 空闲"
# 8130 空闲
- 创建部署目录:
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 部署验证
- 实例状态(镜像内置健康检查):
docker compose ps
实测回显:
NAME STATUS PORTS
languagetool-6.8-server Up 25 seconds (healthy) 0.0.0.0:8130->8081/tcp, [::]:8130->8081/tcp
- 版本与语言清单接口:
curl -s http://127.0.0.1:8130/v2/languages | head -c 200
# [{"name":"American English","code":"en-US","longCode":"en-US"},...(共 60 条)]
- 校对接口冒烟测试:
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/ | /fasttext | lid.176.bin 语言识别模型(131266198 字节) | 首次启动 entrypoint 自动下载 |
/opt/languagetool/ngrams/ | /ngrams | ngram 语言模型(按语言子目录,可数 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:
- compose 的
environment中设JAVA_XMS: 256m、JAVA_XMX: 512m(如 4.1),这是该镜像的官方参数入口,entrypoint 会将其拼入启动命令。 - 重建实例使配置生效:
docker compose up -d --force-recreate。 - 从启动日志确认(见 4.4):
Using JAVA_OPTS=-Xms256m -Xmx512m -XX:+UseG1GC -XX:+UseStringDeduplication。 - 进程级复核——镜像基于 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
两种外置配置路径:
- 继续用环境变量(推荐):任意服务端配置键都能以
langtool_前缀注入,例如限制单请求文本长度与并发线程:
environment:
langtool_maxTextLength: "20000"
langtool_maxCheckThreads: "10"
重启后 /tmp/config.properties 会多出对应键,与手工配置文件等价。全部可用键的语义见 《LanguageTool 6.8 配置参数详解》。
- 挂载手工配置文件:把 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+)。
两种下载方式:
- 启动时自动下载:compose 中把
download_ngrams_for_langs: none改为download_ngrams_for_langs: nl,重建实例。entrypoint 检查/ngrams下对应语言数据缺失时从官方源下载并解压,日志出现Downloading ngrams for nl与进度行;已存在的语言自动跳过。 - 手工下载放置:服务器上直接获取 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 是实例内监听口,与宿主映射无关,一般不动。
十二、参考链接
- 官方自建服务说明:https://dev.languagetool.org/http-server
- 官方仓库(README 含镜像清单):https://github.com/languagetool-org/languagetool
- 本篇所用发行镜像 meyayl/docker-languagetool(环境变量表与加固形态):https://github.com/meyayl/docker-languagetool
- 该镜像 Docker Hub:https://hub.docker.com/r/meyay/languagetool
- ngram 语言模型官方说明:https://dev.languagetool.org/finding-errors-using-n-gram-data
- ngram 空目录陷阱 issue:https://github.com/meyayl/docker-languagetool/issues/179
- 平台内后续阅读:《LanguageTool 介绍与选型指南》(认知与选型)、《LanguageTool 6.8 配置参数详解》(配置与反代)、《LanguageTool API 调用与运维实战(核心篇)》(参数实测与错误处理),均在本专栏内。