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

  1. Why Migrate?
  2. Migration Strategy
  3. Step-by-Step Migration
  4. Command Mapping
  5. Architecture Changes
  6. Common Patterns
  7. Troubleshooting

Why Migrate?

MetricDockerRun 2.0Improvement
Startup time5-10s<10ms500-1000x
Image size50-500MB<5MB10-100x
Memory usage256MB+<10MB25x+
Build time1-5min<10s6-30x
Cold start500ms+<10ms50x+

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

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 CommandRun 2.0 EquivalentNotes
docker-compose uprun v2 dev<10ms startup for WASI
docker-compose buildrun v2 build<10s builds
docker-compose downrun stopStops all components
docker-compose logsrun v2 dev (shows logs)Unified logs
docker-compose psrun v2 infoComponent status
docker-compose execrun v2 execExecute in component
docker buildrun v2 buildNo Dockerfile needed
docker runrun v2 execDirect execution
docker pushrun v2 deploy --target registryWASI registry
docker pullrun v2 installDownload 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.yml with run v2 compose analyze
  • Generate run.toml with run 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

  1. Start with hybrid mode (WASI + Docker bridge)
  2. Gradually migrate stateful services to WASI
  3. Optimize component size and startup time
  4. Deploy to edge/serverless if applicable
  5. Remove Docker entirely when ready

Support