# syntax=docker/dockerfile:1 # Multi-stage Dockerfile for Claude Code Proxy # Builds both Go proxy server and SvelteKit frontend in a single container # # Targets: # - (default): Production runtime image # - dev: Development image with hot-reload tooling # ============================================================================ # Stage: go-builder — compile Go proxy binary # ============================================================================ FROM golang:1.26-alpine AS go-builder WORKDIR /app/proxy # Install build dependencies including gcc for CGO (sqlite) RUN apk add --no-cache git gcc musl-dev sqlite-dev # Copy Go modules first (cache layer) COPY proxy/go.mod proxy/go.sum ./ RUN --mount=type=cache,target=/go/pkg/mod \ go mod download # Copy Go source code and build COPY proxy/ ./ RUN --mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/root/.cache/go-build \ CGO_ENABLED=1 GOOS=linux go build -a -installsuffix cgo -o /app/bin/proxy cmd/proxy/main.go # ============================================================================ # Stage: svelte-deps — install SvelteKit dependencies (cached) # ============================================================================ FROM node:20-alpine AS svelte-deps WORKDIR /app/svelte COPY svelte/package*.json ./ RUN --mount=type=cache,target=/root/.npm \ npm ci # ============================================================================ # Stage: svelte-builder — build SvelteKit frontend # ============================================================================ FROM svelte-deps AS svelte-builder COPY svelte/ ./ # shared/ is referenced by vite.config.ts (../shared/frontend/backend) COPY shared/ /app/shared/ RUN npm run build # ============================================================================ # Stage: svelte-prod — production SvelteKit deps only # ============================================================================ FROM node:20-alpine AS svelte-prod WORKDIR /app/svelte COPY svelte/package*.json ./ RUN --mount=type=cache,target=/root/.npm \ npm ci --omit=dev # ============================================================================ # Stage: dev — development image with hot-reload # ============================================================================ # CGO is required for mattn/go-sqlite3. To avoid slow first-build times # at container start, we pre-build the binary (and warm the build cache) # during image build. CompileDaemon then does fast incremental rebuilds. FROM golang:1.26-alpine AS dev # Copy Node.js 20 from official image (Alpine's repos ship Node 24 which has # breaking module-resolution changes that break SvelteKit's virtual modules) COPY --from=node:20-alpine /usr/local/bin/node /usr/local/bin/node COPY --from=node:20-alpine /usr/local/lib/node_modules /usr/local/lib/node_modules RUN ln -sf /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \ && ln -sf /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx # Install build deps and runtime tools RUN apk add --no-cache \ libstdc++ \ git gcc musl-dev sqlite-dev \ wget su-exec postgresql-client # Install CompileDaemon for Go hot-reload RUN go install github.com/githubnemo/CompileDaemon@latest WORKDIR /app # Pre-install Go dependencies and do initial build to warm cgo cache COPY proxy/go.mod proxy/go.sum ./proxy/ RUN cd proxy && go mod download COPY proxy/ ./proxy/ RUN cd proxy && CGO_ENABLED=1 go build -o /tmp/proxy-bin/proxy cmd/proxy/main.go # Pre-install Node dependencies for svelte (layer cache) COPY svelte/package*.json ./svelte/ RUN cd svelte && npm install # Copy the dev entrypoint COPY docker-entrypoint.dev.sh ./ RUN chmod +x docker-entrypoint.dev.sh ENV PORT=3001 ENV SVELTE_PORT=5174 ENV CGO_ENABLED=1 EXPOSE 3001 5174 ENTRYPOINT ["./docker-entrypoint.dev.sh"] # ============================================================================ # Stage: (default) — production runtime # ============================================================================ FROM node:20-alpine WORKDIR /app # Install runtime dependencies (sqlite for legacy, postgresql-client for healthcheck) RUN apk add --no-cache sqlite wget su-exec postgresql-client # Copy built Go binary COPY --from=go-builder /app/bin/proxy ./bin/proxy RUN chmod +x ./bin/proxy # Copy built SvelteKit application with production deps COPY --from=svelte-builder /app/svelte/build ./svelte/build COPY --from=svelte-prod /app/svelte/package*.json ./svelte/ COPY --from=svelte-prod /app/svelte/node_modules ./svelte/node_modules # Create data directory for SQLite database RUN mkdir -p /app/data && chown -R node:node /app # Copy startup script COPY docker-entrypoint.sh ./ RUN chmod +x docker-entrypoint.sh # Environment variables with defaults ENV PORT=3001 ENV SVELTE_PORT=5174 ENV READ_TIMEOUT=600 ENV WRITE_TIMEOUT=600 ENV IDLE_TIMEOUT=600 ENV ANTHROPIC_FORWARD_URL=https://api.anthropic.com ENV ANTHROPIC_VERSION=2023-06-01 ENV ANTHROPIC_MAX_RETRIES=3 ENV DB_PATH=/app/data/requests.db EXPOSE 3001 5174 HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD wget -qO- http://localhost:3001/health > /dev/null || exit 1 # Entrypoint handles privilege drop — compose overrides user to root at start ENTRYPOINT ["./docker-entrypoint.sh"] CMD []