Repository: marchhq/march
Pull request #1011: feat: add Docker support and implement graceful server shutdown with …
Author: login: oliursahin
Labels: name: size:L
description: This PR changes 100-499 lines, ignoring generated files
Created: 2025-05-10T07:16:52Z | Merged: 2025-05-10T07:16:59Z

## **User description**
…health checks

# What did you ship?

<!-- Describe your changes here -->

**Fixes:**

- [ ] #XXX (GitHub issue number)
- [ ] MAR-XXX (Linear issue number - should be visible at the bottom of the GitHub issue description)

# Checklist:
- [ ] I have self-reviewed the code (A decent size PR without self-review might be rejected)
- [ ] I pinky swear that my codes gonna work as I have testing every possible scenario. 
- [ ] I ignored Coderabbit suggestion because it does not make any sense.
- [ ] I took Coderabbit suggestion under consideration as some of it makes sense.
- [ ] I have commented my code, particularly in hard-to-understand areas.

# OR:


- [ ] shut up and let me cook.


<!-- ELLIPSIS_HIDDEN -->

----

> [!IMPORTANT]
> Adds Docker support and implements graceful server shutdown with health checks in `index.js`.
> 
>   - **Docker Support**:
>     - Adds `Dockerfile` for multi-stage build using Bun.
>     - Exposes port 8080 and includes a health check with `HEALTHCHECK` directive.
>   - **Server Enhancements**:
>     - In `index.js`, adds handlers for `uncaughtException` and `unhandledRejection` to log errors without stopping the process.
>     - Implements graceful shutdown on `SIGTERM` and `SIGINT` signals, with a forced shutdown after 10 seconds if necessary.
>     - Adds `/health` endpoint to `app` for health checks.
>     - Adds error handling for server errors in `index.js`.
> 
> <sup>This description was created by </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=marchhq%2Fmarch&utm_source=github&utm_medium=referral)<sup> for 99f71bc64530f625a9a86a0d51b2afbd20d9aac0. You can [customize](https://app.ellipsis.dev/marchhq/settings/summaries) this summary. It will automatically update as commits are pushed.</sup>

<!-- ELLIPSIS_HIDDEN -->


___

## **CodeAnt-AI Description**
- Added robust error handling in `index.js` by introducing global handlers for uncaught exceptions and unhandled promise rejections, ensuring the process remains alive on errors.
- Refactored server initialization to be asynchronous, with explicit error logging and handling for server errors.
- Implemented a `/health` endpoint to support health checks for container orchestration and monitoring.
- Introduced graceful shutdown logic that listens for `SIGTERM` and `SIGINT`, closing the server cleanly or forcefully after 10 seconds if needed.
- Created a multi-stage Dockerfile to build and run the backend using Bun, installing dependencies, copying necessary files, exposing port 8080, and defining a health check using the new `/health` endpoint.

This PR significantly improves the backend's operational robustness by adding Docker support, health checks, and graceful shutdown procedures. These enhancements facilitate better containerization, monitoring, and reliability in production environments.


___

## **Changes walkthrough**
<table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement
</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>index.js</strong><dd><code>Enhance server robustness with error handling, health checks, and <br>graceful shutdown</code></dd></summary>
<hr>

apps/backend/index.js
<li>Added global handlers for <code>uncaughtException</code> and <code>unhandledRejection</code> to <br>log errors and keep the process running.<br> <li> Refactored server initialization to include error handling and <br>asynchronous startup.<br> <li> Implemented a <code>/health</code> endpoint for health checks.<br> <li> Added graceful shutdown logic for <code>SIGTERM</code> and <code>SIGINT</code> signals, with a <br>forced shutdown after 10 seconds if not completed.<br>


</details>
    

  </td>
  <td><a href="https://github.com/marchhq/march/pull/1011/files#diff-9f750bbadd0007c1ea772fb6cfd41d28542bcecfc990d519e4c325d2cc686bcd">+62/-8</a>&nbsp; &nbsp; </td>
</tr>                    
</table></td></tr><tr><td><strong>Configuration changes
</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>Dockerfile</strong><dd><code>Add multi-stage Dockerfile with health check and production setup</code></dd></summary>
<hr>

Dockerfile
<li>Added a multi-stage Dockerfile for building and running the backend <br>with Bun.<br> <li> Installed dependencies and copied application files in build stage.<br> <li> Exposed port 8080 and included a health check using the <code>/health</code> <br>endpoint.<br> <li> Set up production environment and defined the application start <br>command.<br>


</details>
    

  </td>
  <td><a href="https://github.com/marchhq/march/pull/1011/files#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557">+39/-0</a>&nbsp; &nbsp; </td>
</tr>                    
</table></td></tr></tr></tbody></table><details>
<summary><strong>💡 Usage Guide</strong></summary>

### Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

### Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
<pre>
<code>@codeant-ai ask: Your question here</code>
</pre>
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

### Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
<pre>
<code>@codeant-ai: review</code>
</pre>

### Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at [https://app.codeant.ai](https://app.codeant.ai). This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

</details>



Diff:
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..071e33b7
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,39 @@
+# Build stage
+FROM oven/bun:1 as builder
+
+WORKDIR /app
+
+# Copy package files from backend
+COPY apps/backend/package.json .
+COPY apps/backend/bun.lockb .
+
+# Install dependencies
+RUN bun install --frozen-lockfile
+
+# Copy the backend application
+COPY apps/backend/src ./src
+COPY apps/backend/index.js .
+
+# Production stage
+FROM oven/bun:1-slim
+
+WORKDIR /app
+
+# Copy from builder
+COPY --from=builder /app/node_modules ./node_modules
+COPY --from=builder /app/package.json .
+COPY --from=builder /app/src ./src
+COPY --from=builder /app/index.js .
+
+# Set production environment
+ENV NODE_ENV=production
+
+# Expose the port the app runs on
+EXPOSE 8080
+
+# Health check
+HEALTHCHECK --interval=30s --timeout=3s \
+  CMD curl -f http://localhost:8080/health || exit 1
+
+# Start the application
+CMD ["bun", "run", "start"]
diff --git a/apps/backend/index.js b/apps/backend/index.js
index 2d10c327..08bdc967 100644
--- a/apps/backend/index.js
+++ b/apps/backend/index.js
@@ -3,14 +3,68 @@ import { environment } from "./src/loaders/environment.loader.js";
 import { createServer } from "http";
 import { initializeWebSocket } from "./src/loaders/websocket.loader.js";
 
-(async function init () {
-    const server = createServer(app);
-    server.listen(environment.PORT, '0.0.0.0', () => {
-        console.log(`Server listening on port ${environment.PORT} on all interfaces`);
-    });
-
-    initializeWebSocket(server);
-})();
+process.on('uncaughtException', (error) => {
+    console.error('Uncaught Exception:', error);
+    // Keep the process running despite the error
+});
+
+process.on('unhandledRejection', (reason, promise) => {
+    console.error('Unhandled Rejection at:', promise, 'reason:', reason);
+    // Keep the process running despite the rejection
+});
+
+let server;
+
+async function init() {
+    try {
+        server = createServer(app);
+        
+        // Add error handler for server
+        server.on('error', (error) => {
+            console.error('Server error:', error);
+        });
+
+        await new Promise((resolve) => {
+            server.listen(environment.PORT, '0.0.0.0', () => {
+                console.log(`Server listening on port ${environment.PORT} on all interfaces`);
+                resolve();
+            });
+        });
+
+        await initializeWebSocket(server);
+
+        // Add health check endpoint
+        app.get('/health', (req, res) => {
+            res.status(200).json({ status: 'healthy' });
+        });
+
+    } catch (error) {
+        console.error('Failed to initialize server:', error);
+        // Keep the process running despite initialization errors
+    }
+}
+
+// Graceful shutdown
+function shutdown() {
+    console.log('Received shutdown signal');
+    if (server) {
+        server.close(() => {
+            console.log('Server closed');
+            process.exit(0);
+        });
+
+        // Force close after 10s
+        setTimeout(() => {
+            console.error('Could not close connections in time, forcefully shutting down');
+            process.exit(1);
+        }, 10000);
+    }
+}
+
+process.on('SIGTERM', shutdown);
+process.on('SIGINT', shutdown);
+
+init();
 
 // for webhook testing
 // import { app } from "./src/index.js";


Reviews:
user: login: ellipsis-dev[bot]
body: > [!CAUTION]
> **Changes requested  :x:**
> <details>
> <summary>Reviewed everything up to 99f71bc64530f625a9a86a0d51b2afbd20d9aac0 in <ellipsistime>2 minutes and 15 seconds</ellipsistime>. Click for details.</summary> 
> 
> - Reviewed `126` lines of code in `2` files
> - Skipped `0` files when reviewing.
> - Skipped posting `5` draft comments. View those below.
> - Modify your [settings](https://app.ellipsis.dev/marchhq/settings/code-review) and [rules](https://app.ellipsis.dev/marchhq/code-review/rules) to customize what types of comments Ellipsis leaves. And don't forget to react with :+1: or :-1: to teach Ellipsis.
> 
> <details>
> <summary>1. <code>apps/backend/index.js:27</code></summary>
> 
> - **Draft comment:** 
> Consider storing environment.PORT in a variable to avoid multiple property lookups.
> - **Reason this comment was not posted:**
> Comment was on unchanged code.
> 
> 
> </details>
> 
> <details>
> <summary>2. <code>apps/backend/index.js:36</code></summary>
> 
> - **Draft comment:** 
> Register health check endpoint before starting the server to ensure it's available immediately.
> - **Reason this comment was not posted:**
> Confidence changes required: `50%` <= threshold `50%` None
> 
> 
> </details>
> 
> <details>
> <summary>3. <code>apps/backend/index.js:37</code></summary>
> 
> - **Draft comment:** 
> Consider registering the /health endpoint before starting the server to ensure it's available immediately on startup.
> - **Reason this comment was not posted:**
> Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 20% vs. threshold = 50% While it's technically possible to move the endpoint registration earlier, Express.js will handle new route registrations even after the server is started. There's no real functional difference or performance impact. The health endpoint will be available almost immediately either way. This seems like an unnecessary micro-optimization. The comment could be correct from a best practices perspective - initializing all routes before starting the server is cleaner and more organized. However, this is a minor stylistic preference that doesn't impact functionality. Per our rules, we should avoid comments that don't clearly require a code change or that are purely informative. This comment should be removed as it suggests a minor reorganization that doesn't meaningfully improve the code and isn't strictly necessary.
> 
> 
> </details>
> 
> <details>
> <summary>4. <code>apps/backend/index.js:41</code></summary>
> 
> - **Draft comment:** 
> Swallowing initialization errors may leave the server in an inconsistent state. Consider exiting on failure or handling errors more robustly.
> - **Reason this comment was not posted:**
> Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 30% vs. threshold = 50% The code appears to be intentionally designed for resilience, with multiple error handlers (uncaughtException, unhandledRejection, server.on('error')). The choice to keep running despite errors seems deliberate. While the comment raises a valid concern about potential inconsistent states, it's not clear that this is definitely a problem rather than an intentional design choice for resilience. The code could indeed end up in an inconsistent state if critical initialization fails. The comment points out a legitimate architectural concern. However, the code shows a clear pattern of intentional error handling for resilience. Without more context about the system's requirements, we can't say this is definitely wrong. Delete the comment because it questions an intentional design choice without strong evidence that it's wrong, and appears to be more of a discussion point than a clear issue requiring change.
> 
> 
> </details>
> 
> <details>
> <summary>5. <code>apps/backend/index.js:69</code></summary>
> 
> - **Draft comment:** 
> Remove commented-out code (webhook testing) to keep the codebase clean unless it's needed for debugging.
> - **Reason this comment was not posted:**
> Confidence changes required: `33%` <= threshold `50%` None
> 
> 
> </details>
> 
> 
> Workflow ID: <workflowid>`wflow_IYWS5Nr0KxhoGrRS`</workflowid>
> </details>
> 
> <sup>You can customize </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=marchhq%2Fmarch&utm_source=github&utm_medium=referral)<sup> by changing your [verbosity settings](https://app.ellipsis.dev/marchhq/settings/code-review), reacting with :+1: or :-1:, [replying to comments](https://docs.ellipsis.dev/features/code-review#teaching-ellipsis), or adding [code review rules](https://app.ellipsis.dev/marchhq/code-review/rules).</sup>
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-10T07:19:11Z
user: login: codeant-ai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-10T07:20:28Z

Review comments:
diff_hunk: @@ -0,0 +1,39 @@
+# Build stage
+FROM oven/bun:1 as builder
+
+WORKDIR /app
+
+# Copy package files from backend
+COPY apps/backend/package.json .
+COPY apps/backend/bun.lockb .
+
+# Install dependencies
+RUN bun install --frozen-lockfile
+
+# Copy the backend application
+COPY apps/backend/src ./src
+COPY apps/backend/index.js .
+
+# Production stage
+FROM oven/bun:1-slim
path: Dockerfile
user: login: ellipsis-dev[bot]
body: Ensure `curl` is available in the production image (`oven/bun:1-slim`) or install it, as it's used for the `HEALTHCHECK`.

created_at: 2025-05-10T07:19:11Z
author_association: NONE
diff_hunk: @@ -3,14 +3,68 @@ import { environment } from "./src/loaders/environment.loader.js";
 import { createServer } from "http";
 import { initializeWebSocket } from "./src/loaders/websocket.loader.js";
 
-(async function init () {
-    const server = createServer(app);
-    server.listen(environment.PORT, '0.0.0.0', () => {
-        console.log(`Server listening on port ${environment.PORT} on all interfaces`);
-    });
-
-    initializeWebSocket(server);
-})();
+process.on('uncaughtException', (error) => {
+    console.error('Uncaught Exception:', error);
+    // Keep the process running despite the error
+});
+
+process.on('unhandledRejection', (reason, promise) => {
+    console.error('Unhandled Rejection at:', promise, 'reason:', reason);
+    // Keep the process running despite the rejection
+});
+
+let server;
+
+async function init() {
+    try {
+        server = createServer(app);
+        
+        // Add error handler for server
+        server.on('error', (error) => {
+            console.error('Server error:', error);
+        });
+
+        await new Promise((resolve) => {
+            server.listen(environment.PORT, '0.0.0.0', () => {
+                console.log(`Server listening on port ${environment.PORT} on all interfaces`);
+                resolve();
+            });
+        });
+
+        await initializeWebSocket(server);
+
+        // Add health check endpoint
+        app.get('/health', (req, res) => {
+            res.status(200).json({ status: 'healthy' });
+        });
+
+    } catch (error) {
+        console.error('Failed to initialize server:', error);
+        // Keep the process running despite initialization errors
+    }
+}
+
+// Graceful shutdown
+function shutdown() {
+    console.log('Received shutdown signal');
+    if (server) {
+        server.close(() => {
+            console.log('Server closed');
+            process.exit(0);
+        });
+
+        // Force close after 10s
+        setTimeout(() => {
path: apps/backend/index.js
user: login: ellipsis-dev[bot]
body: Store the shutdown timeout ID and clear it upon successful server closure to avoid potential race conditions.

created_at: 2025-05-10T07:19:11Z
author_association: NONE
diff_hunk: @@ -3,14 +3,68 @@ import { environment } from "./src/loaders/environment.loader.js";
 import { createServer } from "http";
 import { initializeWebSocket } from "./src/loaders/websocket.loader.js";
 
-(async function init () {
-    const server = createServer(app);
-    server.listen(environment.PORT, '0.0.0.0', () => {
-        console.log(`Server listening on port ${environment.PORT} on all interfaces`);
-    });
-
-    initializeWebSocket(server);
-})();
+process.on('uncaughtException', (error) => {
+    console.error('Uncaught Exception:', error);
+    // Keep the process running despite the error
+});
+
+process.on('unhandledRejection', (reason, promise) => {
+    console.error('Unhandled Rejection at:', promise, 'reason:', reason);
+    // Keep the process running despite the rejection
+});
+
+let server;
+
+async function init() {
+    try {
+        server = createServer(app);
+        
+        // Add error handler for server
+        server.on('error', (error) => {
+            console.error('Server error:', error);
+        });
+
+        await new Promise((resolve) => {
+            server.listen(environment.PORT, '0.0.0.0', () => {
+                console.log(`Server listening on port ${environment.PORT} on all interfaces`);
+                resolve();
+            });
+        });
+
+        await initializeWebSocket(server);
+
+        // Add health check endpoint
+        app.get('/health', (req, res) => {
+            res.status(200).json({ status: 'healthy' });
+        });
+
+    } catch (error) {
+        console.error('Failed to initialize server:', error);
+        // Keep the process running despite initialization errors
+    }
+}
+
+// Graceful shutdown
+function shutdown() {
+    console.log('Received shutdown signal');
+    if (server) {
+        server.close(() => {
+            console.log('Server closed');
+            process.exit(0);
+        });
+
+        // Force close after 10s
+        setTimeout(() => {
+            console.error('Could not close connections in time, forcefully shutting down');
+            process.exit(1);
+        }, 10000);
+    }
+}
+
+process.on('SIGTERM', shutdown);
+process.on('SIGINT', shutdown);
+
+init();
path: apps/backend/index.js
user: login: codeant-ai[bot]
body: **Suggestion:** Update global error handlers to invoke the graceful shutdown routine and improve the shutdown logic with a guard flag and forced shutdown timer clearance, and register the health check endpoint before starting the server. [Error Handling & Shutdown]
```suggestion
import { createServer } from "http";
import { initializeWebSocket } from "./src/loaders/websocket.loader.js";

process.on('uncaughtException', (error) => {
    console.error('Uncaught Exception:', error);
    shutdown();
});

process.on('unhandledRejection', (reason, promise) => {
    console.error('Unhandled Rejection at:', promise, 'reason:', reason);
    shutdown();
});

let server;
let isShuttingDown = false;

async function init() {
    try {
        server = createServer(app);

        // Add error handler for server
        server.on('error', (error) => {
            console.error('Server error:', error);
        });

        // Register health check endpoint early
        app.get('/health', (req, res) => {
            res.status(200).json({ status: 'healthy' });
        });

        await new Promise((resolve) => {
            server.listen(environment.PORT, '0.0.0.0', () => {
                console.log(`Server listening on port ${environment.PORT} on all interfaces`);
                resolve();
            });
        });

        await initializeWebSocket(server);
    } catch (error) {
        console.error('Failed to initialize server:', error);
        // Keep the process running despite initialization errors
    }
}

// Graceful shutdown with guard flag and timer clearance
function shutdown() {
    if (isShuttingDown) return;
    isShuttingDown = true;
    console.log('Received shutdown signal');
    if (server) {
        const shutdownTimer = setTimeout(() => {
            console.error('Could not close connections in time, forcefully shutting down');
            process.exit(1);
        }, 10000);
        server.close(() => {
            clearTimeout(shutdownTimer);
            console.log('Server closed');
            process.exit(0);
        });
    }
}

process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);

init();
```
created_at: 2025-05-10T07:20:28Z
author_association: NONE
diff_hunk: @@ -0,0 +1,39 @@
+# Build stage
+FROM oven/bun:1 as builder
+
+WORKDIR /app
+
+# Copy package files from backend
+COPY apps/backend/package.json .
+COPY apps/backend/bun.lockb .
+
+# Install dependencies
+RUN bun install --frozen-lockfile
+
+# Copy the backend application
+COPY apps/backend/src ./src
+COPY apps/backend/index.js .
+
+# Production stage
+FROM oven/bun:1-slim
+
+WORKDIR /app
+
+# Copy from builder
+COPY --from=builder /app/node_modules ./node_modules
+COPY --from=builder /app/package.json .
+COPY --from=builder /app/src ./src
+COPY --from=builder /app/index.js .
+
+# Set production environment
+ENV NODE_ENV=production
+
+# Expose the port the app runs on
+EXPOSE 8080
+
+# Health check
+HEALTHCHECK --interval=30s --timeout=3s \
+  CMD curl -f http://localhost:8080/health || exit 1
+
+# Start the application
+CMD ["bun", "run", "start"]
path: Dockerfile
user: login: codeant-ai[bot]
body: **Suggestion:** Optimize the Dockerfile by setting the production environment in the builder stage, defining the PORT variable, installing curl, and switching to a non-root user in the production stage. [Docker Optimization & Security]
```suggestion
# Build stage
FROM oven/bun:1 as builder

WORKDIR /app

# Copy package files from backend
COPY apps/backend/package.json .
COPY apps/backend/bun.lockb .

# Set production environment for dependency installation
ENV NODE_ENV=production

# Install dependencies
RUN bun install --frozen-lockfile

# Copy the backend application
COPY apps/backend/src ./src
COPY apps/backend/index.js .

# Production stage
FROM oven/bun:1-slim

WORKDIR /app

# Copy from builder
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json .
COPY --from=builder /app/src ./src
COPY --from=builder /app/index.js .

# Install curl and create a non-root user, then switch to it for enhanced security
RUN apt-get update && apt-get install -y curl && adduser -D appuser
USER appuser

# Set production environment and define PORT explicitly for consistency
ENV NODE_ENV=production
ENV PORT=8080

# Expose the port the app runs on
EXPOSE 8080

# Health check
HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:8080/health || exit 1

# Start the application
CMD ["bun", "run", "start"]
```
created_at: 2025-05-10T07:20:28Z
author_association: NONE

PR comments:
user: login: codeant-ai[bot]
created_at: 2025-05-10T07:16:56Z
body: CodeAnt AI is reviewing your PR.
author_association: NONE
user: login: march-dokploy-2025-05-10[bot]
created_at: 2025-05-10T07:16:56Z
body: ### Dokploy Preview Deployment


| Name       | Status       | Preview                             | Updated (UTC)         |
|------------|--------------|-------------------------------------|-----------------------|
| web  | ✅ Done | [Preview URL](http://preview-march-web-d6tbhu-f4v97o-18-139-223-166.traefik.me) | 2025-05-10T07:21:47.011Z |

author_association: NONE
user: login: codeant-ai[bot]
created_at: 2025-05-10T07:18:19Z
body: ## Pull Request Feedback 🔍

<table>
<tr><td>🔒&nbsp;<strong>No security issues identified</strong></td></tr>
<tr><td>⚡&nbsp;<strong>Recommended areas for review</strong><br><br>

<a href='https://github.com/marchhq/march/pull/1011/files#diff-9f750bbadd0007c1ea772fb6cfd41d28542bcecfc990d519e4c325d2cc686bcdR20-R39'><strong>Undefined Variable</strong></a><br>The variable `app` is referenced in `createServer(app)` and `app.get('/health', ...)` but is not defined or imported. Please ensure that `app` is properly declared or imported to avoid runtime errors.

<a href='https://github.com/marchhq/march/pull/1011/files#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557R34-R36'><strong>Dependency</strong></a><br>The HEALTHCHECK command uses `curl` to check the health endpoint. Verify that `curl` is available in the `oven/bun:1-slim` image or include its installation, so that the health check functions as intended.

</td></tr>
</table>

author_association: NONE
user: login: codeant-ai[bot]
created_at: 2025-05-10T07:20:42Z
body: CodeAnt AI finished reviewing your PR.
author_association: NONE