fly.toml is the declarative configuration file for your Fly App. It lives at the root of your project and tells Fly.io:
- How to build your app
- Which ports to expose
- Health check settings
- Scaling parameters
- Environment variables (non-secret)
- Volume mounts
# fly.toml
app = "my-fly-app"
primary_region = "ams"
[build]
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0# ─── App Identity ───────────────────────────────────────────────────────────
app = "my-fly-app" # Unique app name within your org
primary_region = "ams" # Default region for new Machines
# ─── Build ──────────────────────────────────────────────────────────────────
[build]
# Auto-detected if Dockerfile present.
# Or specify a builder:
# builder = "paketobuildpacks/builder:base"
# Or a prebuilt image:
# image = "nginx:1.25"
# Pass build args:
[build.args]
NODE_ENV = "production"
# ─── Environment Variables (non-secret) ──────────────────────────────────────
[env]
LOG_LEVEL = "info"
PORT = "8080"
# ─── HTTP Service (recommended for web apps) ─────────────────────────────────
[http_service]
internal_port = 8080 # Port your app listens on inside the container
force_https = true # Redirect HTTP → HTTPS
auto_stop_machines = true # Stop idle Machines
auto_start_machines = true # Wake on incoming traffic
min_machines_running = 0 # Minimum always-on Machines
[http_service.concurrency]
type = "requests" # "requests" or "connections"
soft_limit = 200 # Start new instances at this load
hard_limit = 250 # Reject connections above this
# ─── TCP Services (alternative to http_service, for non-HTTP) ────────────────
# [[services]]
# protocol = "tcp"
# internal_port = 5432
# [[services.ports]]
# port = 5432
# ─── Health Checks ───────────────────────────────────────────────────────────
[[http_service.checks]]
grace_period = "5s" # Wait before first check after start
interval = "10s" # How often to check
method = "GET"
path = "/health"
protocol = "http"
timeout = "2s"
tls_skip_verify = false
# ─── Mounts (Volumes) ────────────────────────────────────────────────────────
[[mounts]]
source = "myapp_data" # Volume name
destination = "/data" # Mount path inside container
# ─── VM Size ─────────────────────────────────────────────────────────────────
[[vm]]
memory = "512mb"
cpu_kind = "shared"
cpus = 1
# ─── Processes ───────────────────────────────────────────────────────────────
[processes]
app = "node server.js"
worker = "node worker.js"
# ─── Statics (serve files from a directory) ──────────────────────────────────
[[statics]]
guest_path = "/app/public"
url_prefix = "/static"# Deploy with current fly.toml
fly deploy
# Validate fly.toml without deploying
fly config validate
# Show current deployed config
fly config show
# Pull remote config into local fly.toml
fly config save| Use Case | Tool |
|---|---|
Non-sensitive config (LOG_LEVEL, PORT) |
[env] in fly.toml |
Sensitive values (DATABASE_URL, API_KEY) |
fly secrets set KEY=value |
Secrets are never stored in fly.toml and are injected at runtime.
primary_region = "ams" # Where new Machines land by defaultTo deploy to multiple regions, use the CLI:
fly scale count 1 --region lhr
fly scale count 1 --region syd[http_service]
auto_stop_machines = false
min_machines_running = 1[http_service]
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0[processes]
worker = "python worker.py"
# No [http_service] section needed- Add a
/healthendpoint to your app returning200 OK. - Add the
[[http_service.checks]]block tofly.toml. - Run
fly deployand verify health checks pass viafly status.
- Add an
[env]block withAPP_GREETING = "Hello from fly.toml". - Read it in your app code.
- Deploy and verify.
- Intentionally introduce a typo in
fly.toml. - Run
fly config validate— observe the error. - Fix it.
→ Continue to 600 — Networking, Routing & Certificates