Complete guide for installing and running ProjectAchilles on Windows using Docker Desktop.
Estimated time: 20–30 minutes (plus Docker image build)
- Prerequisites
- Install Docker Desktop
- Install Git for Windows
- Clone the Repository
- Create a Clerk Account
- Configure Environment
- Build and Start Services
- Verify Installation
- Optional: Local Elasticsearch
- Stopping and Restarting
- Updating
- Troubleshooting
| Requirement | Minimum | Recommended |
|---|---|---|
| Windows | 10 (21H2+) or 11 | Windows 11 |
| PowerShell | 7.0+ | Latest version |
| RAM | 8 GB | 16 GB (32 GB if using Elasticsearch) |
| Disk | 10 GB free | 20 GB free |
| CPU | 64-bit with virtualization | 4+ cores |
Verify virtualization is enabled:
- Open Task Manager (
Ctrl+Shift+Esc) - Go to Performance → CPU
- Check that Virtualization shows Enabled
If disabled, enable it in your BIOS/UEFI settings (usually called "Intel VT-x" or "AMD-V").
Docker Desktop on Windows requires WSL2 (Windows Subsystem for Linux 2).
Open PowerShell as Administrator and run:
wsl --installRestart your computer when prompted. After restart, WSL will finish setting up — you may be asked to create a Linux username and password (this is for WSL only, not ProjectAchilles).
Verify WSL2 is active:
wsl --version- Download Docker Desktop from: https://www.docker.com/products/docker-desktop/
- Run the installer — keep all default options
- When prompted, ensure "Use WSL 2 instead of Hyper-V" is checked
- Restart your computer if prompted
- Launch Docker Desktop from the Start menu
- Wait for the Docker engine to start (the whale icon in the system tray will stop animating)
Verify Docker is working — open PowerShell or Command Prompt:
docker --version
docker compose versionBoth commands should return version information without errors.
Open Docker Desktop → Settings (gear icon):
- General → Ensure "Use the WSL 2 based engine" is checked
- Resources → WSL Integration → Enable integration with your default WSL distro
- Resources → Advanced → Allocate at least:
- CPUs: 2 (4 recommended)
- Memory: 4 GB (8 GB if using Elasticsearch)
Click Apply & restart.
- Download Git for Windows from: https://git-scm.com/download/win
- Run the installer with these important settings:
- Line ending conversions: Select "Checkout as-is, commit as-is" or "Checkout as-is, commit Unix-style line endings"
This prevents CRLF line ending issues that break shell scripts inside Docker containers.
- All other options can stay at defaults
- Line ending conversions: Select "Checkout as-is, commit as-is" or "Checkout as-is, commit Unix-style line endings"
- Verify installation:
git --versionAlready have Git installed? Configure line endings for this project:
git config --global core.autocrlf input
Open PowerShell or Git Bash and run:
cd $HOME
git clone https://github.com/your-org/ProjectAchilles.git
cd ProjectAchillesImportant: Clone to a path without spaces (e.g.,
C:\Users\YourName\ProjectAchilles). Paths with spaces can cause issues with Docker volume mounts.
The docker-entrypoint.sh file must have Unix (LF) line endings, not Windows (CRLF). Verify and fix if needed:
# In Git Bash:
file frontend/docker-entrypoint.sh
# Should say: "POSIX shell script, ASCII text executable"
# If it says "CRLF" anywhere, fix with:
sed -i 's/\r$//' frontend/docker-entrypoint.shOr in PowerShell:
# Check for CRLF
(Get-Content frontend/docker-entrypoint.sh -Raw) -match "`r`n"
# If True, fix with:
(Get-Content frontend/docker-entrypoint.sh -Raw) -replace "`r`n", "`n" | Set-Content -NoNewline frontend/docker-entrypoint.shProjectAchilles uses Clerk for authentication. You need a free Clerk account.
-
Go to https://clerk.com and sign up
-
Create a new application:
- Name:
ProjectAchilles(or any name you prefer) - Sign-in options: Enable at least Email (optionally add Google, Microsoft, GitHub)
- Name:
-
After creation, go to API Keys in the Clerk dashboard
-
Copy these two values — you will need them in the next step:
Key Looks like Description Publishable key pk_test_abc123...Public key (safe to expose in frontend) Secret key sk_test_xyz789...Private key (backend only, keep secret)
The all-in-one PowerShell script handles configuration and building/launching in a single step:
.\scripts\Install-ProjectAchilles.ps1The script will:
- Check that Git, Docker, and Docker Compose are installed and running
- Fix CRLF line endings in shell scripts (prevents Docker build failures)
- Ask for your Clerk keys (with input masking for the secret key)
- Ask about Elasticsearch and test repository configuration
- Generate secure secrets (SESSION_SECRET, ENCRYPTION_SECRET)
- Write everything to
backend/.env - Build and start Docker containers
- Wait for services to become healthy
- Open
http://localhostin your browser
Quick mode (minimal prompts — just provide Clerk keys):
.\scripts\Install-ProjectAchilles.ps1 -Quick -ClerkPublishableKey pk_test_YOUR_KEY -ClerkSecretKey sk_test_YOUR_KEYWith local Elasticsearch (skips the ES prompt):
.\scripts\Install-ProjectAchilles.ps1 -WithElasticsearchIf you use the bootstrap script, skip to Section 8: Verify Installation — the script handles Sections 6 and 7 automatically.
The interactive setup wizard configures backend/.env (but does not build or launch containers).
Using Git Bash (installed with Git for Windows):
cd ~/ProjectAchilles
bash scripts/setup.shThe wizard will:
- Detect Docker and choose Docker mode automatically
- Ask for your Clerk keys
- Ask about Elasticsearch (choose "Skip" for now — you can configure later)
- Ask about the test repository and GitHub token
- Generate secure secrets (SESSION_SECRET, ENCRYPTION_SECRET)
- Write everything to
backend/.env
After the wizard completes, continue to Section 7 to build and start services.
If you prefer manual setup or the wizard doesn't work:
- Copy the example environment file:
copy backend\.env.example backend\.env- Open
backend\.envin a text editor (Notepad, VS Code, etc.) and set these values:
# === Required: Clerk Authentication ===
CLERK_PUBLISHABLE_KEY=pk_test_YOUR_KEY_HERE
CLERK_SECRET_KEY=sk_test_YOUR_KEY_HERE
# === Server ===
PORT=3000
NODE_ENV=production
# === CORS (for Docker deployment) ===
CORS_ORIGIN=http://localhost
# === Secrets (generate unique values — see below) ===
SESSION_SECRET=REPLACE_ME
ENCRYPTION_SECRET=REPLACE_ME- Generate secure secrets — run in PowerShell:
# Generate SESSION_SECRET
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
# Generate ENCRYPTION_SECRET
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"Or if you don't have Node.js installed locally, use Python:
python -c "import secrets; print(secrets.token_urlsafe(32))"Or use this online generator: https://generate-secret.vercel.app/32
Replace both REPLACE_ME values with the generated strings.
If you have access to a private test library repository, also set:
TESTS_REPO_URL=https://github.com/your-org/f0_library.git
GITHUB_TOKEN=ghp_YOUR_PERSONAL_ACCESS_TOKENGenerate a GitHub PAT at: https://github.com/settings/tokens (scopes: repo)
Open PowerShell in the project directory and run:
docker compose up -d --buildThis will:
- Build the backend image — installs Node.js 22, Go 1.24, compiles TypeScript (~3–5 minutes first time)
- Build the frontend image — compiles React app, creates nginx image (~2–3 minutes first time)
- Start both services — backend on port 3000, frontend on port 80
First build takes 5–10 minutes depending on your internet speed and CPU. Subsequent starts are much faster since Docker caches the build layers.
Watch the build and startup logs:
docker compose logs -fPress Ctrl+C to stop following logs (services continue running).
docker compose psYou should see both services running:
NAME STATUS PORTS
projectachilles-backend-1 Up (healthy) 0.0.0.0:3000->3000/tcp
projectachilles-frontend-1 Up 0.0.0.0:80->80/tcp
The backend has a health check — it may show
(health: starting)for up to 90 seconds before becoming(healthy).
Open your browser and navigate to:
You should see the Clerk sign-in page. Sign in with the method you configured (email, Google, etc.).
Open PowerShell and run:
# Health check (should return JSON with status: "ok")
curl http://localhost:3000/api/health
# Or via the frontend proxy
curl http://localhost/api/healthExpected response:
{
"status": "ok",
"service": "ProjectAchilles",
"version": "1.0.0",
"timestamp": "2026-02-08T..."
}After signing in, check each module:
| Module | What to verify |
|---|---|
| Browser | Test library loads (if test repo configured) |
| Analytics | Shows setup/configuration page (configure Elasticsearch later) |
| Agents | Agent management page loads |
| Settings | Certificate management and build settings accessible |
To enable the Analytics dashboard with sample data, start the Elasticsearch profile:
docker compose --profile elasticsearch up -dThis adds:
- Elasticsearch 8.17 on
localhost:9200(~2 GB RAM) - Seed container that loads ~1,000 sample test results then exits
Wait about 60 seconds for Elasticsearch to start and the seed data to load, then verify:
curl http://localhost:9200/_cluster/healthThe analytics module will auto-detect the local Elasticsearch instance. If not, configure it via Settings → Analytics in the UI, or set these in backend/.env and restart:
ELASTICSEARCH_NODE=http://elasticsearch:9200
ELASTICSEARCH_INDEX_PATTERN=achilles-results-*Then restart the backend:
docker compose restart backenddocker compose downdocker compose --profile elasticsearch downdocker compose up -ddocker compose --profile elasticsearch down -vWarning: The
-vflag deletes all persistent data (agent database, certificates, Elasticsearch data, build caches). Only use this for a clean reinstall.
# All services
docker compose logs -f
# Backend only
docker compose logs -f backend
# Frontend only
docker compose logs -f frontendTo update to the latest version:
# Pull latest code
git pull origin main
# Rebuild and restart
docker compose up -d --buildIf there are database schema changes, the backend handles migrations automatically on startup.
Symptom: Bind for 0.0.0.0:80 failed: port is already allocated
Windows services that commonly use port 80: IIS, Apache, Skype, World Wide Web Publishing Service.
Fix — find and stop the conflicting service:
netstat -ano | findstr :80
# Note the PID, then:
tasklist /FI "PID eq <PID>"Fix — or change the port: Edit docker-compose.yml and change the frontend port mapping:
frontend:
ports:
- "8080:80" # Changed from "80:80"Then access the dashboard at http://localhost:8080 instead.
Same approach as above. Common culprits: Node.js dev servers, React dev servers.
netstat -ano | findstr :3000# Check backend logs for errors
docker compose logs backend
# Common causes:
# 1. Missing or invalid Clerk keys in backend/.env
# 2. Syntax error in .env file
# 3. Port conflictSymptom: Frontend container fails to start with errors like:
/docker-entrypoint.sh: line 2: $'\r': command not found
Fix:
# In Git Bash:
sed -i 's/\r$//' frontend/docker-entrypoint.sh
# Then rebuild:
docker compose up -d --build frontendPrevent future issues:
# Set Git to not convert line endings
git config --global core.autocrlf input
# Or add a .gitattributes rule (already included in the repo):
# *.sh text eol=lf# Clean unused Docker resources
docker system prune -a
# Check Docker disk usage
docker system df- Ensure Docker Desktop is running (check system tray for the whale icon)
- If Docker Desktop won't start, restart your computer
- Verify WSL2 is working:
wsl --status
Docker on Windows can be resource-intensive. Recommendations:
- In Docker Desktop → Settings → Resources → Advanced:
- Reduce memory if you're not using Elasticsearch
- Set CPU limit to half your cores
- Store the project in the Windows filesystem (
C:\Users\...), not inside WSL (\\wsl$\...) - Close other heavy applications during Docker builds
Symptom: achilles-es container keeps restarting.
docker compose --profile elasticsearch logs elasticsearchCommon fixes:
- Insufficient memory — Elasticsearch needs at least 2 GB. Increase Docker memory in Settings.
- vm.max_map_count too low — Open WSL terminal and run:
wsl -d docker-desktop
sysctl -w vm.max_map_count=262144To make it permanent, add to /etc/sysctl.conf inside WSL.
Cannot access http://localhost
- Check services are running:
docker compose ps - Check Windows Firewall isn't blocking Docker
- Try
http://127.0.0.1instead ofhttp://localhost - Disable any VPN software temporarily
- Verify
CLERK_PUBLISHABLE_KEYandCLERK_SECRET_KEYinbackend/.envare correct - In the Clerk dashboard, ensure your application's authorized origins include
http://localhost - Check backend logs:
docker compose logs backend | findstr -i clerk
When running with Docker Compose, the services are networked as follows:
┌──────────────────────────────────────────────────────────┐
│ Docker Network │
│ │
│ ┌──────────────┐ ┌──────────────────────────┐ │
│ │ Frontend │────────▶│ Backend │ │
│ │ (nginx) │ /api/ │ (Express + Go + SQLite)│ │
│ │ Port 80 │ /ws │ Port 3000 │ │
│ └──────┬───────┘ └──────────┬───────────────┘ │
│ │ │ │
│ │ ┌─────────▼──────────────┐ │
│ │ │ Elasticsearch (opt) │ │
│ │ │ Port 9200 │ │
│ │ └────────────────────────┘ │
└──────────┼───────────────────────────────────────────────┘
│
┌──────▼──────┐
│ Browser │
│ localhost:80 │
└─────────────┘
| Volume | Purpose | Location inside container |
|---|---|---|
achilles-data |
Agent database, certificates, settings | /root/.projectachilles |
repo-cache |
Cached test library (Git) | /app/data |
go-cache |
Go module cache (faster builds) | /root/go |
esdata |
Elasticsearch indices (if enabled) | /usr/share/elasticsearch/data |
These volumes persist across container restarts and rebuilds. Only docker compose down -v removes them.
| Action | Command |
|---|---|
| Start services | docker compose up -d |
| Start with Elasticsearch | docker compose --profile elasticsearch up -d |
| Stop services | docker compose down |
| View logs | docker compose logs -f |
| Rebuild after code changes | docker compose up -d --build |
| Check service health | docker compose ps |
| Full reset (deletes data) | docker compose down -v |
| API health check | curl http://localhost:3000/api/health |
| Open dashboard | http://localhost |
After installation is complete:
- Configure the test library — Go to Settings in the UI to connect a Git repository with security tests
- Set up Elasticsearch — Either use the local Docker profile or connect to Elastic Cloud for analytics
- Upload certificates — Go to Settings → Certificates to upload code signing certificates for Windows binary signing
- Deploy agents — Create enrollment tokens in the Agents module and deploy the Go agent to target endpoints
- Explore the API — See the API Reference for all available endpoints