Appearance
Setting Up Ralph (Autonomous Claude Code Loop) for Local Django Development
A Step-by-Step Guide for WSL Ubuntu + Docker
What You're Building
This guide walks you through setting up Ralph — an autonomous development loop that wraps Claude Code CLI — inside a secure Docker container on your WSL Ubuntu system. Ralph will iteratively build your Django + PostgreSQL web application phase by phase, with intelligent exit detection, rate limiting, and progress tracking.
Your workflow will look like this:
┌─────────────────────────────────────────────────────────┐
│ WSL Ubuntu (your machine) │
│ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Docker Container │ │
│ │ │ │
│ │ Ralph (ralph_loop.sh) │ │
│ │ ↕ │ │
│ │ Claude Code CLI ──→ reads PROMPT.md │ │
│ │ ↕ reads fix_plan.md │ │
│ │ Your Django Project reads specs/ │ │
│ │ ├── src/ writes code │ │
│ │ ├── .ralph/ updates fix_plan.md │ │
│ │ └── docker-compose.yml │ │
│ │ ↕ │ │
│ │ PostgreSQL (container) │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘Prerequisites Checklist
Before starting, confirm you have:
- [x] WSL Ubuntu with Docker installed and running
- [x] Claude Code CLI installed and authenticated (Max Pro plan)
- [x] Git configured (
user.nameanduser.emailset) - [x] Node.js 18+ (required by Ralph for
jq-like JSON processing)
Verify everything quickly:
bash
# Run these in your WSL terminal
docker --version # Should show Docker version
claude --version # Should show Claude Code CLI version
git config user.name # Should show your name
node --version # Should show v18+Phase 1: Install Ralph Globally
Ralph is installed once on your system and then used across any number of projects.
Step 1.1 — Clone and Install Ralph
bash
cd ~
git clone https://github.com/frankbria/ralph-claude-code.git
cd ralph-claude-code
./install.shThis adds the following commands to your PATH:
ralph— main autonomous loopralph-monitor— tmux-based live monitoringralph-setup— create new Ralph projects from scratchralph-import— convert existing PRD/spec docs into Ralph formatralph-enable— add Ralph to an existing projectralph-enable-ci— non-interactive version for scripts
Step 1.2 — Verify Installation
bash
ralph --helpYou should see the full help output with options like --monitor, --calls, --prompt, etc.
Step 1.3 — Install jq (if not already present)
Ralph uses jq for JSON parsing of Claude Code's output:
bash
sudo apt install -y jq
jq --versionStep 1.4 — Install tmux (for the monitor)
The --monitor flag gives you a live split-screen view of Ralph's progress:
bash
sudo apt install -y tmuxNote: You can now delete the
~/ralph-claude-codeclone if you want — the install copied everything it needs to your PATH. However, keeping it around is fine for reference.
Phase 2: Create Your Project Structure
Step 2.1 — Create the Project Directory
bash
mkdir ~/my-django-app
cd ~/my-django-appStep 2.2 — Initialize with Ralph
You have two options here. Choose Option A if starting from scratch, or Option B if you already have a requirements document.
Option A — Interactive wizard (recommended for first time):
bash
ralph-enableThis will auto-detect your project type and walk you through setup interactively. When prompted:
- Select Python as the project type
- It will create the
.ralph/directory structure for you
Option B — From a PRD/requirements document:
If you've already written up your app requirements in a markdown file:
bash
ralph-enable --from prd ./my-requirements.mdStep 2.3 — Verify the Ralph Structure
After setup, your project should look like this:
my-django-app/
├── .ralph/
│ ├── PROMPT.md ← Instructions Ralph feeds to Claude Code
│ ├── fix_plan.md ← Checklist of tasks (Claude checks items off)
│ ├── AGENT.md ← Agent behavior configuration
│ ├── specs/ ← Detailed specifications per feature
│ │ └── stdlib/ ← Standard library specs
│ ├── examples/ ← Example code/patterns
│ ├── logs/ ← Ralph's execution logs
│ └── docs/
│ └── generated/ ← Auto-generated documentation
├── src/ ← Your application source code goes here
├── README.md
└── .git/Phase 3: Set Up the Docker Development Environment
This is where you create the secure, isolated container that Ralph and your Django app will run inside.
Step 3.1 — Create the Dockerfile
Create this file at the root of your project:
bash
cat > ~/my-django-app/Dockerfile << 'EOF'
FROM python:3.12-slim
# System dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
libpq-dev \
curl \
git \
jq \
tmux \
&& rm -rf /var/lib/apt/lists/*
# Install Node.js 22 (required for Claude Code CLI)
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
&& apt-get install -y nodejs \
&& rm -rf /var/lib/apt/lists/*
# Install Claude Code CLI globally
RUN npm install -g @anthropic-ai/claude-code
# Create non-root user for security
RUN useradd -m -s /bin/bash developer
USER developer
WORKDIR /home/developer/project
# Python dependencies will be installed via requirements.txt at runtime
ENV PATH="/home/developer/.local/bin:$PATH"
CMD ["bash"]
EOFStep 3.2 — Create docker-compose.yml
This sets up both your development container and PostgreSQL:
bash
cat > ~/my-django-app/docker-compose.yml << 'EOF'
services:
db:
image: postgres:16
container_name: django-postgres
environment:
POSTGRES_DB: myapp_dev
POSTGRES_USER: myapp_user
POSTGRES_PASSWORD: devpassword123
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myapp_user -d myapp_dev"]
interval: 5s
timeout: 5s
retries: 5
dev:
build: .
container_name: django-dev
depends_on:
db:
condition: service_healthy
environment:
- DATABASE_URL=postgresql://myapp_user:devpassword123@db:5432/myapp_dev
- DJANGO_SETTINGS_MODULE=config.settings.development
- PYTHONDONTWRITEBYTECODE=1
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
volumes:
- .:/home/developer/project
- pip-cache:/home/developer/.cache/pip
ports:
- "8000:8000"
stdin_open: true
tty: true
command: bash
volumes:
pgdata:
pip-cache:
EOFStep 3.3 — Handle Claude Code Authentication
Ralph needs your Claude Code CLI authentication inside the container. Since you're on Max Pro, you authenticate via your Anthropic session, not an API key. There are two approaches:
Approach A — Mount your existing Claude config (simplest):
Add this to the dev service volumes in docker-compose.yml:
yaml
volumes:
- .:/home/developer/project
- pip-cache:/home/developer/.cache/pip
- ~/.claude:/home/developer/.claude # ← Add this lineApproach B — Authenticate inside the container each time:
After entering the container (Step 3.5), run claude once and follow the browser-based auth flow.
Recommendation: Use Approach A for a smoother experience. Your auth tokens are mounted read-write so the session can refresh.
Step 3.4 — Build and Start the Containers
bash
cd ~/my-django-app
docker compose build
docker compose up -dVerify both containers are running:
bash
docker compose psYou should see django-dev and django-postgres both in a "running" state.
Step 3.5 — Enter the Development Container
bash
docker compose exec dev bashYou're now inside the secure container. Verify your tools:
bash
claude --version # Claude Code CLI
python --version # Python 3.12
node --version # Node.js 22
jq --version # JSON processor
git --version # Git
psql -h db -U myapp_user -d myapp_dev -c "SELECT 1;" # PostgreSQL connectivityNote: If
psqlisn't available, that's fine — Django handles the database connection. You can install it withsudo apt install postgresql-clientif needed, but since you're using a non-root user you'd need to adjust the Dockerfile. For development purposes, Django'smanage.py dbshellwill work once the project is scaffolded.
Phase 4: Write Your Project Specifications
This is the most important phase. The quality of Ralph's output depends entirely on the quality of your specifications. Ralph reads three key files.
Step 4.1 — Edit PROMPT.md (The Master Instructions)
This file tells Claude Code how to work on your project. Edit it from inside the container or from your WSL filesystem (they're the same files via the volume mount):
bash
# From WSL (outside the container):
nano ~/my-django-app/.ralph/PROMPT.md
# Or from inside the container:
nano /home/developer/project/.ralph/PROMPT.mdHere's a strong starting template for a Django + PostgreSQL project:
markdown
# Project: [Your App Name]
## Overview
[2-3 sentences describing what this application does and who it's for]
## Tech Stack
- **Backend:** Django 5.x with Django REST Framework
- **Database:** PostgreSQL 16 (running in Docker, accessible at host `db` port 5432)
- **Connection:** DATABASE_URL=postgresql://myapp_user:devpassword123@db:5432/myapp_dev
- **Python:** 3.12
- **Frontend:** [Django templates / React / HTMX — pick one]
## Architecture Decisions
- Use Django's built-in User model (extend with a Profile model if needed)
- All API endpoints use Django REST Framework serializers and viewsets
- Use Django migrations for all database schema changes
- Environment variables loaded via django-environ
- Use pytest + pytest-django for testing (not unittest)
## Development Rules
1. Always run migrations after model changes: `python manage.py makemigrations && python manage.py migrate`
2. Write tests for every new feature before marking it complete
3. Use type hints on all function signatures
4. Follow Django's project structure: `config/` for settings, individual apps in `src/`
5. Keep requirements.txt updated when adding new packages
6. Install packages with: `pip install <package> && pip freeze > requirements.txt`
## Database Connection
The PostgreSQL database is available at:
- Host: `db`
- Port: `5432`
- Database: `myapp_dev`
- User: `myapp_user`
- Password: `devpassword123`
## File Structuresrc/ ├── config/ │ ├── settings/ │ │ ├── base.py │ │ ├── development.py │ │ └── production.py │ ├── urls.py │ └── wsgi.py ├── apps/ │ ├── accounts/ ← User management │ ├── core/ ← Shared utilities │ └── [feature apps] ├── templates/ ├── static/ ├── manage.py └── requirements.txt
Step 4.2 — Edit fix_plan.md (The Task Checklist)
This is Ralph's progress tracker. Claude Code reads this file, works on unchecked items, and checks them off as it completes them. Ralph detects when all items are done and exits.
Structure it in phases so Ralph works through your app incrementally:
markdown
# Fix Plan
## Phase 1: Project Scaffolding
- [ ] Initialize Django project in src/ with split settings (base/dev/prod)
- [ ] Configure PostgreSQL database connection using DATABASE_URL
- [ ] Create requirements.txt with Django, psycopg2-binary, django-environ, djangorestframework
- [ ] Install dependencies and verify database connection
- [ ] Run initial migration and create superuser command
- [ ] Verify Django dev server starts on 0.0.0.0:8000
## Phase 2: User Authentication
- [ ] Create accounts app with custom user model (if needed) or Profile extension
- [ ] Implement registration endpoint with email validation
- [ ] Implement login/logout with session or token auth
- [ ] Write tests for all auth endpoints
- [ ] Add password reset flow
## Phase 3: Core Models & API
- [ ] Design and create models for [your domain objects]
- [ ] Create serializers for all models
- [ ] Implement CRUD viewsets with proper permissions
- [ ] Add pagination and filtering
- [ ] Write model and API tests
## Phase 4: [Your Feature]
- [ ] [Break into specific, testable tasks]
- [ ] [Each item should be completable in one Claude Code session]
- [ ] [Include a testing task for each feature group]
## Phase 5: Polish & Hardening
- [ ] Add comprehensive error handling and validation
- [ ] Configure CORS if needed
- [ ] Add logging configuration
- [ ] Write integration tests
- [ ] Update README with setup instructionsKey tip: Each checkbox item should be a single, concrete, testable task. "Build the user system" is too vague. "Create User model with email, name, and role fields" is right.
Step 4.3 — Add Detailed Specs (Optional but Powerful)
For complex features, add spec files to .ralph/specs/:
bash
mkdir -p .ralph/specs
nano .ralph/specs/user-model.mdExample spec:
markdown
# User Model Specification
## Fields
- email (unique, primary login identifier)
- first_name
- last_name
- role (choices: admin, staff, member)
- is_active (default True)
- created_at (auto)
- updated_at (auto)
## Constraints
- Email must be unique and validated
- Role defaults to "member"
- Soft delete via is_active flag (never hard delete users)
## API Endpoints
- POST /api/accounts/register/
- POST /api/accounts/login/
- GET /api/accounts/me/
- PATCH /api/accounts/me/Phase 5: Run Ralph
Now for the exciting part. You'll run Ralph from inside the Docker container, and it will autonomously build your application phase by phase.
Step 5.1 — Enter the Container (if not already inside)
bash
cd ~/my-django-app
docker compose exec dev bashStep 5.2 — Install Ralph Inside the Container
Since Ralph is installed on your WSL host but you're running inside Docker, you need Ralph accessible in the container too. The simplest approach is to mount it:
Add to your docker-compose.yml under the dev service volumes:
yaml
volumes:
- .:/home/developer/project
- pip-cache:/home/developer/.cache/pip
- ~/.claude:/home/developer/.claude
- ~/ralph-claude-code:/home/developer/ralph-claude-code # ← Add thisThen inside the container:
bash
cd /home/developer/ralph-claude-code
./install.sh
cd /home/developer/projectAlternatively, clone and install Ralph directly inside the container:
bash
cd ~
git clone https://github.com/frankbria/ralph-claude-code.git
cd ralph-claude-code
./install.sh
cd /home/developer/projectStep 5.3 — Start Ralph with Monitoring
bash
ralph --monitorThis launches a tmux split-screen session showing:
- Left pane: Ralph's main loop executing Claude Code
- Right pane: Live status monitor showing loop count, tasks completed, circuit breaker status
Step 5.4 — What Happens Now
Ralph enters an autonomous loop:
- Reads
.ralph/PROMPT.mdand.ralph/fix_plan.md - Sends instructions to Claude Code with context about remaining tasks
- Claude Code writes code, creates files, runs commands, runs tests
- Claude Code checks off completed items in
fix_plan.md - Ralph analyzes the response for completion signals
- If tasks remain → loops back to step 1
- If all tasks complete → exits cleanly
Ralph's intelligent exit detection triggers when:
- Multiple consecutive "done" signals from Claude Code
- Too many test-only loops (indicates feature completeness)
- All items in
fix_plan.mdare checked off
Step 5.5 — Useful Commands While Ralph Runs
Open a second terminal into the container:
bash
# From WSL, open another terminal
docker compose exec dev bash
# Check Ralph's status
ralph --status
# Check circuit breaker
ralph --circuit-status
# View logs
tail -f .ralph/logs/*.log
# Watch fix_plan.md progress in real time
watch -n 5 cat .ralph/fix_plan.mdStep 5.6 — Stopping and Resuming
To stop Ralph gracefully: Press Ctrl+C in the Ralph terminal. It will finish the current Claude Code execution and stop.
To resume: Just run ralph --monitor again. Ralph picks up where it left off by reading fix_plan.md for remaining unchecked tasks.
If the circuit breaker trips (too many errors or stagnation):
bash
# Check what happened
ralph --circuit-status
# Reset and continue
ralph --reset-circuit
ralph --monitorPhase 6: Working with Ralph Phase-by-Phase
The real power of Ralph is working through your app incrementally. Here's the recommended workflow:
The Phase-by-Phase Workflow
1. Write specs for Phase 1 only:
- Fill in
fix_plan.mdwith just Phase 1 tasks - Add any relevant specs to
.ralph/specs/
2. Run Ralph:
bash
ralph --monitor3. Review the output: Once Ralph completes Phase 1 (all checkboxes checked), review what it built:
bash
# Look at the code
find src/ -name "*.py" | head -20
# Run the tests
cd src && python manage.py test
# Start the dev server to check manually
python manage.py runserver 0.0.0.0:80004. Make adjustments: If something isn't right, you have two options:
- Manual fix: Edit the code yourself, commit, then update
fix_plan.md - Let Ralph fix it: Add new tasks to
fix_plan.mddescribing what needs to change:markdown- [ ] Refactor User model to use AbstractBaseUser instead of AbstractUser - [ ] Fix the registration serializer to validate email domain
5. Add Phase 2 tasks to fix_plan.md: Once you're satisfied with Phase 1, add the next batch of tasks and run Ralph again.
6. Repeat until your application is complete.
Tips for Best Results
- Be specific in tasks. "Build authentication" → bad. "Create login endpoint that accepts email + password and returns a JWT token" → good.
- Include testing tasks. Ralph (and Claude Code) will actually run the tests and fix failures.
- One phase at a time. Don't load all 5 phases into
fix_plan.mdat once. Work incrementally so you can review and course-correct. - Use specs for complex features. A 20-line spec file saves Ralph from making wrong assumptions.
- Commit between phases. After each successful phase, commit your code:bash
git add -A git commit -m "Phase 1 complete: project scaffolding and DB setup"
Phase 7: Customizing Ralph's Behavior
Adjusting Rate Limits
With Max Pro you won't hit token limits, but you can control how aggressively Ralph loops:
bash
# Default: 100 calls per hour
ralph --monitor --calls 50 # More conservative
ralph --monitor --calls 200 # More aggressiveTimeout Per Loop
Control how long each Claude Code execution can run:
bash
# Default: 15 minutes per execution
ralph --monitor --timeout 30 # 30 minutes for complex tasks
ralph --monitor --timeout 5 # 5 minutes for quick iterationsVerbose Mode
See detailed progress during execution:
bash
ralph --monitor --verboseCustom Prompt File
If you want different instructions for different phases:
bash
ralph --monitor --prompt .ralph/phase2-prompt.mdAllowed Tools
Control what Claude Code can do (security):
bash
# Default: Write, Bash(git *), Read
# Add more permissions:
ralph --monitor --allowed-tools "Write,Bash,Read"Security note: The default
Bash(git *)only allows git-related bash commands. If you need Claude to run Django management commands, pip install, etc., you'll need to expand this toBash(all bash commands). Inside Docker, this is safe since the container is your security boundary.
Quick Reference Card
Starting a Session
bash
cd ~/my-django-app
docker compose up -d # Start containers
docker compose exec dev bash # Enter dev container
ralph --monitor # Start RalphChecking Progress
bash
ralph --status # Current loop status
ralph --circuit-status # Circuit breaker state
cat .ralph/fix_plan.md # Task progress
tail -f .ralph/logs/*.log # Live logsRecovering from Issues
bash
ralph --reset-circuit # Reset tripped circuit breaker
ralph --reset-session # Start fresh session
ralph --no-continue # Don't continue previous sessionShutting Down
bash
exit # Leave container
docker compose down # Stop containers (data persists)
docker compose down -v # Stop + destroy database volumeAccessing Your App
Django dev server: http://localhost:8000
Django admin: http://localhost:8000/admin
PostgreSQL: localhost:5432 (from WSL host)
db:5432 (from inside container)Troubleshooting
"Claude Code not found" inside container
Make sure Ralph is installed inside the container (see Step 5.2), and that Claude Code CLI is installed in the Dockerfile.
Circuit breaker keeps tripping
This usually means Claude is stuck in a loop. Check the logs:
bash
cat .ralph/logs/*.log | tail -50Common causes: failing tests it can't fix, missing system dependency, or a task that's too vague. Add more specific tasks to fix_plan.md or fix the blocker manually.
Claude Code auth fails in container
If you mounted ~/.claude and auth still fails:
bash
# Check the mount
ls -la /home/developer/.claude/
# Re-authenticate if needed
claude auth loginPostgreSQL connection refused
bash
# Check the DB container is running
docker compose ps
# Check from inside dev container
pg_isready -h db -U myapp_user
# Restart if needed
docker compose restart dbPermission errors on mounted volumes
This happens when the container user's UID doesn't match your WSL user's UID:
bash
# From WSL, check your UID
id -u
# If it's not 1000, update the Dockerfile:
# RUN useradd -m -s /bin/bash -u YOUR_UID developerSummary: The Full Workflow at a Glance
1. Install Ralph globally (once) → ./install.sh
2. Create project directory → mkdir my-django-app
3. Initialize Ralph → ralph-enable
4. Set up Docker (Dockerfile + compose) → docker compose build
5. Write PROMPT.md (how to build) → .ralph/PROMPT.md
6. Write fix_plan.md Phase 1 (what to build) → .ralph/fix_plan.md
7. Start containers → docker compose up -d
8. Enter container → docker compose exec dev bash
9. Run Ralph → ralph --monitor
10. Review Phase 1 output → test, inspect, commit
11. Write fix_plan.md Phase 2 → add next tasks
12. Run Ralph again → ralph --monitor
13. Repeat until done → 🎉