diff --git a/Dockerfile b/Dockerfile index 6484d1a..e260253 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,6 +17,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libffi-dev \ libyaml-dev \ libpq-dev \ + socat \ && rm -rf /var/lib/apt/lists/* # --- rbenv (installed into home volume on first run, via entrypoint) --- diff --git a/README.md b/README.md index 3b6bd72..bd7c060 100644 --- a/README.md +++ b/README.md @@ -55,10 +55,10 @@ claude doctor # diagnose installation issues ## Volumes -| Volume | Purpose | -|---|---| +| Volume | Purpose | +| --------------------- | --------------------------------------------------------- | | `claude-home` (named) | Persists Claude Code binary, config, and auth credentials | -| `$PROJECT_DIR` (bind) | Your project code — swap freely between sessions | +| `$PROJECT_DIR` (bind) | Your project code — swap freely between sessions | To wipe the Claude install and start fresh: @@ -68,9 +68,84 @@ docker compose down -v # removes the claude-home volume ## Security notes -- Runs as a non-root user (`coder`, uid 1000) -- All Linux capabilities are dropped (`cap_drop: ALL`) +- Runs as a non-root user (`coder`, uid 1001) +- All Linux capabilities are dropped except `NET_BIND_SERVICE` - Privilege escalation is disabled (`no-new-privileges`) - The container has no network restrictions beyond what Docker provides — add a custom network or `--network none` with `--add-host` if you want to lock that down further + +## Connecting to a host PostgreSQL database + +The container can reach a PostgreSQL server running on the host, but +`127.0.0.1` inside the container refers to the container itself, not the +host. The solution is to connect via the Docker bridge gateway IP instead, +which both the host and the container can see. + +### 1. Find the gateway IP + +```bash +docker network inspect bridge | grep Gateway +``` + +This is typically `172.17.0.1`. Use the value specific to your machine +in all steps below. + +### 2. Configure PostgreSQL on the host + +Edit `/etc/postgresql//main/postgresql.conf`: + +``` +listen_addresses = 'localhost,172.17.0.1' +``` + +Edit `/etc/postgresql//main/pg_hba.conf` and add: + +``` +host all all 172.17.0.0/16 scram-sha-256 +``` + +Restart PostgreSQL: + +```bash +sudo systemctl restart postgresql +``` + +### 3. Configure your app + +Use the gateway IP as the database host. Since it is reachable from both +the host and the container, a single `DATABASE_URL` works in both contexts: + +``` +DATABASE_URL=postgresql://user:password@172.17.0.1:5432/mydb +``` + +Set this in your `.env` file or shell profile on the host, and pass it +through in `docker-compose.yml`: + +```yaml +environment: + - DATABASE_URL=${DATABASE_URL} +``` + +### Collation version warning + +If you see a warning like: + +``` +WARNING: database "mydb" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the +operating system provides version 2.42. +``` + +This is caused by the container's glibc version differing from the host's. +It is a warning only and will not break anything. To silence it, run once +on the host: + +```bash +psql -d mydb -c "ALTER DATABASE mydb REFRESH COLLATION VERSION;" +``` + +Note: the warning will reappear inside the container because its glibc +version differs from the host. The long-term fix is to rebase the Docker +image on the same Ubuntu release as the host so glibc versions match. diff --git a/docker-compose.yml b/docker-compose.yml index 3db9b5c..6551c94 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,11 +4,6 @@ services: image: claude-code:local container_name: claude-code - # Pass your Anthropic API key in from the host environment, - # or drop it into a .env file alongside this compose file. - # environment: - # - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:?Set ANTHROPIC_API_KEY in your environment or .env file} - volumes: # Fixed home volume — persists Claude Code install, config, and credentials # across container restarts and image rebuilds. @@ -18,13 +13,21 @@ services: # PROJECT_DIR=/path/to/myproject docker compose run --rm claude-code - ${PROJECT_DIR:-./code}:/code - # Drop all Linux capabilities and disable privilege escalation — - # Claude Code doesn't need any of them. + # Drop all Linux capabilities except NET_BIND_SERVICE, which socat needs + # to proxy port 5432 on 127.0.0.1 inside the container. cap_drop: - ALL + cap_add: + - NET_BIND_SERVICE security_opt: - no-new-privileges:true + # Allow the container to reach the host's network (e.g. a local postgres). + # On Linux, host.docker.internal isn't automatic — this creates it. + # On Mac/Windows Docker Desktop it's already available but this is harmless. + extra_hosts: + - "host.docker.internal:host-gateway" + # Interactive terminal so `claude` works properly stdin_open: true tty: true diff --git a/entrypoint.sh b/entrypoint.sh index bc1d370..5597f99 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -39,4 +39,12 @@ else echo "Claude Code $(claude --version 2>/dev/null || echo '(version unknown)') ready." fi +# Proxy host postgres to 127.0.0.1:5432 inside the container so the app can +# use the same DATABASE_URL whether running inside or outside Docker. +if ! ss -tlnp 2>/dev/null | grep -q ':5432'; then + echo "Starting postgres proxy 127.0.0.1:5432 -> host.docker.internal:5432" + socat TCP-LISTEN:5432,bind=127.0.0.1,fork,reuseaddr \ + TCP:host.docker.internal:5432 & +fi + exec "$@"