Skip to content

Conversation

@FarisHijazi
Copy link

@FarisHijazi FarisHijazi commented Nov 24, 2025

Add production-ready Docker deployment with the following features:

  • Multi-stage Dockerfile: Optimized build with separate builder and production stages
  • Docker Compose: Full orchestration with Traefik integration for automatic routing
  • Security: Runs as non-root user (node:node) with minimal runtime dependencies
  • Health Checks: Built-in health monitoring every 30 seconds
  • Volume Persistence: Separate volumes for data and configuration
  • Network Isolation: Dedicated network with Traefik integration via public network
  • LinuxServer.io Standards: Follows container best practices for production deployment

Configuration:

  • Internal port: 3001
  • External port: 3002 (configurable via EXTERNAL_PORT env var)
  • Traefik labels for automatic routing to claudecodeui.fhijazi.com
  • WebSocket support for real-time communication
  • Read-only mount of ~/.claude for project discovery

Build and deployment tested successfully with health checks passing.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Added containerization with optimized multi-stage image builds for smaller, production-ready artifacts.
    • Added orchestration configuration for easy local and production deployment with networks, persistent volumes, environment-driven settings, and health checks.
    • Added build-context ignore rules to reduce image size and exclude development artifacts.

✏️ Tip: You can customize this high-level summary in your review settings.

Add production-ready Docker deployment with the following features:

- **Multi-stage Dockerfile**: Optimized build with separate builder and production stages
- **Docker Compose**: Full orchestration with Traefik integration for automatic routing
- **Security**: Runs as non-root user (node:node) with minimal runtime dependencies
- **Health Checks**: Built-in health monitoring every 30 seconds
- **Volume Persistence**: Separate volumes for data and configuration
- **Network Isolation**: Dedicated network with Traefik integration via public network
- **LinuxServer.io Standards**: Follows container best practices for production deployment

Configuration:
- Internal port: 3001
- External port: 3002 (configurable via EXTERNAL_PORT env var)
- Traefik labels for automatic routing to claudecodeui.fhijazi.com
- WebSocket support for real-time communication
- Read-only mount of ~/.claude for project discovery

Build and deployment tested successfully with health checks passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 24, 2025

Walkthrough

Adds Docker containerization: a .dockerignore to reduce build context, a multi-stage Dockerfile for building and running a Node.js (Alpine) app, and a docker-compose.yml defining the service, networks, volumes, environment, and a healthcheck.

Changes

Cohort / File(s) Summary
Containerization files
\.dockerignore, Dockerfile, docker-compose.yml
Introduces Docker support: ignore patterns to shrink build context; a multi-stage Node.js (Alpine) Dockerfile with separate builder and production stages, non-root runtime user, env vars, ports, and created data/config dirs; and a docker-compose service with build, env_file, volumes (host binds + named volumes), networks, restart policy, and an HTTP healthcheck.

Sequence Diagram(s)

sequenceDiagram
  participant Dev as Developer / CI
  participant Compose as docker-compose
  participant Builder as Docker build (builder stage)
  participant Prod as Docker build (production stage) / Container
  Dev->>Compose: docker-compose build & up
  Compose->>Builder: build builder stage (npm ci, build assets)
  Builder-->>Compose: built artifacts (dist, server, public, index.html)
  Compose->>Prod: assemble runtime image, install prod deps, copy artifacts
  Prod-->>Compose: runnable image
  Compose->>Prod: start container (node server/index.js)
  Prod-->>Dev: healthcheck /api/health -> 200 (service ready)
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Verify the /api/health endpoint exists and returns 200.
  • Confirm environment variable names and defaults match application expectations.
  • Check volume host paths and permissions for /data and /config and non-root node user usage.
  • Ensure the Dockerfile copies the correct built artifacts (dist, server, public, index.html) between stages.
  • Validate docker-compose env_file usage and any host bind paths referenced.

Poem

🐰
I hopped through builds with nimble feet,
Bundled ignores to keep things neat,
Built in stages, small and spry,
Volumes mounted, healthchecks fly,
Containers ready — time to greet!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: add Docker deployment support' directly and accurately reflects the main objective of the changeset, which adds Docker deployment artifacts including Dockerfile, docker-compose.yml, and .dockerignore configuration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link
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.

Actionable comments posted: 4

🧹 Nitpick comments (1)
docker-compose.yml (1)

38-43: Consider removing the redundant healthcheck from docker-compose.yml.

The healthcheck is already defined in the Dockerfile (lines 68–69) and will be inherited by the container. Defining it again in docker-compose.yml is redundant. If you need to override the Dockerfile's healthcheck, keep it here; otherwise, remove it to reduce duplication.

