Skip to content

paradisecy/fcctl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🔥 fcctl

Serverless Firecracker — Lambda-like autoscaling for any Docker image

TypeScript Firecracker License: MIT

Point fcctl serve at any Docker image and get an autoscaling fleet of Firecracker microVMs with sub-20ms warm starts, per-slot snapshot caching, and a real-time dashboard.


Demo

$ fcctl serve my-api --port 8080 --max 20

╭───────────────────────────────────────────────────────────────────╮
│ 🔥 fcctl gateway  http://localhost:8080  image=my-api max=20     │
╰───────────────────────────────────────────────────────────────────╯
Instances: 3 idle 2 busy 5 paused 1 booting
Requests: 2 active / 1,847 total
Starts: 4 cold (1.2s) 38 warm (9ms) 156 snapshot (473ms)
Dashboard: http://localhost:8080/dashboard

One command. Zero config. Your Docker app runs inside hardware-isolated microVMs that boot in milliseconds.


Table of Contents


Features

  • 🚀 Lambda-like Autoscaling — Pool scales from min to max based on request load, scales down on idle
  • ⚡ Sub-20ms Warm Starts — Paused VMs resume from memory in ~9ms
  • 📸 Per-Slot Snapshot Caching — First boot creates a snapshot; subsequent boots restore in ~300ms instead of cold-booting in ~1.5s
  • 🐳 Docker Native — Any Docker image becomes a Firecracker microVM rootfs automatically
  • 🔒 Hardware Isolation — Each request runs in its own KVM-backed VM, not a container
  • 📊 Real-time Dashboard — React SPA with live instance states, request metrics, and event stream via SSE
  • 🌐 Reverse Proxy — HTTP gateway with request queuing, health checks, and connection draining
  • 🔄 Lifecycle Managementrun, pause, resume, stop, delete for individual VMs
  • 📈 Built-in Benchmarks — Cold / warm / snapshot boot timing with percentile breakdowns
  • 🧪 Stress Test Suite — 5-phase autoscaling exerciser (ramp → sustain → burst → cooldown → second burst)

Architecture

                          ┌────────────────────────────────┐
   HTTP Request ──────────►      fcctl gateway             │
                          │                                │
                          │  ┌──────────────────────────┐  │
                          │  │     Pool Manager         │  │
                          │  │                          │  │
                          │  │  acquire() logic:        │  │
                          │  │  1. idle VM? → use it    │  │
                          │  │  2. resume paused (9ms)  │  │
                          │  │  3. snapshot restore     │  │
                          │  │  4. cold boot            │  │
                          │  │  5. queue (at capacity)  │  │
                          │  └──────────┬───────────────┘  │
                          │             │                   │
                          └─────────────┼───────────────────┘
                                        │
               ┌────────────────────────┼────────────────────────┐
               │                        │                        │
        ┌──────▼──────┐          ┌──────▼──────┐          ┌──────▼──────┐
        │  microVM 1  │          │  microVM 2  │          │  microVM N  │
        │ 172.16.0.6  │          │ 172.16.0.10 │          │ 172.16.0.xx │
        │  tap_fc1    │          │  tap_fc2    │          │  tap_fcN    │
        │ ┌─────────┐ │          │ ┌─────────┐ │          │ ┌─────────┐ │
        │ │ Your App │ │          │ │ Your App │ │          │ │ Your App │ │
        │ │ :3000    │ │          │ │ :3000    │ │          │ │ :3000    │ │
        │ └─────────┘ │          │ └─────────┘ │          │ └─────────┘ │
        └─────────────┘          └─────────────┘          └─────────────┘
            KVM/VT-x                 KVM/VT-x                 KVM/VT-x

Each VM runs on its own TAP interface with NAT routing. The gateway proxies requests and adds response headers (X-Instance-Id, X-Latency-Ms) for observability.


Quick Start

Prerequisites

  • Linux with KVM support (/dev/kvm must exist)
  • Docker (for building rootfs from images)
  • Node.js 20+
  • Root access (Firecracker requires /dev/kvm and TAP networking)

Install

git clone https://github.com/paradisecy/fcctl.git
cd fcctl
npm install
npm run build
npm link    # installs 'fcctl' globally

Run a single VM

# Boot an Express app in a Firecracker microVM
fcctl run my-docker-image --name my-app --mem 256

# Check it's running
fcctl list

# Get details
fcctl info my-app

# Pause (freezes state, near-zero resource usage)
fcctl pause my-app

# Resume (~9ms)
fcctl resume my-app

# Clean up
fcctl stop my-app
fcctl delete my-app

Run as a serverless gateway

# Start autoscaling gateway — any Docker image works
fcctl serve my-api --port 8080 --min 2 --max 20 --idle-timeout 30

# Requests are automatically routed to VMs
curl http://localhost:8080/
curl http://localhost:8080/api/users

# Watch the dashboard
open http://localhost:8080/dashboard

CLI Reference

