-
Notifications
You must be signed in to change notification settings - Fork 5
feat: add Docker Compose setup for local development #278
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
2092374
c689206
5996a6f
1aa8107
171f20a
ca6ef62
be877c7
164dcc5
462bf6e
5ad98f4
9173e7b
a1f25a7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,212 @@ | ||
| # Docker-based Local Development | ||
|
|
||
| Run the entire monorepo (Next.js backend + all Cloudflare Workers + PostgreSQL) with a single command instead of managing ~16 terminal windows. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| | Tool | Version | Check | | ||
| |------|---------|-------| | ||
| | Docker Desktop (macOS/Windows) / Docker Engine (Linux) | 4.x+ | `docker --version` | | ||
| | Docker Compose v2 | 2.20+ | `docker compose version` | | ||
| | pnpm | 10.27.0 | `pnpm --version` | | ||
| | Node.js | ^22 | `node --version` | | ||
|
|
||
| > **Note:** `pnpm install` must be run at least once before starting — the Docker containers mount the repo and expect `node_modules` to exist. | ||
kiloconnect[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| ## Quick Start | ||
|
|
||
| ```bash | ||
| # Install dependencies (if not already done) | ||
| pnpm install | ||
|
|
||
| # Start everything | ||
| ./dev/dev.sh | ||
|
|
||
| # Or directly with docker compose | ||
| docker compose -f dev/docker-compose.dev.yml --profile all up | ||
| ``` | ||
|
|
||
| ## Profiles | ||
|
|
||
| Services are grouped into profiles for selective startup. PostgreSQL always starts (no profile required). | ||
|
|
||
| | Profile | Services | | ||
| |---------|----------| | ||
| | `core` | PostgreSQL + Next.js backend | | ||
| | `agents` | PostgreSQL + cloud-agent + cloud-agent-next | | ||
| | `workers` | PostgreSQL + cloud-agent + all Cloudflare Workers | | ||
| | `all` | Everything | | ||
|
|
||
| ```bash | ||
| # Core only (postgres + nextjs) | ||
| ./dev/dev.sh --profile core up | ||
|
|
||
| # Core + agents | ||
| ./dev/dev.sh --profile core --profile agents up | ||
|
|
||
| # Everything | ||
| ./dev/dev.sh # defaults to --profile all | ||
| ./dev/dev.sh --profile all up | ||
|
|
||
| # Specific services by name | ||
| docker compose -f dev/docker-compose.dev.yml up postgres nextjs cloud-agent | ||
| ``` | ||
|
|
||
| ## Port Map | ||
|
|
||
| | Service | Port | Directory | | ||
| |---------|------|-----------| | ||
| | PostgreSQL | 5432 | — | | ||
| | Next.js backend | 3000 | `.` (root) | | ||
| | cloud-agent | 8788 | `cloud-agent/` | | ||
| | cloudflare-ai-attribution | 8787 | `cloudflare-ai-attribution/` | | ||
| | cloudflare-code-review-infra | 8789 | `cloudflare-code-review-infra/` | | ||
| | cloudflare-app-builder | 8790 | `cloudflare-app-builder/` | | ||
| | cloudflare-auto-triage-infra | 8791 | `cloudflare-auto-triage-infra/` | | ||
| | cloudflare-webhook-agent-ingest | 8793 | `cloudflare-webhook-agent-ingest/` | | ||
| | cloud-agent-next | 8794 | `cloud-agent-next/` | | ||
| | kiloclaw | 8795 | `kiloclaw/` | | ||
| | cloudflare-auto-fix-infra | 8796 | `cloudflare-auto-fix-infra/` | | ||
| | cloudflare-db-proxy | 8797 | `cloudflare-db-proxy/` | | ||
| | cloudflare-deploy-builder | 8798 | `cloudflare-deploy-infra/builder/` | | ||
| | cloudflare-deploy-dispatcher | 8799 | `cloudflare-deploy-infra/dispatcher/` | | ||
| | cloudflare-session-ingest | 8800 | `cloudflare-session-ingest/` | | ||
| | cloudflare-o11y | 8801 | `cloudflare-o11y/` | | ||
| | cloudflare-git-token-service | 8802 | `cloudflare-git-token-service/` | | ||
|
|
||
| > Ports are overridden via `--port` in the wrangler dev command to avoid conflicts between workers that share the same default port in their `wrangler.jsonc`. | ||
|
|
||
| ## Networking | ||
|
|
||
| All services share the default Docker Compose bridge network. Services reach each other by their Compose service name (e.g., `postgres`, `nextjs`, `cloud-agent`) via Docker's built-in DNS — no `network_mode: host` needed, so this works on both **macOS** (Docker Desktop) and **Linux**. | ||
|
|
||
| ### How inter-service URLs are resolved | ||
|
|
||
| The wrangler.jsonc files in each worker hardcode `localhost` for inter-service URLs (e.g., `KILOCODE_BACKEND_BASE_URL: "http://localhost:3000"`, Hyperdrive `localConnectionString: "postgres://...@localhost:5432/..."`). Inside Docker containers on a bridge network, `localhost` refers to the container itself, not other services. | ||
|
|
||
| To fix this without modifying the shared wrangler.jsonc files: | ||
|
|
||
| - **Wrangler workers** use [`dev/docker-wrangler-entrypoint.sh`](dev/docker-wrangler-entrypoint.sh) which creates a temporary patched copy of `wrangler.jsonc` at startup, replacing `localhost` references with Docker service names (e.g., `localhost:3000` → `nextjs:3000`, `localhost:5432` → `postgres:5432`). | ||
| - **Next.js** overrides env vars directly via the `environment:` key in docker-compose (e.g., `POSTGRES_URL`, `CLOUD_AGENT_API_URL`), which take precedence over values in `.env`. | ||
|
|
||
| ### Accessing services from the host | ||
|
|
||
| From your host machine, services are still accessible at `localhost:<port>` via Docker's port forwarding (the `ports:` mappings in docker-compose). | ||
|
|
||
| ## Environment Variables | ||
|
|
||
| ### Next.js backend | ||
|
|
||
| The Next.js service loads `env_file: ../.env` (repo root `.env`). Make sure this file exists: | ||
|
|
||
| ```bash | ||
| # If you have an .env.example, copy it | ||
| cp .env.example .env | ||
| # Then fill in the required values | ||
| ``` | ||
|
|
||
| The docker-compose file overrides `POSTGRES_URL`, `CLOUD_AGENT_API_URL`, and `WEBHOOK_AGENT_URL` to use Docker service names. You don't need to set these in `.env` for Docker dev. | ||
|
|
||
| ### Cloudflare Workers | ||
|
|
||
| Workers that need secrets use `.dev.vars` files in their respective directories. Copy the examples: | ||
|
|
||
| ```bash | ||
| # Example for cloud-agent | ||
| cp cloud-agent/.dev.vars.example cloud-agent/.dev.vars | ||
|
|
||
| # Workers with .dev.vars.example files: | ||
| # cloud-agent, cloud-agent-next, cloudflare-app-builder, | ||
| # cloudflare-auto-fix-infra, cloudflare-auto-triage-infra, | ||
| # cloudflare-code-review-infra, cloudflare-db-proxy, | ||
| # cloudflare-deploy-infra/builder, cloudflare-deploy-infra/dispatcher, | ||
| # cloudflare-git-token-service, cloudflare-webhook-agent-ingest, | ||
| # kiloclaw | ||
| ``` | ||
|
|
||
| > **Note:** You do NOT need to change `localhost` references in `.dev.vars` files for Docker — the entrypoint script handles URL rewriting automatically via the wrangler.jsonc patching. | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [WARNING]: This note says
Consider either:
|
||
|
|
||
| ## Architecture | ||
|
|
||
| - **Shared Docker image** (`dev/Dockerfile.dev`): `node:22.14.0-slim` with pnpm, wrangler, and bun pre-installed. | ||
| - **Volume mount**: The entire repo is mounted at `/app` — file changes are reflected immediately (hot reload works). | ||
| - **Port mappings**: Each service exposes its port via explicit `ports:` mappings, which works on both macOS (Docker Desktop) and Linux. | ||
| - **Inter-service networking**: Docker Compose bridge network with DNS-based service discovery. See [Networking](#networking) above. | ||
| - **Existing `dev/docker-compose.yml`** is untouched — it continues to work standalone for PostgreSQL-only usage. | ||
|
|
||
| ## Docker Socket Mount (Container-backed Durable Objects) | ||
|
|
||
| Four workers use Cloudflare's [container-backed Durable Objects](https://developers.cloudflare.com/durable-objects/best-practices/create-durable-object-stubs-and-send-requests/#container-durable-objects), which require Docker to spawn sandbox containers at runtime via Wrangler: | ||
|
|
||
| - `cloud-agent` | ||
| - `cloud-agent-next` | ||
| - `cloudflare-app-builder` | ||
| - `cloudflare-deploy-builder` | ||
|
|
||
| Because these workers already run inside Docker containers, they can't use Docker natively (Docker-in-Docker). Instead, the host's Docker socket is mounted into these containers: | ||
|
|
||
| ```yaml | ||
| volumes: | ||
| - /var/run/docker.sock:/var/run/docker.sock | ||
| ``` | ||
|
|
||
| This lets Wrangler inside the container talk to the host's Docker daemon to create sibling containers. | ||
|
|
||
| ### Security implications | ||
|
|
||
| Mounting the Docker socket gives the container **full, unrestricted access to the host's Docker daemon** — equivalent to root access on the host. This is acceptable for local development but must **never** be used in production or CI environments with untrusted code. The mount is only applied to the four workers listed above; all other services use the default volume configuration from the shared base. | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### Port already in use | ||
|
|
||
| If a service fails with `EADDRINUSE`, another process is already using that port. Check with: | ||
|
|
||
| ```bash | ||
| lsof -i :<port> | ||
| ``` | ||
|
|
||
| ### Worker fails to start | ||
|
|
||
| Some workers (cloud-agent, cloud-agent-next) have `predev` scripts that build a wrapper using `bun`. The Docker image includes bun, but if you see build errors, try: | ||
|
|
||
| ```bash | ||
| # Rebuild the Docker image | ||
| docker compose -f dev/docker-compose.dev.yml build --no-cache | ||
| ``` | ||
|
|
||
| ### node_modules issues | ||
|
|
||
| The containers mount the host's `node_modules`. If you see missing dependency errors: | ||
|
|
||
| ```bash | ||
| pnpm install | ||
| # Then restart the containers | ||
| docker compose -f dev/docker-compose.dev.yml --profile all restart | ||
| ``` | ||
|
|
||
| ### Inter-service connection refused | ||
|
|
||
| If a worker can't reach another service (e.g., `ECONNREFUSED` to `nextjs:3000`), make sure the target service is running. Check with: | ||
|
|
||
| ```bash | ||
| docker compose -f dev/docker-compose.dev.yml ps | ||
| ``` | ||
|
|
||
| Services only start if their profile is active. For example, `cloud-agent` requires the `agents` or `all` profile. | ||
|
|
||
| ### Viewing logs for a single service | ||
|
|
||
| ```bash | ||
| docker compose -f dev/docker-compose.dev.yml logs -f nextjs | ||
| docker compose -f dev/docker-compose.dev.yml logs -f cloud-agent | ||
| ``` | ||
|
|
||
| ### Stopping everything | ||
|
|
||
| ```bash | ||
| docker compose -f dev/docker-compose.dev.yml --profile all down | ||
|
|
||
| # Also remove the postgres volume if you want a fresh database | ||
| docker compose -f dev/docker-compose.dev.yml --profile all down -v | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # Makefile — convenience targets for Kilo-Org/cloud | ||
|
|
||
| COMPOSE_DEV := docker compose -f dev/docker-compose.dev.yml | ||
|
|
||
| .PHONY: dev-docker dev-docker-core dev-docker-down | ||
|
|
||
| ## Start all services in Docker (postgres + nextjs + all workers) | ||
| dev-docker: | ||
| ./dev/dev.sh | ||
|
|
||
| ## Start only postgres + nextjs | ||
| dev-docker-core: | ||
| $(COMPOSE_DEV) --profile core up | ||
|
|
||
| ## Stop all Docker dev services | ||
| dev-docker-down: | ||
| $(COMPOSE_DEV) --profile all down |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| FROM node:22.14.0-slim | ||
|
|
||
| # Install pnpm | ||
| RUN corepack enable && corepack prepare pnpm@10.27.0 --activate | ||
|
|
||
| # Install wrangler globally for workers (pinned to match repo devDependencies) | ||
| RUN pnpm add -g wrangler@4.61.1 | ||
|
|
||
| # Install bun (needed by cloud-agent and cloud-agent-next predev scripts) | ||
| # Pinned to a specific version for reproducibility | ||
| RUN apt-get update && apt-get install -y curl unzip && \ | ||
| curl -fsSL https://bun.sh/install | BUN_INSTALL=/usr/local bash -s "bun-v1.2.5" && \ | ||
| apt-get clean && rm -rf /var/lib/apt/lists/* | ||
|
|
||
| # Set working directory | ||
| WORKDIR /app |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| #!/usr/bin/env bash | ||
| # Start the Docker Compose dev environment for the monorepo. | ||
| # | ||
| # Usage: | ||
| # ./dev/dev.sh # start everything | ||
| # ./dev/dev.sh --profile core # postgres + nextjs | ||
| # ./dev/dev.sh --profile core --profile agents # core + cloud-agent services | ||
| # ./dev/dev.sh --profile workers # postgres + all CF workers | ||
| # ./dev/dev.sh up postgres nextjs cloud-agent # specific services only | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" | ||
| COMPOSE_FILE="$REPO_ROOT/dev/docker-compose.dev.yml" | ||
|
|
||
| # ── Preflight checks ───────────────────────────────────────────────── | ||
|
|
||
| if ! command -v docker &>/dev/null; then | ||
| echo "❌ Docker is not installed. Please install Docker first." | ||
| echo " https://docs.docker.com/get-docker/" | ||
| exit 1 | ||
| fi | ||
|
|
||
| if ! docker compose version &>/dev/null; then | ||
| echo "❌ Docker Compose v2 is required (docker compose, not docker-compose)." | ||
| echo " https://docs.docker.com/compose/install/" | ||
| exit 1 | ||
| fi | ||
|
|
||
| if [ ! -d "$REPO_ROOT/node_modules" ]; then | ||
| if ! command -v pnpm &>/dev/null; then | ||
| echo "❌ pnpm is not installed and node_modules is missing." | ||
| echo " Install pnpm first: corepack enable && corepack prepare pnpm@10.27.0 --activate" | ||
| echo " Then run: pnpm install" | ||
| exit 1 | ||
| fi | ||
| echo "⚠️ node_modules not found. Running pnpm install first..." | ||
| (cd "$REPO_ROOT" && pnpm install) | ||
| fi | ||
|
|
||
| if [ ! -f "$REPO_ROOT/.env" ]; then | ||
| echo "⚠️ No .env file found at repo root." | ||
| echo " Copy .env.example or create one before the Next.js service can start." | ||
| fi | ||
|
|
||
| # ── Launch ──────────────────────────────────────────────────────────── | ||
|
|
||
| # If no arguments provided, start everything with the "all" profile | ||
| if [ $# -eq 0 ]; then | ||
| echo "🚀 Starting all services..." | ||
| exec docker compose -f "$COMPOSE_FILE" --profile all up | ||
| else | ||
| echo "🚀 Starting services..." | ||
| exec docker compose -f "$COMPOSE_FILE" "$@" | ||
| fi |
Uh oh!
There was an error while loading. Please reload this page.