Skip to content

fix(web): add dedicated health route to prevent memory leak (#2344)#2582

Open
themillhauz wants to merge 2 commits intokarakeep-app:mainfrom
themillhauz:fix/health-endpoint-memory-leak
Open

fix(web): add dedicated health route to prevent memory leak (#2344)#2582
themillhauz wants to merge 2 commits intokarakeep-app:mainfrom
themillhauz:fix/health-endpoint-memory-leak

Conversation

@themillhauz
Copy link
Copy Markdown

@themillhauz themillhauz commented Mar 11, 2026

fixes #2344

Problem

The /api/health endpoint was handled by the Hono catch-all route in
apps/web/app/api/[[...route]]/route.ts, which runs the nextAuth
middleware on every request. This middleware calls
createContextFromRequest()next-auth init(), which allocates ~14
new closure objects wrapping all DrizzleAdapter methods per request.

With healthchecks running every 30–60 seconds (Docker built-in, Uptime
Kuma, Pangolin, Blackbox Exporter, etc.), these objects accumulate in the
V8 old generation heap faster than GC can collect them, causing gradual
memory growth of ~3–17 MB/hour depending on healthcheck interval.

This is the root cause of the memory leak reported in #2344. Several
users confirmed that disabling healthchecks stopped the memory growth.

Fix

Add a dedicated Next.js route at apps/web/app/api/health/route.ts.
Next.js static routes take priority over [[...route]] catch-all routes,
so healthcheck requests no longer trigger auth context initialization.

The response format is preserved: {"status":"ok","message":"..."}

Testing

Verified locally: GET /api/health returns 200 OK without any
auth/session calls appearing in logs.

The /api/health endpoint was handled by the Hono catch-all route in
apps/web/app/api/[[...route]]/route.ts, which runs the nextAuth middleware
on every request. This middleware calls createContextFromRequest() which
calls next-auth init(), allocating ~14 closure objects per request wrapping
all DrizzleAdapter methods.

With healthchecks running every 30-60 seconds (Docker, Uptime Kuma, etc.),
these objects accumulate in the V8 old generation heap faster than GC can
collect them, causing gradual memory growth of ~3-17 MB/hour depending on
healthcheck interval. This is the root cause of the memory leak reported
in issue karakeep-app#2344.

Fix: add a dedicated Next.js route at apps/web/app/api/health/route.ts.
Next.js static routes take priority over [[...route]] catch-all routes,
so healthcheck requests no longer trigger auth context initialization.

The response format is preserved: {status: 'ok', message: '...'}

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 11, 2026

Walkthrough

Adds a new unauthenticated health check API route at apps/web/app/api/health/route.ts that exports runtime = "nodejs", dynamic = "force-dynamic", and a GET() handler returning JSON { status: "ok", message: "Web app is working" }.

Changes

Cohort / File(s) Summary
Health Check Endpoint
apps/web/app/api/health/route.ts
New file: exports runtime and dynamic settings and a GET() handler that returns a lightweight JSON health status; bypasses auth initialization.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a dedicated health route to fix a memory leak issue, matching the changeset content.
Description check ✅ Passed The PR description clearly relates to the changeset, explaining the problem (memory leak from frequent healthchecks triggering auth middleware) and how the dedicated health route at apps/web/app/api/health/route.ts solves it.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Mar 11, 2026

Greptile Summary

This PR adds a dedicated Next.js route at apps/web/app/api/health/route.ts to handle GET /api/health requests, preventing them from being caught by the Hono [[...route]] catch-all which unconditionally runs the nextAuth middleware (and thus createContextFromRequest() / next-auth init()) on every request. This is a minimal, well-targeted fix for the gradual memory leak reported in #2344.

  • The new route short-circuits the Hono middleware stack for health requests, since Next.js static routes take priority over catch-all routes.
  • The JSON response body ({"status":"ok","message":"Web app is working"}) is identical to the existing Hono handler in packages/api/routes/health.ts, preserving full backwards compatibility.
  • export const runtime = "nodejs" is consistent with the catch-all route.
  • The existing Hono health route (packages/api/routes/health.ts) is now effectively shadowed for the web app; it remains in the codebase but is no longer reachable via the web server. This is harmless and can be cleaned up separately if desired.

Confidence Score: 5/5

  • This PR is safe to merge — it is a minimal, targeted fix with no functional regressions.
  • The change is a single new file with 8 lines. The response format is verified to match the existing Hono health handler exactly, Next.js routing priority over catch-all routes is well-documented behaviour, and the fix directly addresses the root cause described in the issue.
  • No files require special attention.

Important Files Changed

Filename Overview
apps/web/app/api/health/route.ts New dedicated Next.js health route that bypasses the Hono catch-all and its nextAuth middleware, fixing the memory leak from #2344. Response format matches the existing Hono health handler exactly.

Last reviewed commit: 9f3db0e

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/web/app/api/health/route.ts (1)

4-7: Make the health route explicitly dynamic.

This handler is deterministic, so it is worth pinning it with export const dynamic = "force-dynamic" to keep /api/health from ever being prerendered or served as a cached "ok" response if Cache Components or routing defaults change. That is an inference from the current Next.js route-handler caching behavior. (nextjs.org)

♻️ Suggested change
 export const runtime = "nodejs";
+export const dynamic = "force-dynamic";
 
 export function GET() {
   return Response.json({ status: "ok", message: "Web app is working" });
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/api/health/route.ts` around lines 4 - 7, Add an explicit dynamic
export to prevent prerendering: in the route.ts where runtime is exported and
GET is defined (export const runtime = "nodejs"; and function GET()), add export
const dynamic = "force-dynamic" so the /api/health handler is always dynamic and
never cached/prerendered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/web/app/api/health/route.ts`:
- Around line 4-7: Add an explicit dynamic export to prevent prerendering: in
the route.ts where runtime is exported and GET is defined (export const runtime
= "nodejs"; and function GET()), add export const dynamic = "force-dynamic" so
the /api/health handler is always dynamic and never cached/prerendered.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 96f7601a-7200-4754-9994-3e5d9fbcc26c

📥 Commits

Reviewing files that changed from the base of the PR and between 467c732 and 9f3db0e.

📒 Files selected for processing (1)
  • apps/web/app/api/health/route.ts

Per CodeRabbit review: without explicit dynamic export, Next.js may
prerender or cache the /api/health response as a static page. Adding
export const dynamic = 'force-dynamic' ensures the handler is always
evaluated at runtime.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Memory leak in 0.30 (with Pangolin) - RAM usage continuously growing over time

1 participant