Apply this diff to remove the redundant healthcheck:

    networks:
      - claudecodeui
      - public
-   healthcheck:
-     test: ["CMD", "node", "-e", "require('http').get('http://localhost:3001/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"]
-     interval: 30s
-     timeout: 10s
-     retries: 3
-     start_period: 40s
    labels:
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a72a26 and cf5c5b7.

📒 Files selected for processing (3)
  • .dockerignore (1 hunks)
  • Dockerfile (1 hunks)
  • docker-compose.yml (1 hunks)
🔇 Additional comments (1)
Dockerfile (1)

68-69: Verify that the application exposes an /api/health health check endpoint.

The Dockerfile defines a health check that calls http://localhost:3001/api/health. Confirm that the application implements this endpoint and returns HTTP 200 for healthy status. If the endpoint is missing or returns a different status code, the container health check will fail.

start_period: 40s
labels:
- "traefik.enable=true"
- "traefik.http.routers.claudecodeui.rule=Host(`claudecodeui.fhijazi.com`)"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Make the Traefik host domain configurable instead of hardcoding the author's domain.

The hardcoded domain claudecodeui.fhijazi.com is specific to the PR author and will not work for other deployments. Users will need to manually edit the docker-compose.yml file to change the domain, which is error-prone and not maintainable. Use an environment variable with a sensible default (or no default) to allow users to configure their own domain.

Apply this diff to make the domain configurable:

- - "traefik.http.routers.claudecodeui.rule=Host(`claudecodeui.fhijazi.com`)"
+ - "traefik.http.routers.claudecodeui.rule=Host(`${DOMAIN:-localhost}`)"
  - "traefik.http.routers.claudecodeui.entrypoints=web"
  - "traefik.http.services.claudecodeui.loadbalancer.server.port=3001"
  - "traefik.http.routers.claudecodeui.middlewares=common-headers@file"
  # WebSocket support
- - "traefik.http.routers.claudecodeui-ws.rule=Host(`claudecodeui.fhijazi.com`) && PathPrefix(`/ws`)"
+ - "traefik.http.routers.claudecodeui-ws.rule=Host(`${DOMAIN:-localhost}`) && PathPrefix(`/ws`)"

Then document in a README or deployment guide that users should set the DOMAIN environment variable to their actual domain.

Also applies to: 51-51

🤖 Prompt for AI Agents
In docker-compose.yml around line 46 (and similarly line 51), the Traefik host
rule is hardcoded to claudecodeui.fhijazi.com; change it to use an environment
variable (e.g. ${DOMAIN}) so deployments can supply their own domain, optionally
providing a sensible default like ${DOMAIN:-example.com} or leaving no default
to force explicit configuration, and update README/deployment docs instructing
users to set DOMAIN to their actual domain before running docker-compose.

labels:
- "traefik.enable=true"
- "traefik.http.routers.claudecodeui.rule=Host(`claudecodeui.fhijazi.com`)"
- "traefik.http.routers.claudecodeui.entrypoints=web"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add HTTPS/TLS configuration for production security.

The Traefik configuration only uses the web entrypoint (HTTP). For production deployments, HTTPS with TLS is essential. Add the websecure entrypoint and TLS configuration. If TLS is intentionally handled externally (e.g., by an upstream reverse proxy), this should be documented.

Apply this diff to add HTTPS support (adjust based on your Traefik TLS configuration):

  - "traefik.http.routers.claudecodeui.rule=Host(`${DOMAIN:-localhost}`)"
  - "traefik.http.routers.claudecodeui.entrypoints=web"
+ - "traefik.http.routers.claudecodeui.entrypoints=websecure"
+ - "traefik.http.routers.claudecodeui.tls=true"
  - "traefik.http.services.claudecodeui.loadbalancer.server.port=3001"
  - "traefik.http.routers.claudecodeui.middlewares=common-headers@file"
  # WebSocket support
  - "traefik.http.routers.claudecodeui-ws.rule=Host(`${DOMAIN:-localhost}`) && PathPrefix(`/ws`)"
  - "traefik.http.routers.claudecodeui-ws.entrypoints=websecure"
+ - "traefik.http.routers.claudecodeui-ws.tls=true"

Also applies to: 52-52

🤖 Prompt for AI Agents
In docker-compose.yml around lines 47 and 52, the Traefik labels only expose the
HTTP entrypoint ("web") which leaves production traffic unencrypted; update the
service labels to also reference the "websecure" entrypoint and enable TLS by
adding the appropriate traefik.http.routers.<name>.entrypoints=websecure and
traefik.http.routers.<name>.tls=true labels and configure a cert resolver (e.g.,
traefik.http.routers.<name>.tls.certresolver=le) or, if TLS is managed
externally, add a clear comment in the compose file documenting that choice;
ensure the router names match existing labels and that your Traefik static
configuration defines the websecure entrypoint and the named cert resolver.

