Self-host LanguageTool in Docker Compose
- Add erikvl87/languagetool service on the internal network (port 8010, English only by default — change langsToLoad to add more) - 256 MB min / 512 MB max heap; healthcheck waits up to ~3 min for first startup while Java initialises - Server LANGUAGETOOL_URL hardwired to http://languagetool:8010/v2 — no character limits, no rate limits, fully private - Remove 40 000-char cap from lint route (not needed self-hosted) - Bump fetch timeout to 30 s for cold-start requests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
55375d2ff0
commit
bdafd3f2c3
@ -11,6 +11,7 @@ services:
|
|||||||
- OLLAMA_URL=${OLLAMA_URL:-https://ai.binarygnome.com}
|
- OLLAMA_URL=${OLLAMA_URL:-https://ai.binarygnome.com}
|
||||||
- OLLAMA_MODEL=${OLLAMA_MODEL:-gemma4:latest}
|
- OLLAMA_MODEL=${OLLAMA_MODEL:-gemma4:latest}
|
||||||
- OLLAMA_API_KEY=${OLLAMA_API_KEY:-}
|
- OLLAMA_API_KEY=${OLLAMA_API_KEY:-}
|
||||||
|
- LANGUAGETOOL_URL=http://languagetool:8010/v2
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "host-gateway:host-gateway"
|
- "host-gateway:host-gateway"
|
||||||
networks:
|
networks:
|
||||||
@ -22,6 +23,23 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
start_period: 15s
|
start_period: 15s
|
||||||
|
|
||||||
|
languagetool:
|
||||||
|
image: erikvl87/languagetool
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- Java_Xms=256m
|
||||||
|
- Java_Xmx=512m
|
||||||
|
# Add more languages (comma-separated) or remove this line to load all
|
||||||
|
- langsToLoad=en
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -qO- http://localhost:8010/v2/languages > /dev/null"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 18 # LT takes ~30–60 s to start on first run
|
||||||
|
start_period: 90s
|
||||||
|
|
||||||
app:
|
app:
|
||||||
build: ./frontend
|
build: ./frontend
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
@ -11,7 +11,6 @@ router.use(auth)
|
|||||||
router.post('/check', async (req, res) => {
|
router.post('/check', async (req, res) => {
|
||||||
const { text, language = 'en-US' } = req.body || {}
|
const { text, language = 'en-US' } = req.body || {}
|
||||||
if (!text || typeof text !== 'string') return res.status(400).json({ error: 'text required' })
|
if (!text || typeof text !== 'string') return res.status(400).json({ error: 'text required' })
|
||||||
if (text.length > 40000) return res.status(400).json({ error: 'text too long (max 40 000 chars)' })
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const body = new URLSearchParams({
|
const body = new URLSearchParams({
|
||||||
@ -25,7 +24,7 @@ router.post('/check', async (req, res) => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json' },
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json' },
|
||||||
body: body.toString(),
|
body: body.toString(),
|
||||||
signal: AbortSignal.timeout(20000),
|
signal: AbortSignal.timeout(30000),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!r.ok) {
|
if (!r.ok) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user