v1 - basic functionality

This commit is contained in:
2026-04-11 09:26:46 -04:00
commit 243b84946f
5 changed files with 166 additions and 0 deletions

25
Dockerfile Normal file
View File

@ -0,0 +1,25 @@
FROM node:22-slim
# Install runtime dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
git \
ripgrep \
bash \
&& rm -rf /var/lib/apt/lists/*
# Create a non-root user with a real home directory
RUN useradd -m -s /bin/bash -u 1001 coder
# Ensure ~/.local/bin is on PATH (where the native installer puts the binary)
ENV PATH="/home/coder/.local/bin:/home/coder/.claude/bin:${PATH}"
# Copy entrypoint (as root, then lock down)
COPY --chmod=755 entrypoint.sh /usr/local/bin/entrypoint.sh
USER coder
WORKDIR /code
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["bash"]

76
README.md Normal file
View File

@ -0,0 +1,76 @@
# Claude Code — Dockerized
A minimal, guardrailed container for running Claude Code. The home
directory and project folder are volumes, keeping your Claude install
and credentials separate from any specific project.
## Setup
```bash
# 1. Build the image
docker compose build
# 2. Export your API key (or put it in a .env file)
export ANTHROPIC_API_KEY=sk-ant-...
# 3. First run — installs Claude Code into the home volume, then drops you
# into an interactive shell inside the default ./code directory
docker compose run --rm claude-code
```
On first start the entrypoint runs the native installer and places the
binary in the `claude-home` named volume (under `/home/coder/.local/bin`).
Subsequent starts skip the install and launch immediately.
## Switching projects
Point `PROJECT_DIR` at any directory on your host:
```bash
PROJECT_DIR=/path/to/myproject docker compose run --rm claude-code
```
Or set it in a `.env` file:
```
ANTHROPIC_API_KEY=sk-ant-...
PROJECT_DIR=/Users/me/projects/my-app
```
Then just:
```bash
docker compose run --rm claude-code
```
## Starting Claude Code
Once inside the container shell:
```bash
claude # start an interactive session in the current directory
claude --help # show available options
claude doctor # diagnose installation issues
```
## Volumes
| Volume | Purpose |
|---|---|
| `claude-home` (named) | Persists Claude Code binary, config, and auth credentials |
| `$PROJECT_DIR` (bind) | Your project code — swap freely between sessions |
To wipe the Claude install and start fresh:
```bash
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`)
- 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

37
docker-compose.yml Normal file
View File

@ -0,0 +1,37 @@
services:
claude-code:
build: .
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.
- 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 and disable privilege escalation —
# Claude Code doesn't need any of them.
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
# 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)

15
entrypoint.sh Normal file
View File

@ -0,0 +1,15 @@
#!/bin/bash
set -e
# Install Claude Code if not already present in the home volume.
# Because the home directory is a volume, this install persists across
# container restarts and rebuilds.
if ! command -v claude &>/dev/null; then
echo "Claude Code not found — running installer..."
curl -fsSL https://claude.ai/install.sh | bash
echo "Claude Code installed successfully."
else
echo "Claude Code $(claude --version 2>/dev/null || echo '(version unknown)') ready."
fi
exec "$@"

13
safeclaude Executable file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -lt 1 ]]; then
echo "Usage: $(basename "$0") <path-to-project>" >&2
exit 1
fi
PROJECT_DIR="$(cd "$1" && pwd)" # resolve to absolute path
SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)"
PROJECT_DIR="$PROJECT_DIR" docker compose -f "$SCRIPT_DIR/docker-compose.yml" run -w /code --rm claude-code claude