services: claude-code: build: . image: claude-code:local container_name: claude-code volumes: # Fixed home volume — persists Claude Code install, config, and credentials # across container restarts and image rebuilds. - claude-home:/home/coder # Swappable project folder — override PROJECT_DIR to point at any directory: # PROJECT_DIR=/path/to/myproject docker compose run --rm claude-code - ${PROJECT_DIR:-./code}:/code # 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 working_dir: /code volumes: claude-home: # Named volume — Docker manages it; survives `docker compose down` # (use `docker compose down -v` to wipe it along with the install)