Command Alias Description
fcctl run <image> Create and boot a VM from a Docker image
fcctl list ls List all VMs
fcctl info <id|name> Show VM details
fcctl pause <id|name> sleep Pause a running VM
fcctl resume <id|name> wake Resume a paused VM
fcctl stop <id|name> Stop a running VM
fcctl delete <id|name> rm Delete a VM (--force to kill first)
fcctl serve <image> Start serverless gateway
fcctl help -h Show help

fcctl run flags

Flag Default Description
--name auto-generated Custom VM name
--cpus 1 vCPU count
--mem 256 Memory in MiB
--disk-size 1024 Rootfs disk size in MiB

fcctl serve flags

Flag Default Description
--port 8080 Gateway listen port
--app-port 3000 Guest application port
--mem 128 Memory per VM in MiB
--concurrency 1 Max concurrent requests per VM
--min 1 Minimum pool size (always warm)
--max 10 Maximum pool size
--idle-timeout 30 Seconds before idle VM is paused
--scale-down 300 Seconds before paused VM is destroyed

Gateway & Autoscaling

The gateway implements a 5-tier request acquisition strategy:

  1. Idle VM — Route immediately (0ms overhead)
  2. Busy VM with capacity — If concurrency > 1, reuse busy VMs
  3. Resume paused VM — Warm start from memory (~9ms)
  4. Snapshot restore — Boot from cached snapshot (~300-500ms)
  5. Cold boot — Full boot from rootfs (~1.5s)
  6. Queue — Wait for capacity if at --max

Lifecycle

cold boot ──► idle ──► busy ──► idle ──► paused ──► destroyed
                ▲        │        │         │
                │        ▼        │         ▼
                └── request ──────┘     scale-down
                                        (if > min)

HTTP Proxy

Every proxied response includes headers:

X-Instance-Id: a1b2c3d4
X-Instance-Ip: 172.16.0.6
X-Latency-Ms: 12

API Endpoints

