Docker to Run 2.0 Migration Guide
February 5, 2026 ยท View on GitHub
Run 2.0 is experimental and opt-in. Use run v2 to access the v2 commands.
This guide helps you migrate from Docker/Docker Compose to Run 2.0.
Table of Contents
- Why Migrate?
- Migration Strategy
- Step-by-Step Migration
- Command Mapping
- Architecture Changes
- Common Patterns
- Troubleshooting
Why Migrate?
| Metric | Docker | Run 2.0 | Improvement |
|---|---|---|---|
| Startup time | 5-10s | <10ms | 500-1000x |
| Image size | 50-500MB | <5MB | 10-100x |
| Memory usage | 256MB+ | <10MB | 25x+ |
| Build time | 1-5min | <10s | 6-30x |
| Cold start | 500ms+ | <10ms | 50x+ |
Additional Benefits:
- No Docker daemon required
- Capability-based security (no root)
- Cross-platform (Linux, macOS, Windows)
- Reproducible builds
- Component isolation
- Multi-language interop via WIT
Migration Strategy
Phase 1: Hybrid Mode (Recommended Start)
Keep Docker for stateful services (databases, caches), migrate application logic to WASI.
Docker Compose -> Run 2.0 Hybrid
App (Docker) -> App (WASI) <10ms
DB (Docker) -> DB (Docker) Keep
Cache -> Cache Keep
Phase 2: Pure WASI (Goal)
Migrate everything to WASI components.
Run 2.0 Hybrid -> Run 2.0 Pure
App (WASI) -> App (WASI)
DB (Docker) -> DB (WASI) All WASI
Cache -> Cache
Step-by-Step Migration
Step 1: Analyze Your Docker Setup
# Install Run 2.0
curl -sSL https://run.esubalew.et/install.sh | bash
# Analyze compose file
cd your-project
run v2 compose analyze docker-compose.yml
Output:
Services:
OK web-api -> WASI component (can migrate)
OK worker -> WASI component (can migrate)
WARN postgres -> Docker bridge (stateful, keep Docker)
WARN redis -> Docker bridge (stateful, keep Docker)
Recommendation: Hybrid mode (2 WASI + 2 Docker)
Estimated startup: 5s (Docker) + 10ms (WASI)
Estimated size: 128MB (Docker) + 3MB (WASI)
Step 2: Auto-Generate run.toml
run v2 compose migrate docker-compose.yml run.toml
This creates a run.toml with:
- Application services as WASI components
- Stateful services as Docker bridge
Before: docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- "8080:8080"
environment:
DATABASE_URL: postgres://db:5432/mydb
depends_on:
- db
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
After: run.toml
[package]
name = "my-app"
version = "1.0.0"
[[component]]
name = "web"
source = "src/lib.rs"
language = "rust"
wit = "wit/http.wit"
[component.web.env]
DATABASE_URL = "postgres://localhost:5432/mydb"
[bridge.postgres]
image = "postgres:15"
ports = { 5432 = 5432 }
env = { POSTGRES_PASSWORD = "secret" }
Step 3: Convert Application to WASI Component
Option A: Rust
# Install cargo-component
cargo install cargo-component
# Initialize component
cargo component new web --lib
# Write code
cat > src/lib.rs <<'EOF'
wit_bindgen::generate!({
world: "http-handler",
exports: {
"wasi:http/handler": Handler,
},
});
struct Handler;
impl exports::wasi::http::handler::Guest for Handler {
fn handle(request: Request) -> Response {
Response::new(200, b"Hello from Run 2.0!")
}
}
EOF
# Build
cargo component build --release
Option B: Python
# Install componentize-py
pip install componentize-py
# Write code
cat > app.py <<'EOF'
def handle_request(request):
return {
"status": 200,
"body": b"Hello from Run 2.0!"
}
EOF
# Build
componentize-py -d http.wit -o app.wasm app.py
Option C: TypeScript
# Install jco
npm install -g @bytecodealliance/jco
# Write code
cat > index.ts <<'EOF'
export function handleRequest(request: Request): Response {
return new Response("Hello from Run 2.0!", { status: 200 });
}
EOF
# Build
jco transpile index.ts -o app.wasm
Step 4: Test Hybrid Setup
# Start dev server
run v2 dev
# Output:
[run] Starting WASI components...
[web] OK Built in 8ms
[run] Starting Docker bridge...
[docker] postgres -> postgres:15
[run] All services ready:
[run] http://localhost:8080 (web)
# Test
curl http://localhost:8080
# Hello from Run 2.0!
Step 5: Deploy
# Production build
run v2 build --release --reproducible
# Deploy (examples)
run v2 deploy --target local
run v2 deploy --target edge --provider cloudflare
run v2 deploy --target registry
Command Mapping
| Docker Command | Run 2.0 Equivalent | Notes |
|---|---|---|
docker-compose up | run v2 dev | <10ms startup for WASI |
docker-compose build | run v2 build | <10s builds |
docker-compose down | run stop | Stops all components |
docker-compose logs | run v2 dev (shows logs) | Unified logs |
docker-compose ps | run v2 info | Component status |
docker-compose exec | run v2 exec | Execute in component |
docker build | run v2 build | No Dockerfile needed |
docker run | run v2 exec | Direct execution |
docker push | run v2 deploy --target registry | WASI registry |
docker pull | run v2 install | Download components |
Architecture Changes
Before: Docker Compose
docker-compose.yml
|-- Service A (container)
| |-- Dockerfile
| |-- node_modules/
| `-- app.js
|-- Service B (container)
| |-- Dockerfile
| |-- venv/
| `-- app.py
`-- Database (container)
`-- postgres:15
Docker Daemon (required)
|-- Network bridge
|-- Volume management
`-- Container orchestration
Issues:
- Docker daemon required
- 500MB+ images
- 5-10s startup
- Complex networking
- Root access risks
After: Run 2.0
run.toml
|-- Component A (WASI) <10ms, 2MB
|-- Component B (WASI) <10ms, 3MB
`-- Database (Docker*) 5s, 128MB
Run Runtime (no daemon)
|-- Component Model (WASI 0.2)
|-- Capability security
`-- Direct host process
*Docker bridge optional
Benefits:
- No daemon needed
- <5MB components
- <10ms startup
- Capability-based security
- Cross-platform
Common Patterns
Pattern 1: HTTP Service
Before (Dockerfile):
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "server.js"]
After (run.toml):
[[component]]
name = "api"
source = "src/lib.rs"
language = "rust"
wit = "wit/http.wit"
Pattern 2: Database Connection
Before:
services:
app:
depends_on:
- db
environment:
DATABASE_URL: postgres://db:5432/mydb
db:
image: postgres:15
After:
[[component]]
name = "app"
source = "src/lib.rs"
env = { DATABASE_URL = "postgres://localhost:5432/mydb" }
[bridge.postgres]
image = "postgres:15"
ports = { 5432 = 5432 }
Pattern 3: Multi-service Application
Before (docker-compose.yml):
services:
frontend:
build: ./frontend
ports: ["3000:3000"]
backend:
build: ./backend
ports: ["8080:8080"]
worker:
build: ./worker
After (run.toml):
[[component]]
name = "frontend"
source = "frontend/src/lib.rs"
[[component]]
name = "backend"
source = "backend/src/lib.rs"
[[component]]
name = "worker"
source = "worker/src/lib.rs"
All start in <10ms, no containers needed.
Pattern 4: Environment Variables
Before:
services:
app:
environment:
NODE_ENV: production
API_KEY: ${API_KEY}
After:
[[component]]
name = "app"
env = { NODE_ENV = "production", API_KEY = "${API_KEY}" }
[env.production]
NODE_ENV = "production"
LOG_LEVEL = "info"
Troubleshooting
Issue 1: "Docker is not available"
If you see this error but have Docker installed:
# Check Docker is running
docker info
# Enable Docker bridge in run.toml
[bridge]
enabled = true
Issue 2: Port conflicts
# Error: Port 8080 already in use
# Solution: Change port in run.toml
[[component]]
name = "web"
dev.port = 8081 # Changed from 8080
Issue 3: Missing dependencies
# Error: Cannot find module 'xyz'
# For Rust: Add to Cargo.toml
[dependencies]
xyz = "1.0"
# For Python: Add to requirements.txt
xyz==1.0.0
# For JS/TS: Add to package.json
{
"dependencies": {
"xyz": "^1.0.0"
}
}
Issue 4: WASI compatibility
Not all code can run in WASI yet. Use Docker bridge for:
- Native dependencies (e.g.,
libpq,openssl) - GPU access
- Kernel modules
- X11/GUI applications
# Keep these in Docker bridge
[bridge.legacy-service]
image = "my-legacy-app"
Issue 5: Performance regression
If WASI is slower than Docker:
# Enable release builds
run v2 build --release
# Verbose logs
run v2 dev --verbose
# Check component size
ls -lh target/wasm/*.wasm
# Optimize
[component.my-component]
opt_level = 3
strip_debug = true
Migration Checklist
- Install Run 2.0
- Analyze
docker-compose.ymlwithrun v2 compose analyze - Generate
run.tomlwithrun v2 compose migrate - Convert application code to WASI components
- Test with
run v2 dev - Verify functionality matches Docker setup
- Deploy to production
- (Optional) Remove Docker for pure WASI
Success Stories
Case 1: Microservices API
Before:
- 5 services in Docker
- 10s startup
- 800MB total images
After:
- 5 WASI components
- <10ms startup
- 8MB total size
Result: 100x smaller, 1000x faster startup
Case 2: Serverless Edge
Before:
- Node.js Lambda function
- 200MB deployment
- 300ms cold start
After:
- WASI component
- 3MB deployment
- <10ms cold start
Result: 66x smaller, 30x faster
Case 3: CI/CD Pipeline
Before:
- Docker builds in CI
- 5 min build time
- Non-reproducible
After:
- Run 2.0 builds
- <30s build time
- Reproducible (same hash)
Result: 10x faster, verifiable
Next Steps
- Start with hybrid mode (WASI + Docker bridge)
- Gradually migrate stateful services to WASI
- Optimize component size and startup time
- Deploy to edge/serverless if applicable
- Remove Docker entirely when ready
Support
- Documentation: https://run.esubalew.et/docs
- Examples:
examples/v2/docker-hybrid/ - Issues: https://github.com/esubaalew/run/issues