Merge pull request #345 from opastorello/main

feat(docker): add Docker/Compose setup for CCS dashboard
This commit is contained in:
Kai (Tam Nhu) Tran
2026-01-18 08:20:00 -05:00
committed by GitHub
6 changed files with 426 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
# Git and GitHub
.git
.github
.husky
# Dependencies
node_modules
ui/node_modules
# Build outputs (rebuilt in container)
dist
ui/dist
ui/.vite
# Test and coverage
coverage
ui/coverage
tests
**/__tests__
# Environment files (SECURITY: prevent secret leakage)
.env
.env.*
.env.local
.env.production
.env.development
# Documentation (not needed for runtime)
docs
*.md
!README.md
!LICENSE
# Logs and OS files
*.log
.DS_Store
Thumbs.db
# IDE and editor
.vscode
.idea
*.swp
*.swo
# Misc
*.tgz
*.tar.gz
+2
View File
@@ -53,6 +53,8 @@ ccs config
# Opens http://localhost:3000
```
Want to run the dashboard in Docker? See `docker/README.md`.
### 3. Configure Your Accounts
The dashboard provides visual management for all account types:
+85
View File
@@ -0,0 +1,85 @@
# syntax=docker/dockerfile:1
# =============================================================================
# Build stage: compile TypeScript and build UI
# =============================================================================
FROM node:20-bookworm-slim AS build
SHELL ["/bin/bash", "-lc"]
# Pin bun version for reproducible builds
ARG BUN_VERSION=1.2.21
ENV BUN_INSTALL=/usr/local/bun
ENV PATH="$BUN_INSTALL/bin:$PATH"
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl ca-certificates unzip \
&& rm -rf /var/lib/apt/lists/*
# Install specific bun version
RUN curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}"
WORKDIR /app
# Dependency install layer (avoid running install scripts inside the image build)
COPY package.json bun.lock bunfig.toml ./
COPY ui/package.json ui/bun.lock ./ui/
RUN bun install --frozen-lockfile --ignore-scripts \
&& (cd ui && bun install --frozen-lockfile --ignore-scripts)
COPY . .
RUN bun run build:all
# Validate build artifacts exist
RUN test -d dist && test -d lib && echo "[OK] Build artifacts validated"
# =============================================================================
# Runtime stage: minimal production image
# =============================================================================
FROM node:20-bookworm-slim AS runtime
SHELL ["/bin/bash", "-lc"]
# Pin bun version for reproducible builds
ARG BUN_VERSION=1.2.21
ENV BUN_INSTALL=/usr/local/bun
ENV PATH="$BUN_INSTALL/bin:/home/node/.opencode/bin:$PATH"
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl ca-certificates unzip \
&& rm -rf /var/lib/apt/lists/*
# Install specific bun version
RUN curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}"
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile --production --ignore-scripts
COPY docker/entrypoint.sh /usr/local/bin/ccs-entrypoint
RUN chmod +x /usr/local/bin/ccs-entrypoint
COPY --from=build /app/dist ./dist
COPY --from=build /app/lib ./lib
COPY --from=build /app/config ./config
COPY --from=build /app/scripts ./scripts
COPY --from=build /app/README.md ./README.md
COPY --from=build /app/LICENSE ./LICENSE
# Install AI CLI tools (using latest - pin versions in production if needed)
# These are optional tools for docker exec usage
RUN npm install -g @google/gemini-cli @vibe-kit/grok-cli @anthropic-ai/claude-code \
&& npm install -g @kaitranntt/ccs --force \
&& npm cache clean --force \
&& su -s /bin/bash node -c 'curl -fsSL https://opencode.ai/install | bash -s -- --no-modify-path' \
&& ln -sf /app/dist/ccs.js /usr/local/bin/ccs
ENV CCS_PORT=3000
EXPOSE 3000 8317
ENTRYPOINT ["/usr/local/bin/ccs-entrypoint"]
CMD ["bash", "-c", "node dist/ccs.js config --port ${CCS_PORT}"]
+205
View File
@@ -0,0 +1,205 @@
<div align="center">
# CCS Dashboard - Docker
![CCS Logo](../assets/ccs-logo-medium.png)
### Run the CCS Config Dashboard in Docker.
Persistent config, restart on reboot.
**[Back to README](../README.md)**
</div>
<br>
## Quick Start (Docker Run)
```bash
docker build -f docker/Dockerfile -t ccs-dashboard:latest .
docker run -d \
--name ccs-dashboard \
--restart unless-stopped \
-p 3000:3000 \
-p 8317:8317 \
-e CCS_PORT=3000 \
-v ccs_home:/home/node/.ccs \
ccs-dashboard:latest
```
Open `http://localhost:3000` (Dashboard).
CCS also starts CLIProxy on `http://localhost:8317` (used by Dashboard features and OAuth providers).
## Environment Variables
Common CCS environment variables (from the docs):
- Docs: [Environment variables](https://docs.ccs.kaitran.ca/getting-started/configuration#environment-variables)
- `CCS_CONFIG`: override config file path
- `CCS_UNIFIED_CONFIG=1`: force unified YAML config loader
- `CCS_MIGRATE=1`: trigger config migration
- `CCS_SKIP_MIGRATION=1`: skip migrations
- `CCS_DEBUG=1`: enable verbose logs
- `NO_COLOR=1`: disable ANSI colors
- `CCS_SKIP_PREFLIGHT=1`: skip API key validation checks
- `CCS_WEBSEARCH_SKIP=1`: skip WebSearch hook integration
- Proxy: `CCS_PROXY_HOST`, `CCS_PROXY_PORT`, `CCS_PROXY_PROTOCOL`, `CCS_PROXY_AUTH_TOKEN`, `CCS_PROXY_TIMEOUT`, `CCS_PROXY_FALLBACK_ENABLED`, `CCS_ALLOW_SELF_SIGNED`
Example (passing env vars to the running container):
```bash
docker run -d \
--name ccs-dashboard \
--restart unless-stopped \
-p 3000:3000 \
-p 8317:8317 \
-e CCS_PORT=3000 \
-e CCS_DEBUG=1 \
-e NO_COLOR=1 \
-e CCS_PROXY_HOST="proxy.example.com" \
-e CCS_PROXY_PORT=443 \
-e CCS_PROXY_PROTOCOL="https" \
-v ccs_home:/home/node/.ccs \
ccs-dashboard:latest
```
## Useful Commands
```bash
docker logs -f ccs-dashboard
docker stop ccs-dashboard
docker start ccs-dashboard
docker rm -f ccs-dashboard
```
## Docker Compose (Optional)
Using the included `docker/docker-compose.yml`:
```bash
docker-compose -f docker/docker-compose.yml up --build -d
docker-compose -f docker/docker-compose.yml logs -f
```
Stop:
```bash
docker-compose -f docker/docker-compose.yml down
```
## Persistence
- CCS stores data in `/home/node/.ccs` inside the container.
- The examples use a named volume (`ccs_home`) to persist that data.
- Compose also persists `/home/node/.claude`, `/home/node/.opencode`, and `/home/node/.grok-cli` via named volumes.
## Resource Limits
For production deployments, limit container resources:
```bash
docker run -d \
--name ccs-dashboard \
--restart unless-stopped \
--memory=1g \
--cpus=2 \
-p 3000:3000 \
-p 8317:8317 \
-v ccs_home:/home/node/.ccs \
ccs-dashboard:latest
```
Docker Compose includes default limits (1GB RAM, 2 CPUs). Adjust in `docker-compose.yml` under `deploy.resources`.
## Graceful Shutdown
CCS handles `SIGTERM` gracefully. When stopping the container:
```bash
docker stop ccs-dashboard # Sends SIGTERM, waits 10s, then SIGKILL
docker stop -t 30 ccs-dashboard # Wait 30s for graceful shutdown
```
The `init: true` in docker-compose.yml ensures proper signal forwarding.
## Troubleshooting
### Permission Errors (EACCES)
If you see permission errors on startup:
```bash
# Check volume permissions
docker exec ccs-dashboard ls -la /home/node/.ccs
# Fix by recreating volumes
docker-compose down -v
docker-compose up -d
```
### Port Already in Use
```bash
# Check what's using the port
lsof -i :3000
lsof -i :8317
# Use different ports
docker run -p 4000:3000 -p 9317:8317 ...
# Or with compose
CCS_DASHBOARD_PORT=4000 CCS_CLIPROXY_PORT=9317 docker-compose up -d
```
### Container Keeps Restarting
```bash
# Check logs for errors
docker logs ccs-dashboard --tail 50
# Check container health
docker inspect ccs-dashboard --format='{{.State.Health.Status}}'
```
### Debug Mode
Enable verbose logging:
```bash
docker run -e CCS_DEBUG=1 ...
```
## Examples: Claude + Gemini inside Docker
Open a shell inside the running container:
```bash
docker exec -it ccs-dashboard bash
```
Claude (non-interactive / print mode):
```bash
docker exec -it ccs-dashboard claude -p "Hello from Docker"
```
Gemini (one-shot prompt):
```bash
docker exec -it ccs-dashboard gemini "Hello from Docker"
```
If you need to configure credentials, do it according to each CLI's docs:
```bash
docker exec -it ccs-dashboard claude --help
docker exec -it ccs-dashboard gemini --help
```
## Security Notes
- **Secrets**: For sensitive values like `CCS_PROXY_AUTH_TOKEN`, consider using Docker secrets or a `.env` file (not committed to git).
- **Network**: The container exposes ports 3000 and 8317. In production, use a reverse proxy (nginx, traefik) with TLS.
- **Updates**: Regularly rebuild the image to get security patches: `docker-compose build --pull`
+53
View File
@@ -0,0 +1,53 @@
# CCS Dashboard Docker Compose
# See docker/README.md for documentation
services:
ccs-dashboard:
image: ccs-dashboard:latest
build:
context: ..
dockerfile: docker/Dockerfile
restart: unless-stopped
init: true
ports:
- "${CCS_DASHBOARD_PORT:-3000}:3000"
- "${CCS_CLIPROXY_PORT:-8317}:8317"
environment:
CCS_PORT: 3000
CCS_DEBUG: "${CCS_DEBUG:-}"
NO_COLOR: "${NO_COLOR:-}"
CCS_SKIP_PREFLIGHT: "${CCS_SKIP_PREFLIGHT:-}"
CCS_WEBSEARCH_SKIP: "${CCS_WEBSEARCH_SKIP:-}"
CCS_PROXY_HOST: "${CCS_PROXY_HOST:-}"
CCS_PROXY_PORT: "${CCS_PROXY_PORT:-}"
CCS_PROXY_PROTOCOL: "${CCS_PROXY_PROTOCOL:-}"
CCS_PROXY_AUTH_TOKEN: "${CCS_PROXY_AUTH_TOKEN:-}"
CCS_PROXY_TIMEOUT: "${CCS_PROXY_TIMEOUT:-}"
CCS_PROXY_FALLBACK_ENABLED: "${CCS_PROXY_FALLBACK_ENABLED:-}"
CCS_ALLOW_SELF_SIGNED: "${CCS_ALLOW_SELF_SIGNED:-}"
volumes:
- ccs_home:/home/node/.ccs
- claude_home:/home/node/.claude
- opencode_home:/home/node/.opencode
- grok_home:/home/node/.grok-cli
# Healthcheck uses internal ports (3000/8317) which are fixed
healthcheck:
test: ["CMD-SHELL", "curl -fsS --max-time 2 http://localhost:3000/ >/dev/null && curl -sS --max-time 2 http://127.0.0.1:8317/ >/dev/null"]
interval: 10s
timeout: 3s
retries: 12
start_period: 30s
# Resource limits (adjust based on workload)
deploy:
resources:
limits:
memory: 1G
cpus: '2'
reservations:
memory: 256M
cpus: '0.5'
volumes:
ccs_home:
claude_home:
opencode_home:
grok_home:
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
set -euo pipefail
ccs_home_dir="${CCS_HOME_DIR:-/home/node/.ccs}"
mkdir -p "$ccs_home_dir"
# Fix volume permissions if running as root
if [ "$(id -u)" = "0" ]; then
if ! chown -R node:node "$ccs_home_dir" 2>/dev/null; then
echo "[!] Warning: Could not change ownership of $ccs_home_dir (read-only volume?)" >&2
fi
fi
# Show usage if no command provided
if [ "$#" -eq 0 ]; then
echo "[X] No command provided" >&2
echo "" >&2
echo "Usage: docker run ccs-dashboard <command>" >&2
echo "" >&2
echo "Examples:" >&2
echo " docker run ccs-dashboard node dist/ccs.js config" >&2
echo " docker run ccs-dashboard ccs --help" >&2
echo "" >&2
exit 1
fi
# Drop privileges from root to node user
if [ "$(id -u)" = "0" ]; then
cmd="$(printf '%q ' "$@")"
exec su -s /bin/bash node -c "exec ${cmd}"
fi
exec "$@"