Endpoint Method Description
/api/status GET Pool stats, instances, config
/api/instances GET Detailed instance list
/api/events GET SSE stream (real-time updates)
/dashboard GET Web dashboard UI
/* ANY Proxied to VM pool

Snapshot Engine

The snapshot system eliminates cold boots after the first run on each slot.

How it works

  1. First boot on slot N: Full cold boot → app becomes healthy → VM is paused → memory + state saved as snapshot
  2. Subsequent boots on slot N: Copy rootfs to stable slot path → load snapshot → resume VM → app is immediately ready

Per-slot design

Firecracker snapshots bake the TAP interface name and rootfs path into the VM state. fcctl uses stable per-slot paths so snapshots are reusable:

~/.firecracker-orchestrator/
├── slot-rootfs/
│   ├── slot-1.ext4          # Stable path for slot 1
│   ├── slot-2.ext4          # Stable path for slot 2
│   └── ...
├── snapshots/
│   ├── fc-node-hello-1024mb-3000-slot1/
│   │   ├── snapshot.bin     # VM state (17KB)
│   │   ├── mem.bin          # Memory (sparse: 85MB of 256MB)
│   │   └── rootfs.ext4     # Disk at snapshot time
│   └── ...
└── rootfs-cache/
    └── fc-node-hello-1024mb.ext4   # Base rootfs (built once from Docker)

Optimizations

  • Parallel copies: Rootfs and mem.bin are copied concurrently while Firecracker process starts
  • Sparse memory: mem.bin uses sparse copy — only written pages are copied (typically 85MB of 256MB)
  • Hardlinked state: snapshot.bin is hardlinked (17KB, read-only)
  • Skip health check: Snapshot was taken after app was healthy, so health check is skipped on restore

Dashboard

The gateway includes a built-in real-time dashboard at /dashboard:

  • Stats cards: Total instances, idle/busy/paused counts, request throughput, start type breakdowns
  • Pool bar: Color-coded visualization of instance states
  • Instance table: Per-VM details (ID, IP, state, requests, boot time)
  • Event stream: Live log of pool events (scale-up, state changes, request routing)
  • Configuration panel: Current gateway settings

The dashboard uses Server-Sent Events (SSE) for real-time updates with no polling.


Benchmarks

Run the built-in benchmark to measure boot performance:

npx tsx src/benchmark.ts [image] [cold_runs] [warm_runs] [snapshot_runs]

Typical results (128MB memory, fc-node-hello)

Start Type Avg Latency Description
Cold boot ~1,500ms Full boot: rootfs copy + networking + VM start + health check
Snapshot restore ~300ms Restore from cached snapshot + resume
Warm resume ~9ms Resume paused VM from memory

Warm resume is ~160x faster than cold boot. Snapshot restore is ~5x faster than cold boot.


Stress Testing

Run the 5-phase stress test to exercise autoscaling:

npx tsx src/stress.ts http://localhost:9090 --max-concurrency 6 --duration 60

Phases

Phase What it does
1. Ramp Up Gradually increases concurrency (1 → 3 → 5 → max)
2. Sustained Load Holds max concurrency for several waves
3. Burst Spikes to 2x max concurrency (tests queuing)
4. Cool Down Watches idle → pause → scale-down progression
5. Second Burst Tests snapshot restores after scale-down

Sample output

╔════════════════════════════════════════════════════════════╗
║                  AGGRESSIVE STRESS TEST RESULTS           ║
╠════════════════════════════════════════════════════════════╣
║  Total requests:    87                                    ║
║  Succeeded:         87                                    ║
║  Failed:            0                                     ║
╠════════════════════════════════════════════════════════════╣
║  Latency p50:       39ms                                  ║
║  Latency p95:       11.59s                                ║
║  Latency p99:       12.09s                                ║
╠════════════════════════════════════════════════════════════╣
║  Cold starts:       0 (avg 0ms)                           ║
║  Warm starts:       7 (avg 9ms)                           ║
║  Snapshot starts:   10 (avg 473ms)                        ║
╚════════════════════════════════════════════════════════════╝

Configuration

Environment

Requirement Version
Linux kernel 4.14+ with KVM
Firecracker v1.14.2 (auto-downloaded)
Guest kernel vmlinux-6.1.155 (auto-downloaded)
Node.js 20+
Docker Any recent version

Network layout

Each VM slot N is assigned a /30 subnet:

Slot TAP Host IP Guest IP
1 tap_fc1 172.16.0.5 172.16.0.6
2 tap_fc2 172.16.0.9 172.16.0.10
3 tap_fc3 172.16.0.13 172.16.0.14
... ... ... ...
63 tap_fc63 172.16.0.253 172.16.0.254

Up to 63 concurrent VMs are supported.

File layout

~/.firecracker-orchestrator/
├── bin/
│   ├── firecracker          # Firecracker binary (auto-downloaded)
│   └── kernel/
│       └── vmlinux-6.1.155  # Guest kernel (auto-downloaded)
├── vms/
│   └── {vmId}/
│       ├── metadata.json    # VM config & state
│       ├── firecracker.sock # API socket
│       ├── rootfs.ext4      # VM disk
│       └── firecracker.log  # FC logs
├── rootfs-cache/            # Built rootfs images (per Docker image)
├── slot-rootfs/             # Stable per-slot rootfs paths
└── snapshots/               # Per-slot snapshot cache

Project Structure

src/
├── cli.tsx                  # Entry point & command router (React Ink)
├── benchmark.ts             # Cold/warm/snapshot boot benchmarks
├── stress.ts                # 5-phase autoscaling stress test
├── commands/
│   ├── run.tsx              # Create & boot VM
│   ├── list.tsx             # List VMs
│   ├── info.tsx             # VM details
│   ├── pause.tsx            # Pause VM
│   ├── resume.tsx           # Resume VM
│   ├── stop.tsx             # Stop VM
│   ├── delete.tsx           # Delete VM
│   └── serve.tsx            # Serverless gateway
├── gateway/
│   ├── pool.ts              # Autoscaling pool manager
│   └── server.ts            # HTTP reverse proxy + API + SSE
├── lib/
│   ├── config.ts            # Paths, versions, constants
│   ├── docker.ts            # Docker → ext4 rootfs conversion
│   ├── download.ts          # Asset download (FC binary, kernel)
│   ├── firecracker-api.ts   # Firecracker socket API client
│   ├── network.ts           # TAP/NAT networking
│   ├── snapshot.ts          # Per-slot snapshot engine
│   └── vm.ts                # VM metadata & filesystem
└── ui/
    └── dashboard.ts         # React 19 dashboard SPA (inline HTML)

Roadmap

  • TLS termination — HTTPS support with auto-cert via Let's Encrypt
  • Authentication — API key and JWT auth for gateway and management API
  • Rate limiting — Per-client request throttling
  • Structured logging — JSON logs with request correlation IDs
  • Metrics export — Prometheus /metrics endpoint with histograms and counters
  • Graceful drain — Complete in-flight requests before shutdown
  • Request retry — Automatic retry on VM failure with circuit breaker
  • Crash recovery — Detect and clean up orphaned VMs, TAPs, and sockets on startup
  • Resource cleanup — GC for stale snapshots, rootfs cache, and leaked TAP interfaces
  • Config file — YAML/TOML config as alternative to CLI flags
  • Rolling updates — Swap image version with zero-downtime drain
  • Multi-host clustering — Distribute VMs across multiple physical hosts
  • Persistent volumes — Mount host directories into VMs for stateful workloads
  • CPU pinning — Pin vCPUs to physical cores for consistent performance
  • cgroup limits — Per-VM CPU and I/O bandwidth controls
  • Network isolation — Per-VM firewall rules and network policies
  • Image registry — Pull from remote registries (ECR, GCR, Docker Hub)
  • WebSocket proxying — Full duplex WebSocket support through the gateway
  • fcctl ps — Inspect running gateway from a separate terminal
  • fcctl logs — Stream VM and gateway logs
  • Unit & integration tests — Comprehensive test suite with CI pipeline

License

MIT


Built with 🔥 by ParadiseCy

About

🔥 Serverless Firecracker — Lambda-like autoscaling for any Docker image

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors