A scalable, distributed VM management system built with Cloudflare Workers and Arrakis. This system allows you to manage VMs through a REST API with reliable task processing using Cloudflare's global infrastructure.
This project provides a complete task queue solution for VM management operations. It decouples VM requests from execution, allowing for reliable, scalable, and asynchronous VM operations on your bare metal servers.
Key Benefits:
- 🌍 Global Scale: Leverages Cloudflare's edge network
- 🔄 Reliable Processing: Queue-based with automatic retries
- 🔒 Secure: VMs run on your infrastructure, API runs on Cloudflare
- 📊 Observable: Full task tracking and status monitoring
- ⚡ Fast: Low-latency API responses with background processing
-
API Worker (Cloudflare Workers)
- REST API for task submission and status tracking
- Stores tasks in KV storage
- Queues tasks for processing
-
Consumer Worker (Cloudflare Workers)
- Processes queue messages
- Buffers tasks for local consumers
- Manages task lifecycle
-
Local Consumer (Node.js on Bare Metal)
- Polls for VM tasks
- Executes operations via Arrakis API
- Reports completion back to API
-
Storage & Queue
- Task Storage KV: Persistent task data
- Task Buffer KV: Temporary task buffer
- Queue: Reliable message delivery
Here's how a typical VM launch request flows through the system:
- Cloudflare account with Workers enabled
- Node.js 18+ installed
- Arrakis server running on bare metal
- Wrangler CLI:
npm install -g wrangler
# Create task storage namespace
wrangler kv:namespace create "TASK_STORAGE"
wrangler kv:namespace create "TASK_STORAGE" --preview
# Create task buffer namespace
wrangler kv:namespace create "TASK_BUFFER"
wrangler kv:namespace create "TASK_BUFFER" --previewCopy the generated namespace IDs for the next step.
# Create main queue
wrangler queues create vm-task-queue
# Optional: Create dead letter queue for failed messages
wrangler queues create vm-task-dlqcd api-worker
npm install
# Update wrangler.toml with your KV namespace IDs:
# binding = "TASK_STORAGE"
# id = "your-task-storage-kv-id"
wrangler deploycd consumer-worker
npm install
# Update wrangler.toml with your KV namespace IDs:
# binding = "TASK_BUFFER"
# id = "your-task-buffer-kv-id"
wrangler deploycd local-consumer
npm install
# Configure environment
cp .env.example .env
# Edit .env with your URLs:
# CONSUMER_WORKER_URL=https://vm-consumer-worker.your-subdomain.workers.dev
# API_WORKER_URL=https://vm-api-worker.your-subdomain.workers.dev
# ARRAKIS_URL=http://127.0.0.1:8080
# Start the consumer
npm run dev# Test API Worker
curl https://vm-api-worker.your-subdomain.workers.dev/health
# Test Consumer Worker
curl https://vm-consumer-worker.your-subdomain.workers.dev/healthcurl -X POST https://vm-api-worker.your-subdomain.workers.dev/vm/tasks \
-H "Content-Type: application/json" \
-d '{
"type": "launch_vm",
"payload": {
"vmName": "test-vm-001",
"vmConfig": {
"memory": "2G"
},
"targetServer": "production"
}
}'Expected response:
{
"success": true,
"taskId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "pending",
"message": "VM launch vm task queued successfully"
}curl https://vm-api-worker.your-subdomain.workers.dev/vm/tasks/a1b2c3d4-e5f6-7890-abcd-ef1234567890/statuscurl -X POST https://vm-api-worker.your-subdomain.workers.dev/vm/tasks \
-H "Content-Type: application/json" \
-d '{
"type": "run_command",
"payload": {
"vmName": "test-vm-001",
"command": "echo Hello from VM",
"blocking": true
}
}'# Stop VM
curl -X POST https://vm-api-worker.your-subdomain.workers.dev/vm/tasks \
-H "Content-Type: application/json" \
-d '{
"type": "stop_vm",
"payload": {
"vmName": "test-vm-001"
}
}'
# Delete VM
curl -X POST https://vm-api-worker.your-subdomain.workers.dev/vm/tasks \
-H "Content-Type: application/json" \
-d '{
"type": "delete_vm",
"payload": {
"vmName": "test-vm-001"
}
}'curl https://vm-api-worker.your-subdomain.workers.dev/vm/tasks| Operation | Description | Payload |
|---|---|---|
launch_vm |
Start a new VM | vmName, vmConfig (kernel, rootfs, etc.) |
delete_vm |
Destroy a VM | vmName |
stop_vm |
Stop a running VM | vmName |
pause_vm |
Pause a running VM | vmName |
snapshot_vm |
Create VM snapshot | vmName, snapshotId |
run_command |
Execute command in VM | vmName, command, blocking |
# API Worker logs
wrangler tail --name vm-api-worker
# Consumer Worker logs
wrangler tail --name vm-consumer-worker
# Local Consumer logs (in terminal where it's running)# Queue information
wrangler queues info vm-task-queue
# Buffer statistics
curl https://vm-consumer-worker.your-subdomain.workers.dev/stats
# Ready tasks
curl https://vm-consumer-worker.your-subdomain.workers.dev/tasks/ready-
500 Internal Server Error
- Check KV namespace IDs in
wrangler.toml - Ensure queue exists:
wrangler queues info vm-task-queue
- Check KV namespace IDs in
-
Consumer Connection Failed
- Verify worker URLs in
.env - Check workers are deployed:
wrangler deployments list
- Verify worker URLs in
-
Arrakis Connection Failed
- Ensure Arrakis server is running:
curl http://127.0.0.1:8080/v1/health - Check ARRAKIS_URL in
.env
- Ensure Arrakis server is running:
-
Tasks Not Processing
- Check local consumer logs
- Verify consumer worker is receiving messages:
wrangler tail
# Test Arrakis directly
curl http://127.0.0.1:8080/v1/health
curl http://127.0.0.1:8080/v1/vms
# Check worker deployments
wrangler deployments list
# View KV contents
wrangler kv:key list --binding=TASK_STORAGE --prefix=task:| Variable | Description | Default |
|---|---|---|
CONSUMER_WORKER_URL |
Consumer Worker URL | Required |
API_WORKER_URL |
API Worker URL | Required |
ARRAKIS_URL |
Arrakis server URL | http://127.0.0.1:8080 |
DEFAULT_KERNEL |
Default kernel path | /opt/arrakis/vmlinux.bin |
DEFAULT_ROOTFS |
Default rootfs path | /opt/arrakis/rootfs.ext4 |
DEFAULT_INITRAMFS |
Default initramfs path | /opt/arrakis/initramfs.cpio.gz |
In consumer-worker/wrangler.toml:
[[queues.consumers]]
queue = "vm-task-queue"
max_batch_size = 10 # Process up to 10 messages at once
max_batch_timeout = 30 # Wait max 30s to fill batch
max_retries = 3 # Retry failed messages 3 times
dead_letter_queue = "vm-task-dlq" # Failed messages go hereThis project is open source and available under the MIT License.

