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) <noreply@anthropic.com>
This commit is contained in:
A 2026-02-21 19:59:18 -08:00 committed by GitHub
parent 459f97ee47
commit 96df2a2a52
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 27 additions and 21 deletions

View file

@ -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();

View file

@ -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<void> {
async function createMachine(
name: string,
region: string,
cpuKind: CpuKind,
cpus: number,
vmMemory: number,
volumeId?: string,
): Promise<string> {
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<string, unknown> = {
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<v
let machineId: string;
try {
machineId = await createMachine(name, region, opts.cpus, opts.memoryMb, volumeId);
machineId = await createMachine(name, region, opts.cpuKind, opts.cpus, opts.memoryMb, volumeId);
} catch (err) {
await cleanupOnFailure(name);
throw err;

View file

@ -29,11 +29,11 @@ async function promptVmOptions(): Promise<ServerOptions> {
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<ServerOptions> {
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() {