Comment on lines +31 to +37
RUN apk add --no-cache \
python3 \
make \
g++ \
git \
ca-certificates \
tzdata
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Remove unnecessary build dependencies from production stage.

The production stage includes build tools (python3, make, g++, git) that are not needed at runtime. These only increase the image size. Keep only runtime dependencies: ca-certificates and tzdata.

Apply this diff to remove build dependencies:

  # Install runtime dependencies
  RUN apk add --no-cache \
-     python3 \
-     make \
-     g++ \
-     git \
-     ca-certificates \
+     ca-certificates \
      tzdata
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
RUN apk add --no-cache \
python3 \
make \
g++ \
git \
ca-certificates \
tzdata
RUN apk add --no-cache \
ca-certificates \
tzdata
🤖 Prompt for AI Agents
In Dockerfile around lines 31 to 37, the production stage is installing
build-time packages (python3, make, g++, git) that aren't required at runtime;
remove those packages and only install the runtime dependencies ca-certificates
and tzdata to reduce image size. Edit the RUN apk add --no-cache line to drop
python3, make, g++, and git so it only installs ca-certificates and tzdata, and
verify there are no subsequent runtime steps that expect the removed tools.

Copy link
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.

Actionable comments posted: 0

♻️ Duplicate comments (1)
Dockerfile (1)

31-37: Remove build dependencies from production stage to reduce image bloat.

The production stage installs python3, make, g++, and git, which are build-time tools and not needed at runtime. These packages unnecessarily inflate the image size and increase the attack surface. Keep only the runtime dependencies: ca-certificates and tzdata.

  # Install runtime dependencies
  RUN apk add --no-cache \
-     python3 \
-     make \
-     g++ \
-     git \
      ca-certificates \
      tzdata

Note: This resolves the blocker from the previous review.

🧹 Nitpick comments (1)
.dockerignore (1)

8-9: Remove duplicate .github/ entry.

Line 9 and line 39 both exclude .github/, which is redundant. Keep one exclusion (either in the Development or CI/CD section) and remove the duplicate.

  # Development
  .git/
- .github/
  .vscode/
  .idea/

  # CI/CD
  .github/

Also applies to: 38-39

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf5c5b7 and 556e6c9.

📒 Files selected for processing (3)
  • .dockerignore (1 hunks)
  • Dockerfile (1 hunks)
  • docker-compose.yml (1 hunks)
🔇 Additional comments (7)
Dockerfile (2)

1-24: Multi-stage build and builder stage are well-structured.

The builder stage correctly isolates build dependencies, and the separation prevents unnecessary artifacts from leaking into production.


52-68: Security and runtime setup is sound.

Non-root user execution (line 57), proper permission delegation (lines 53–54), and environment variable configuration (lines 60–62) follow production best practices. The healthcheck-friendly CMD is appropriate for orchestration.

docker-compose.yml (5)

16-18: Good practice: env_file marked as required.

Marking the env file as required (line 18) ensures the composition fails fast if the file is missing, preventing silent misconfiguration.


14-15: Port and environment configuration is flexible and well-structured.

External port is configurable via EXTERNAL_PORT env var with a sensible default (3002). Environment variables for runtime configuration (PORT, DATABASE_PATH, CONTEXT_WINDOW, CLAUDE_CLI_PATH) follow the twelve-factor app pattern.

Also applies to: 19-24


37-42: Healthcheck implementation is solid.

The healthcheck queries /api/health every 30 seconds with reasonable timeouts (10s) and retries (3). The 40-second start period allows the service time to initialize before health checks begin.


1-49: Code snippet appears truncated; Traefik labels from previous reviews are not visible.

Past review comments reference Traefik labels and hardcoded domain (claudecodeui.fhijazi.com) and HTTPS/TLS configuration issues at lines around 46–51, but the provided code snippet ends at line 49. If Traefik labels exist beyond the visible range, please verify that:

  1. The domain is configurable (not hardcoded to claudecodeui.fhijazi.com)
  2. HTTPS/TLS is configured (not HTTP-only)

If you do not intend to use Traefik, clarify the routing/reverse-proxy strategy in documentation.


31-31: The original review comment is incorrect—.claude mount must remain read-write.

The application in server/projects.js requires write access to ~/.claude/project-config.json to store manually added projects (line 35: "Stored in ~/.claude/project-config.json with 'manuallyAdded' flag"). The :rw flag on line 31 is necessary for this functionality. Changing it to :ro would break the feature. There is no evidence in the PR that a read-only mount was intended.

Likely an incorrect or invalid review comment.

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.

1 participant