Turborepo has become the standard for JavaScript/TypeScript monorepos. A typical Turbo monorepo might have 3-5 deployable services: a web app, an API, an admin panel, a marketing site, and a docs site. Each needs uptime monitoring — but they share a single repo. Here's the recommended approach.
The Monorepo Monitoring Challenge
In a Turbo monorepo, you have multiple deployable apps that can fail independently:
apps/
web/ # Main Next.js app → app.yourproduct.com
api/ # Express/Hono API → api.yourproduct.com
admin/ # Internal admin panel → admin.yourproduct.com
docs/ # Docs site (Astro) → docs.yourproduct.com
packages/
ui/
config/
db/
If api/ is down but web/ is up, users see your site but all interactions fail. You need individual monitoring for each deployable service.
Step 1: Add Health Check Endpoints to Each App
apps/api/ (Express, Hono, or Fastify)
app.get('/health', async (req, res) => {
res.json({
status: 'ok',
service: 'api',
timestamp: new Date().toISOString(),
})
})
apps/web/ (Next.js)
// apps/web/app/api/health/route.ts
import { NextResponse } from 'next/server'
export async function GET() {
return NextResponse.json({
status: 'ok',
service: 'web',
timestamp: new Date().toISOString(),
})
}
apps/docs/ (Astro)
// apps/docs/src/pages/api/health.ts
import type { APIRoute } from 'astro'
export const GET: APIRoute = async () => {
return new Response(JSON.stringify({ status: 'ok', service: 'docs' }), {
headers: { 'Content-Type': 'application/json' },
})
}
Step 2: Add a Shared Health Package (Optional but Clean)
In a monorepo, you can create a shared health check package:
// packages/health/src/index.ts
export function createHealthHandler(service: string) {
return async () => ({
status: 'ok' as const,
service,
timestamp: new Date().toISOString(),
})
}
Then use it in each app:
import { createHealthHandler } from '@repo/health'
const handler = createHealthHandler('api')
app.get('/health', async (req, res) => res.json(await handler()))
Step 3: Set Up Vigilmon Monitors
Create one Vigilmon monitor per deployable app at vigilmon.online:
| App | Monitor URL | Check |
|---|---|---|
| Web app | https://app.yourproduct.com/api/health |
HTTP 200 |
| API | https://api.yourproduct.com/health |
HTTP 200 |
| Admin | https://admin.yourproduct.com/api/health |
HTTP 200 |
| Docs | https://docs.yourproduct.com/api/health |
HTTP 200 |
For each monitor:
- Add Monitor → HTTP
- Enter health check URL
- Expected status: 200
- Keyword:
"status":"ok" - Multi-region: enabled
Step 4: Turbo Build Failure Detection with Heartbeats
Use Vigilmon's heartbeat monitoring to detect broken Turbo builds:
# .github/workflows/deploy.yml
- name: Deploy apps
run: turbo run deploy
- name: Verify health endpoints
run: |
for url in \n "https://app.yourproduct.com/api/health" \n "https://api.yourproduct.com/health" \n "https://docs.yourproduct.com/api/health"; do
status=$(curl -s -o /dev/null -w "%{http_code}" "$url")
if [ "$status" != "200" ]; then
echo "Health check failed for $url (status: $status)"
exit 1
fi
done
- name: Ping Vigilmon heartbeat
run: curl -s https://vigilmon.online/api/heartbeat/YOUR_HEARTBEAT_ID
If a day passes without a successful deploy, Vigilmon alerts you.
Status Page for Your Monorepo
Create a Vigilmon status page grouping all your services:
- Web App
- API
- Documentation
- Admin Panel
This gives users and internal teams visibility into which service is having issues. Vigilmon's public status pages are free and easy to set up.
Common Turbo Monorepo Failure Patterns
| Failure | Detected By |
|---|---|
| Single app deployment failed | That app's health monitor |
| Shared package broke all apps | All monitors alert simultaneously |
| API down but web up | API monitor alerts, web monitor passes |
| Build pipeline broken | Heartbeat goes cold |
| SSL cert expired on one domain | SSL monitor alerts for that domain |
Why Independent Monitors Matter
In a monorepo, a single broken deployment or shared package can cascade. Having per-service monitors means:
- You immediately know which service failed
- You can quickly determine if it's isolated (one app) or systemic (shared package)
- Your on-call rotation knows exactly where to look
Top comments (0)