The fly.toml file is the primary configuration for your Fly.io applications, dictating everything from how your app is built and deployed to how it runs and scales.
Let’s see it in action. Imagine you have a simple Go web server that listens on port 8080. Here’s a basic fly.toml to get it running on Fly.io:
app = "my-go-app"
primary_region = "ord"
[build]
builder = "heroku/buildpacks"
[build.config]
path = "github.com/fly-apps/super-go-app" # Example path to your Go app
[deploy]
strategy = "rolling"
[services]
internal_port = 8080
protocol = "tcp"
auto_stop_machines = true
auto_destroy_machines = false
[[services.ports]]
port = 80
handlers = ["http"]
[[services.ports]]
port = 443
handlers = ["http"]
[checks]
[checks.app]
interval = "1m"
timeout = "10s"
grace_period = "5m"
http_path = "/health"
This fly.toml tells Fly.io several crucial things. The app and primary_region define your app’s name and where it will initially be deployed. The [build] section specifies how your application image is created. In this case, we’re using heroku/buildpacks, a common choice for many languages, and pointing it to the Go application code.
The [deploy] section controls how new versions of your app are rolled out. strategy = "rolling" means that new machines will be brought up before old ones are taken down, ensuring zero downtime.
The [services] block is where you configure how your app receives traffic. internal_port = 8080 is vital: it tells Fly.io which port your application inside the container is listening on. Fly.io’s edge network will then route external traffic to this internal port. protocol = "tcp" is standard for web traffic. auto_stop_machines = true is a cost-saving measure; if no traffic is hitting your app, Fly.io will automatically stop the machines. auto_destroy_machines = false means that even when stopped, the machines retain their IP addresses and state, allowing for faster restarts.
The [[services.ports]] entries define the public-facing ports and how they are handled. Here, we’re exposing both port 80 (HTTP) and port 443 (HTTPS) to the internet. The handlers = ["http"] indicates that these ports expect HTTP traffic. Fly.io automatically provisions and manages TLS certificates for HTTPS on port 443.
Finally, [checks] defines health checks. The [checks.app] section configures an HTTP health check to /health on a 1-minute interval with a 10-second timeout. If your app doesn’t respond to this health check within the timeout, Fly.io will consider the machine unhealthy and may restart it or route traffic away from it. grace_period allows new machines time to start up and become healthy before they are expected to pass checks.
The true power of fly.toml lies in its granular control. You can define multiple regions for deployment, set up sticky sessions, configure custom domains, and manage secrets directly within this file. For instance, to deploy to multiple regions and ensure sessions are sticky, you might add:
[[services.concurrency]]
type = "dns"
hard_limit = 25
soft_limit = 20
key = ["session_id"] # Assuming your app sets a session_id cookie
[[services.tcp_options]]
tls_timeout = "1s"
[[services.http_options]]
compress = true
[[services.headers]]
name = "X-Powered-By"
values = "Fly.io"
[[services.automin]]
count = 1
interval = "10m"
[[services.automax]]
count = 5
interval = "1m"
[[services.mounts]]
source = "data"
destination = "/data"
The [[services.concurrency]] block with type = "dns" and a key enables session affinity based on a cookie named session_id. This ensures a user’s subsequent requests are routed to the same machine, which is crucial for applications that maintain session state. [[services.tcp_options]] and [[services.http_options]] allow fine-tuning of network protocols, like setting a tls_timeout or enabling HTTP compression. The [[services.headers]] section lets you add custom HTTP headers to all responses.
The [[services.automin]] and [[services.automax]] blocks configure autoscaling. automin ensures at least one machine is always running, while automax allows scaling up to five machines based on load, with checks happening every minute. The [[services.mounts]] section allows you to persist data by mounting a volume named data inside the container at /data.
Most people don’t realize that the fly.toml is not just a static configuration but a dynamic instruction set that Fly.io continuously interprets. When you run fly deploy, Fly.io reads this file, translates its directives into specific machine configurations, image builds, and network routing rules. Changes to this file, even seemingly minor ones, can trigger a full redeployment cycle.
The next step after mastering fly.toml is understanding how to integrate external services and databases with your Fly.io applications.