Monitoring Your Self-Hosted Eclipse Theia IDE with Vigilmon
Your Eclipse Theia IDE deployment went down. Developers can't open files. The terminal backend crashed silently. You found out when your team started complaining in chat.
Eclipse Theia is a cloud IDE framework used by companies to host browser-based development environments at scale. Running it self-hosted on port 3000 means you're responsible for keeping LSP services, WebSocket connections, file system access, and extension loading all healthy simultaneously.
By the end of this guide you'll have:
- HTTP monitoring for the Theia web server
- LSP service health checks (typescript-language-server, pylsp)
- WebSocket connection stability monitoring
- File system and terminal backend health
- Git integration service monitoring
- Slack alerts and a public status page
Why monitoring matters for Eclipse Theia
Theia is a Node.js/TypeScript application but it orchestrates many backend services that can fail independently:
- LSP crashes — code completion and diagnostics stop working, but the IDE UI still loads
- WebSocket instability — the editor frontend disconnects repeatedly, making the IDE unusable
- Terminal backend failure — integrated terminals stop spawning, forcing users to SSH separately
- Search indexing failures — workspace search returns incomplete or stale results
- Extension loading errors — plugins silently fail to activate, removing key functionality
Without monitoring each layer, you're flying blind.
Step 1: Add a health check endpoint to Theia
Theia doesn't ship with a built-in /health endpoint, so add one via a lightweight Express sidecar or a Theia backend plugin.
Option A: Express health sidecar
// theia-health.js
const express = require('express')
const { execSync } = require('child_process')
const http = require('http')
const app = express()
function checkTheia() {
return new Promise((resolve) => {
const req = http.get('http://localhost:3000/', { timeout: 3000 }, (res) => {
resolve(res.statusCode === 200 ? 'up' : 'degraded')
})
req.on('error', () => resolve('down'))
req.on('timeout', () => { req.destroy(); resolve('down') })
})
}
function checkLsp(processName) {
try {
const out = execSync(`pgrep -f ${processName}`, { timeout: 2000 }).toString()
return out.trim().length > 0 ? 'up' : 'down'
} catch {
return 'down'
}
}
app.get('/health', async (req, res) => {
const checks = {
theia_web: await checkTheia(),
typescript_lsp: checkLsp('typescript-language-server'),
python_lsp: checkLsp('pylsp'),
git_service: checkLsp('git'),
}
const healthy = Object.values(checks).every(v => v === 'up')
res.status(healthy ? 200 : 503).json({
status: healthy ? 'ok' : 'degraded',
checks,
})
})
app.listen(3003, () => console.log('Theia health on :3003'))
Start it:
node theia-health.js &
Verify:
curl http://localhost:3003/health
# {"status":"ok","checks":{"theia_web":"up","typescript_lsp":"up","python_lsp":"up","git_service":"up"}}
Option B: Theia backend plugin
If you control the Theia distribution, register a health route in a backend plugin:
// packages/health/src/node/health-backend-module.ts
import { ContainerModule } from 'inversify'
import { BackendApplicationContribution } from '@theia/core/lib/node'
import { HealthContribution } from './health-contribution'
export default new ContainerModule(bind => {
bind(BackendApplicationContribution).to(HealthContribution).inSingletonScope()
})
// packages/health/src/node/health-contribution.ts
import { injectable } from 'inversify'
import { BackendApplicationContribution } from '@theia/core/lib/node'
import * as express from 'express'
@injectable()
export class HealthContribution implements BackendApplicationContribution {
configure(app: express.Application): void {
app.get('/api/health', (req, res) => {
res.json({ status: 'ok', timestamp: Date.now() })
})
}
}
Step 2: Monitor the Theia web server
Sign up at vigilmon.online (free, no card required).
Web server monitor:
- Click New Monitor → HTTP
- URL:
https://theia.yourdomain.com - Expected status:
200 - Check interval: 1 minute
Composite health check:
- Click New Monitor → HTTP
- URL:
https://theia.yourdomain.com:3003/health - Expected status:
200 - Keyword alert:
"status":"ok"
Add a response time alert at 3000ms — if Theia's initial page load takes over 3 seconds, the developer experience is already degraded.
Step 3: Monitor LSP service health
Language Server Protocol services are the heart of IDE functionality. They're separate OS processes that Theia spawns on demand and can crash independently.
The health sidecar already checks for running LSP processes. You can extend it to actually test LSP responsiveness:
// Add to theia-health.js
const net = require('net')
function checkLspSocket(port) {
return new Promise((resolve) => {
const socket = new net.Socket()
socket.setTimeout(2000)
socket.on('connect', () => { socket.destroy(); resolve('up') })
socket.on('timeout', () => { socket.destroy(); resolve('down') })
socket.on('error', () => resolve('down'))
socket.connect(port, '127.0.0.1')
})
}
// typescript-language-server typically listens on a stdio pipe, not a TCP port
// Check the process is alive and responding to signals
function checkLspHealth(processName) {
try {
const pid = execSync(`pgrep -f ${processName}`).toString().trim()
if (!pid) return 'down'
// Send signal 0 to check process is alive
process.kill(parseInt(pid), 0)
return 'up'
} catch {
return 'down'
}
}
In Vigilmon, set a keyword alert on "typescript_lsp":"down" or "python_lsp":"down" to get notified when specific LSP services fail.
Step 4: Monitor WebSocket connection stability
Theia uses WebSockets for all real-time communication between the browser client and backend. Disconnections cause the editor to go into a reconnecting state, which is disruptive for active work.
Create a WebSocket probe:
// ws-probe.js
const WebSocket = require('ws')
const ws = new WebSocket('wss://theia.yourdomain.com', {
headers: { 'Origin': 'https://theia.yourdomain.com' }
})
let connected = false
const timeout = setTimeout(() => {
if (!connected) {
console.error('WebSocket connection timeout')
process.exit(1)
}
}, 5000)
ws.on('open', () => {
connected = true
clearTimeout(timeout)
ws.close()
process.exit(0)
})
ws.on('error', (err) => {
clearTimeout(timeout)
console.error('WebSocket error:', err.message)
process.exit(1)
})
Run it as a cron heartbeat:
# /etc/cron.d/theia-ws-probe
*/3 * * * * node /opt/theia/ws-probe.js && curl -fsS "https://vigilmon.online/heartbeat/YOUR_HEARTBEAT_ID"
In Vigilmon, create a Heartbeat Monitor with a 6-minute expected interval (two missed checks = alert).
Step 5: Monitor the file system backend
The Theia file system backend (@theia/filesystem) handles all file reads and writes. If it becomes unresponsive, the editor loses the ability to open or save files.
Add a file system check to the health sidecar:
const fs = require('fs').promises
async function checkFilesystem(workspacePath) {
try {
const testFile = `${workspacePath}/.theia-health-probe`
await fs.writeFile(testFile, Date.now().toString())
await fs.readFile(testFile, 'utf8')
await fs.unlink(testFile)
return 'up'
} catch {
return 'down'
}
}
// Add to health endpoint
checks.filesystem = await checkFilesystem(process.env.THEIA_WORKSPACE || '/workspace')
This write/read/delete cycle confirms the workspace filesystem is actually writable, not just mounted read-only or full.
Step 6: Monitor the terminal backend and search indexing
Terminal backend:
The Theia terminal backend (@theia/terminal) spawns shell processes via node-pty. Check that shells can be spawned:
const { spawn } = require('child_process')
function checkTerminalBackend() {
return new Promise((resolve) => {
const proc = spawn('echo', ['health-ok'], { timeout: 2000 })
let output = ''
proc.stdout.on('data', d => output += d)
proc.on('close', (code) => {
resolve(code === 0 && output.includes('health-ok') ? 'up' : 'down')
})
proc.on('error', () => resolve('down'))
})
}
Search indexing:
Theia uses ripgrep for workspace search. Confirm the rg binary is available and functional:
function checkSearchIndexing() {
try {
execSync('rg --version', { timeout: 2000 })
return 'up'
} catch {
return 'down'
}
}
Step 7: Slack alerts and status page
Slack alerts:
- Go to Notifications → New Channel → Slack
- Paste your Slack incoming webhook URL
- Enable on all Theia monitors
When the TypeScript LSP crashes:
🔴 DEGRADED: theia.yourdomain.com:3003/health
Keyword mismatch: "typescript_lsp":"down" detected
Region: EU-Central
Status page:
- Status Pages → New Status Page
- Add all monitors
- Name it "Theia IDE Status"
- Share with your development team
What you've built
| What | How | |------|-----| | Web server availability | Vigilmon HTTP → port 3000 | | LSP service health | Health sidecar process checks + Vigilmon keyword alerts | | WebSocket stability | Heartbeat from ws-probe cron | | File system backend | Write/read/delete probe in health sidecar | | Terminal backend | Shell spawn probe in health sidecar | | Search indexing | ripgrep binary check in health sidecar | | Composite health | Vigilmon HTTP → port 3003 | | Slack alerts | Vigilmon Slack notification channel | | Status page | Vigilmon public status page |
Next steps
- Monitor response time trends on the web server endpoint — Theia's startup time grows with large workspaces
- Add SSL certificate expiry monitoring for your Theia domain
- Set up separate heartbeat monitors for each language server you support at scale
Get started free at vigilmon.online.