Choosing the right machine size on Fly.io isn’t just about picking the biggest one to be safe; it’s about understanding how your application actually uses resources and how Fly.io provisions them.
Let’s see a simple web app in action. Imagine we have a basic Go web server that just returns "Hello, Fly!" and logs incoming requests.
package main
import (
"fmt"
"log"
"net/http"
"os"
)
func handler(w http.ResponseWriter, r *http.Request) {
log.Printf("Received request from %s: %s %s", r.RemoteAddr, r.Method, r.URL.Path)
fmt.Fprintln(w, "Hello, Fly!")
}
func main() {
http.HandleFunc("/", handler)
port := os.Getenv("PORT")
if port == "" {
port = "8080" // Default to 8080 if PORT is not set
}
log.Printf("Starting server on port %s", port)
if err := http.ListenAndServe(":"+port, nil); err != nil {
log.Fatal(err)
}
}
We can build this and deploy it to Fly.io. After deployment, we’d get a URL like my-app-1234.fly.dev. When we hit this URL, the Go app logs the request. Now, let’s consider scaling.
Fly.io offers different machine sizes, often referred to by their CPU and RAM. For instance, you might see options like shared-cpu-1x (512MB RAM, 1 vCPU), shared-cpu-2x (1GB RAM, 2 vCPU), or even dedicated CPU options. The key is that these aren’t guaranteed dedicated resources in the same way a traditional VPS might offer.
The shared-cpu types mean your application is running on a hypervisor alongside other tenants. Fly.io uses a scheduler to give your VM a slice of CPU time. When you request a shared-cpu-1x, you’re not getting a dedicated 1 vCPU; you’re getting access to up to the equivalent of 1 vCPU’s worth of processing time, shared with others on the same underlying hardware. This is why smaller sizes can be surprisingly performant for I/O-bound or low-CPU-bursting applications, but can suffer from "noisy neighbor" issues or unpredictable latency under heavy CPU load.
The problem Fly.io solves here is the operational overhead of managing infrastructure. Instead of provisioning VMs, configuring networks, and handling OS updates, you focus on your application. Fly.io abstracts this away, allowing you to deploy containers globally with simple commands. The flyctl CLI is your primary interface for managing deployments, scaling, and configuration.
When you flyctl deploy, Fly.io builds your container image (if needed) and then schedules it onto their global fleet of servers. The flyctl.toml file is where you define your application’s configuration, including resource requests.
app = "my-app-1234"
primary_region = "ord"
[deploy]
# Example: request a specific machine size
# This tells Fly.io to aim for a machine with at least 1GB RAM and 2 vCPUs
# for this app, if available. For shared CPUs, it means a certain slice of CPU time.
# For dedicated, it means that much dedicated CPU.
# The actual resources are managed by Fly's scheduler.
[deploy.strategy]
type = "rolling" # Or "bluegreen"
[build]
image = "your-dockerhub-username/my-app:latest"
[services]
processes = ["app"]
http_options = { response_timeout_seconds = 90 }
[[services.ports]]
port = 80
handlers = ["http"]
[[services.tcp_checks]]
interval = 20000
timeout = 2000
The mental model for Fly.io machine sizing revolves around allocations rather than reservations. For shared CPUs, you’re asking for a certain share of a CPU and RAM, which the hypervisor then arbitrates. For dedicated CPUs, you’re asking for a more consistent, non-shared slice. The cost scales with the requested resources and the number of machines.
Here’s the counterintuitive part: sometimes, a shared-cpu-1x machine can feel more responsive for certain workloads than a shared-cpu-2x if the underlying hardware is less contended for the 1x slot. This is because the scheduler might be more aggressive in granting CPU time to less resource-intensive requests. Conversely, if your app has consistent, high CPU demands, a dedicated-cpu machine will offer much more predictable performance, even if it costs more. The key is to monitor your application’s actual resource utilization using flyctl monitoring <app-name> and flyctl metrics <app-name> to understand its real needs.
The next step after optimizing machine size is understanding how to leverage Fly.io’s global load balancing and auto-scaling features.