Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

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

# Build outputs
dist/
build/

# Environment files
.env.local
.env.development
.env.test

# Testing
coverage/
.nyc_output/

# Misc
*.log
*.swp
*.swo
*~
.DS_Store
Thumbs.db

# Documentation
*.md
!README.md

# CI/CD
.github/
.gitlab-ci.yml
.travis.yml

# Docker
Dockerfile*
docker-compose*
.dockerignore
72 changes: 72 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
FROM node:20-alpine AS builder

# Install build dependencies
RUN apk add --no-cache \
python3 \
make \
g++ \
git

WORKDIR /app

# Copy package files
COPY package*.json ./

# Install all dependencies (including dev dependencies for building)
RUN npm ci && \
npm cache clean --force

# Copy source code
COPY . .

# Build frontend
RUN npm run build

# =============================================================================
# Production stage
# =============================================================================
FROM node:20-alpine

# Install runtime dependencies
RUN apk add --no-cache \
python3 \
make \
g++ \
git \
ca-certificates \
tzdata
Comment on lines +31 to +37
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.


WORKDIR /app

# Copy package files and install production dependencies
COPY package*.json ./
RUN npm ci --only=production && \
npm cache clean --force

# Copy built files from builder
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/server ./server
COPY --from=builder /app/public ./public
COPY --from=builder /app/index.html ./index.html

# Create necessary directories with proper permissions
RUN mkdir -p /data /config && \
chown -R node:node /app /data /config

# Switch to node user (uid/gid 1000)
USER node

# Environment variables
ENV NODE_ENV=production \
PORT=3001 \
DATABASE_PATH=/data/auth.db

# Expose port
EXPOSE 3001

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD node -e "require('http').get('http://localhost:3001/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"

# Start server
CMD ["node", "server/index.js"]
59 changes: 59 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
networks:
claudecodeui:
name: claudecodeui
public:
external: true

services:
claudecodeui:
container_name: claudecodeui
build:
context: .
dockerfile: Dockerfile
image: claudecodeui:latest
restart: unless-stopped
ports:
- "${EXTERNAL_PORT:-3002}:3001"
env_file:
- path: ./.env
required: true
environment:
- PORT=${PORT:-3001}
- DATABASE_PATH=${DATABASE_PATH:-/data/auth.db}
- CONTEXT_WINDOW=${CONTEXT_WINDOW:-160000}
- CLAUDE_CLI_PATH=${CLAUDE_CLI_PATH:-claude}
- NODE_ENV=production
volumes:
# Data persistence
- claudecodeui-data:/data
- claudecodeui-config:/config
# Mount user's .claude directory for project access (read-only)
- ${HOME}/.claude:/home/node/.claude:ro
# Mount user's home for project access (optional, configure as needed)
# Uncomment and adjust if you need access to specific project directories
# - ${HOME}/projects:/projects:rw
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:
- "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.

- "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.

- "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.entrypoints=web"
- "traefik.http.services.claudecodeui-ws.loadbalancer.server.port=3001"

volumes:
claudecodeui-data:
name: claudecodeui_data
claudecodeui-config:
name: claudecodeui_config