Last updated: Aug 12, 2025, 01:18 PM UTC

Claude CLI Docker Integration Success Guide

Generated: 2025-08-10 UTC
Purpose: Guide for successfully integrating Claude CLI in Docker/Sliplane environments
Status: Complete Implementation


Objective

Successfully integrate Claude CLI into a Dockerized application deployed on Sliplane, ensuring proper workspace management and environment configuration.

Prerequisites

  • Docker application with Node.js 20-alpine base image
  • Sliplane deployment account
  • Claude CLI package (@anthropic-ai/claude-code)
  • SSH access to Sliplane containers

Implementation Steps

Step 1: Dockerfile Configuration

# Install Claude CLI globally and set up properly
RUN npm install -g @anthropic-ai/claude-code@latest && \
    ln -sf $(npm root -g)/@anthropic-ai/claude-code/cli.js /usr/local/bin/claude && \
    chmod +x /usr/local/bin/claude && \
    claude --version || echo "Claude CLI installation verification failed"

# Create Claude config directory and symlink projects to workspaces
RUN mkdir -p /home/nodejs/.claude && \
    ln -s /app/workspaces /home/nodejs/.claude/projects && \
    chown -R nodejs:nodejs /home/nodejs/.claude

# Environment variables
ENV RUNNING_IN_DOCKER=true \
    USE_DOCKER_WORKSPACE=true \
    WORKSPACES_PATH=/app/workspaces \
    CLAUDE_PROJECTS_PATH=/app/workspaces \
    CLAUDE_HOME=/home/nodejs/.claude

Step 2: Docker Entrypoint Script

Create docker-entrypoint.sh:

#!/bin/bash

# Ensure environment variables are set
export RUNNING_IN_DOCKER="${RUNNING_IN_DOCKER:-true}"
export USE_DOCKER_WORKSPACE="${USE_DOCKER_WORKSPACE:-true}"
export WORKSPACES_PATH="${WORKSPACES_PATH:-/app/workspaces}"
export CLAUDE_PROJECTS_PATH="${CLAUDE_PROJECTS_PATH:-/app/workspaces}"
export CLAUDE_HOME="${CLAUDE_HOME:-/home/nodejs/.claude}"

# Create necessary directories
mkdir -p "$WORKSPACES_PATH"
mkdir -p "$CLAUDE_HOME"

# Create symlink so Claude CLI uses our workspace directory
if [ -d "$CLAUDE_HOME/projects" ] && [ ! -L "$CLAUDE_HOME/projects" ]; then
    if [ "$(ls -A $CLAUDE_HOME/projects 2>/dev/null)" ]; then
        cp -r $CLAUDE_HOME/projects/* "$WORKSPACES_PATH/" 2>/dev/null || true
    fi
    rm -rf "$CLAUDE_HOME/projects"
fi

if [ ! -e "$CLAUDE_HOME/projects" ]; then
    ln -s "$WORKSPACES_PATH" "$CLAUDE_HOME/projects"
    echo "βœ… Symlink created: $CLAUDE_HOME/projects -> $WORKSPACES_PATH"
fi

# Execute main command
exec "$@"

Step 3: Code Updates

workspace-manager.js

export async function setupOrganizationWorkspace(userId, organizationData, projectId = null) {
  // Use WORKSPACES_PATH if in Docker, otherwise use HOME directory
  const baseWorkspacePath = process.env.WORKSPACES_PATH || path.join(process.env.HOME, '.claude/projects');
  
  let workspacePath;
  if (projectId && organizationData.searchTerm) {
    const sanitizedName = organizationData.searchTerm.toLowerCase()
      .replace(/[^a-z0-9]/g, '-')
      .replace(/-+/g, '-')
      .replace(/^-|-$/g, '')
      .substring(0, 30);
    workspacePath = path.join(baseWorkspacePath, `${sanitizedName}-${projectId}`);
  } else {
    workspacePath = path.join(baseWorkspacePath, 'default-workspace');
  }
  // ... rest of function
}

claude-cli.js

// Simplified Docker detection - rely on environment variable
const isDockerEnv = process.env.RUNNING_IN_DOCKER === 'true';

// Handle workspace paths
let workingDir = cwd || process.cwd();
if (!workingDir.startsWith('/')) {
  if (process.env.WORKSPACES_PATH) {
    workingDir = path.join(process.env.WORKSPACES_PATH, workingDir);
  } else {
    workingDir = path.resolve(workingDir);
  }
}

Step 4: Verification Scripts

Create scripts/verify-claude-deployment.sh:

#!/bin/bash

echo "πŸ” Claude CLI Deployment Verification"

# Check environment variables
check_env_var() {
    local var_name=$1
    local var_value="${!var_name}"
    
    if [ -n "$var_value" ]; then
        echo "βœ… $var_name: $var_value"
    else
        echo "❌ $var_name: Not set"
    fi
}

check_env_var "RUNNING_IN_DOCKER"
check_env_var "WORKSPACES_PATH"
check_env_var "CLAUDE_PROJECTS_PATH"

# Check Claude CLI
if command -v claude &> /dev/null; then
    echo "βœ… Claude CLI found at: $(which claude)"
    claude --version
else
    echo "❌ Claude CLI not found"
fi

# Check symlink
if [ -L "/home/nodejs/.claude/projects" ]; then
    echo "βœ… Symlink exists: $(readlink /home/nodejs/.claude/projects)"
else
    echo "❌ Symlink missing"
fi

Key Issues Solved

1. Multiple Workspace Directories

Problem: Claude CLI created projects in /home/nodejs/.claude/projects/ while app used /app/workspaces/

Solution: Created symlink from Claude's project directory to app workspace directory

2. Environment Detection

Problem: Complex multi-layered Docker detection logic

Solution: Simplified to single RUNNING_IN_DOCKER=true check

3. Path Resolution

Problem: Inconsistent path resolution between services

Solution: Standardized on WORKSPACES_PATH environment variable

Verification Checklist

  • Build Docker image with updated Dockerfile
  • Deploy to Sliplane
  • SSH into container: ssh -p 22222 service_xxx@server.sliplane.app
  • Run verification script: ./scripts/verify-claude-deployment.sh
  • Check symlink: ls -la /home/nodejs/.claude/ | grep projects
  • Test Claude CLI: claude --version
  • Verify single workspace: ls -la /app/workspaces/

Final Architecture

/app/workspaces/                     # Main workspace directory
β”œβ”€β”€ workspace/                       # Default workspace
β”‚   β”œβ”€β”€ CLAUDE.md                   # Project configuration
β”‚   └── docs/                       # Documentation structure
└── [other-projects]/               # Additional workspaces

/home/nodejs/.claude/
β”œβ”€β”€ projects -> /app/workspaces     # Symlink to workspaces
β”œβ”€β”€ sessions/                       # Claude sessions
└── claude.json                     # Claude configuration

Deployment Commands

# Build image
docker build -f Dockerfile.sliplane -t myapp:latest .

# Push to registry
docker push myregistry/myapp:latest

# Deploy to Sliplane
curl -X POST "https://api.sliplane.io/deploy/service_id/secret?tag=latest"

# Verify deployment
ssh -p 22222 service_xxx@server.sliplane.app
./scripts/verify-claude-deployment.sh

Lessons Learned

  1. Symlinks are critical for unifying workspace locations
  2. Simple environment detection is more reliable than complex logic
  3. docker-entrypoint.sh ensures runtime configuration
  4. Verification scripts are essential for deployment validation
  5. Documentation of the solution prevents future issues

References


Result: Successfully integrated Claude CLI in Docker/Sliplane environment with unified workspace management and proper environment configuration.