From 96df2a2a52b546bef1e2d84fa72aabf371cc616c Mon Sep 17 00:00:00 2001 From: A <258483684+la14-1@users.noreply.github.com> Date: Sat, 21 Feb 2026 19:59:18 -0800 Subject: [PATCH] feat: add dedicated CPU VM tiers for Fly.io (#1664) Add performance-1x/2x/4x (dedicated vCPU) options alongside existing shared CPU tiers. Thread cpuKind through to the Machines API cpu_kind field so users can provision dedicated VMs for consistent performance. Co-authored-by: lab <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) --- cli/src/__tests__/fly.test.ts | 19 ++++++++----------- cli/src/fly/fly.ts | 23 ++++++++++++++++------- cli/src/fly/main.ts | 6 +++--- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/cli/src/__tests__/fly.test.ts b/cli/src/__tests__/fly.test.ts index d661405e..05d68598 100644 --- a/cli/src/__tests__/fly.test.ts +++ b/cli/src/__tests__/fly.test.ts @@ -142,26 +142,23 @@ describe("fly/lib/fly", () => { }); describe("FLY_VM_TIERS", () => { - it("has 3 tiers", () => { - expect(FLY_VM_TIERS.length).toBe(3); + it("has shared and dedicated tiers", () => { + expect(FLY_VM_TIERS.length).toBe(6); + expect(FLY_VM_TIERS.filter((t) => t.cpuKind === "shared").length).toBe(3); + expect(FLY_VM_TIERS.filter((t) => t.cpuKind === "performance").length).toBe(3); }); - it("default tier is shared-cpu-2x", () => { - expect(DEFAULT_VM_TIER.id).toBe("shared-cpu-2x"); + it("default tier is performance-2x", () => { + expect(DEFAULT_VM_TIER.id).toBe("performance-2x"); + expect(DEFAULT_VM_TIER.cpuKind).toBe("performance"); expect(DEFAULT_VM_TIER.cpus).toBe(2); expect(DEFAULT_VM_TIER.memoryMb).toBe(4096); }); - it("tiers have increasing resources", () => { - for (let i = 1; i < FLY_VM_TIERS.length; i++) { - expect(FLY_VM_TIERS[i].cpus).toBeGreaterThan(FLY_VM_TIERS[i - 1].cpus); - expect(FLY_VM_TIERS[i].memoryMb).toBeGreaterThan(FLY_VM_TIERS[i - 1].memoryMb); - } - }); - it("all tiers have required fields", () => { for (const tier of FLY_VM_TIERS) { expect(tier.id).toBeTruthy(); + expect(tier.cpuKind === "shared" || tier.cpuKind === "performance").toBe(true); expect(tier.cpus).toBeGreaterThan(0); expect(tier.memoryMb).toBeGreaterThan(0); expect(tier.label).toBeTruthy(); diff --git a/cli/src/fly/fly.ts b/cli/src/fly/fly.ts index 55451ba0..5b490190 100644 --- a/cli/src/fly/fly.ts +++ b/cli/src/fly/fly.ts @@ -19,24 +19,31 @@ const FLY_DASHBOARD_URL = "https://fly.io/dashboard"; // ─── VM Size Tiers ────────────────────────────────────────────────────────── +export type CpuKind = "shared" | "performance"; + export interface VmTier { id: string; + cpuKind: CpuKind; cpus: number; memoryMb: number; label: string; } export const FLY_VM_TIERS: VmTier[] = [ - { id: "shared-cpu-1x", cpus: 1, memoryMb: 1024, label: "1 shared vCPU, 1 GB (~$3/mo)" }, - { id: "shared-cpu-2x", cpus: 2, memoryMb: 4096, label: "2 shared vCPUs, 4 GB (~$12/mo)" }, - { id: "shared-cpu-4x", cpus: 4, memoryMb: 8192, label: "4 shared vCPUs, 8 GB (~$51/mo)" }, + { id: "shared-cpu-1x", cpuKind: "shared", cpus: 1, memoryMb: 1024, label: "1 shared vCPU, 1 GB (~$3/mo)" }, + { id: "shared-cpu-2x", cpuKind: "shared", cpus: 2, memoryMb: 4096, label: "2 shared vCPUs, 4 GB (~$12/mo)" }, + { id: "shared-cpu-4x", cpuKind: "shared", cpus: 4, memoryMb: 8192, label: "4 shared vCPUs, 8 GB (~$51/mo)" }, + { id: "performance-1x", cpuKind: "performance", cpus: 1, memoryMb: 2048, label: "1 dedicated vCPU, 2 GB (~$32/mo)" }, + { id: "performance-2x", cpuKind: "performance", cpus: 2, memoryMb: 4096, label: "2 dedicated vCPUs, 4 GB (~$63/mo)" }, + { id: "performance-4x", cpuKind: "performance", cpus: 4, memoryMb: 8192, label: "4 dedicated vCPUs, 8 GB (~$126/mo)" }, ]; -export const DEFAULT_VM_TIER = FLY_VM_TIERS[1]; // shared-cpu-2x +export const DEFAULT_VM_TIER = FLY_VM_TIERS[4]; // performance-2x // ─── Server Options ───────────────────────────────────────────────────────── export interface ServerOptions { + cpuKind: CpuKind; cpus: number; memoryMb: number; volumeId?: string; @@ -556,14 +563,16 @@ async function createApp(name: string): Promise { async function createMachine( name: string, region: string, + cpuKind: CpuKind, cpus: number, vmMemory: number, volumeId?: string, ): Promise { - logStep(`Creating Fly.io machine (region: ${region}, ${cpus} vCPU, ${vmMemory}MB)...`); + const kindLabel = cpuKind === "performance" ? "dedicated" : "shared"; + logStep(`Creating Fly.io machine (region: ${region}, ${cpus} ${kindLabel} vCPU, ${vmMemory}MB)...`); const config: Record = { image: "ubuntu:24.04", - guest: { cpu_kind: "shared", cpus, memory_mb: vmMemory }, + guest: { cpu_kind: cpuKind, cpus, memory_mb: vmMemory }, init: { exec: ["/bin/sleep", "inf"] }, auto_destroy: false, }; @@ -689,7 +698,7 @@ export async function createServer(name: string, opts: ServerOptions): Promise { if (process.env.FLY_VM_MEMORY) { const memoryMb = parseInt(process.env.FLY_VM_MEMORY, 10); const tier = FLY_VM_TIERS.find((t) => t.memoryMb === memoryMb) || DEFAULT_VM_TIER; - return { cpus: tier.cpus, memoryMb: tier.memoryMb }; + return { cpuKind: tier.cpuKind, cpus: tier.cpus, memoryMb: tier.memoryMb }; } if (process.env.SPAWN_NON_INTERACTIVE === "1") { - return { cpus: DEFAULT_VM_TIER.cpus, memoryMb: DEFAULT_VM_TIER.memoryMb }; + return { cpuKind: DEFAULT_VM_TIER.cpuKind, cpus: DEFAULT_VM_TIER.cpus, memoryMb: DEFAULT_VM_TIER.memoryMb }; } // VM size prompt @@ -42,7 +42,7 @@ async function promptVmOptions(): Promise { const tierId = await selectFromList(tierItems, "VM size", DEFAULT_VM_TIER.id); const selectedTier = FLY_VM_TIERS.find((t) => t.id === tierId) || DEFAULT_VM_TIER; - return { cpus: selectedTier.cpus, memoryMb: selectedTier.memoryMb }; + return { cpuKind: selectedTier.cpuKind, cpus: selectedTier.cpus, memoryMb: selectedTier.memoryMb }; } async function main() {