Answers you can trust, from Codeables

Every page on Codeables is structured and verified — built so people and the AI agents they rely on can trust it. Explore more from the source behind this answer.

Explore Codeables
Verified Source
Platform as a Service (PaaS)

How do I set up Fly.io autostop/autostart so my staging app sleeps when unused?

Fly.io9 min read

Most staging apps don’t deserve 24/7 servers. You use them a few times a day, then they sit there quietly burning money. Fly.io’s autostop/autostart lets your staging app “sleep” when no one is hitting it, and wake up automatically on the next request—without you wiring up a custom scheduler or cron.

Quick Answer: Configure auto_stop_machines and auto_start_machines in your app’s fly.toml (or via flyctl) so Fly Proxy can stop idle Machines and automatically start them on the next HTTP or TCP request. This keeps staging cheap while still feeling on-demand.


The Quick Overview

  • What It Is: Autostop/autostart is a Fly.io feature that automatically stops idle Fly Machines and starts them on the next incoming request, using Fly Proxy’s built‑in traffic detection.
  • Who It Is For: Teams running staging, preview, or demo environments on Fly.io who want “serverless-ish” cost savings without managing Kubernetes or custom autoscalers.
  • Core Problem Solved: You don’t want to pay for idle CPU and memory on staging, but you still want staging.myapp.com to work when a teammate hits it.

How It Works

Fly Machines are hardware-virtualized containers that can start fast enough to catch HTTP requests. Fly Proxy sits in front of your app; it sees incoming connections, decides which region and Machine should serve them, and can also stop Machines that sit idle for a while.

When you enable autostop/autostart on a service:

  • Fly Proxy watches each Machine’s connections.
  • After a configurable idle period, it stops or suspends those Machines.
  • On the next request to your app, Fly Proxy spins up a Machine in that app and routes the request to it once it’s live.

From your perspective, staging “just wakes up” when someone hits it. You only pay for CPU/RAM while Machines are actually running.

Here’s the high‑level flow:

  1. Configure autostop/autostart in fly.toml:
    Declare that a service can be stopped when idle and automatically started when needed.

  2. Deploy your staging app with these settings:
    Use flyctl deploy to apply the config; Fly Proxy starts handling idle detection and wakeups.

  3. (Optional) Add autoscaling or spare Machines:
    For heavier staging or QA environments, layer in fly-autoscaler to keep at least one “ready to start” Machine and control min/max counts.


Step 1: Enable Autostop/Autostart in fly.toml

You configure autostop/autostart per service in your app, not globally. That means inside [[services]] or [http_service] in fly.toml.

A simple HTTP staging app might look like this:

app = "my-staging-app"

primary_region = "iad"

[build]
  image = "registry.example.com/my-app-staging:latest"

[http_service]
  internal_port = 8080
  force_https = true

  # Turn on autostop/autostart:
  auto_stop_machines = "stop"
  auto_start_machines = true

  # Optional but recommended for staging:
  min_machines_running = 0
  max_machines_running = 1

  [http_service.concurrency]
    type = "requests"
    soft_limit = 20
    hard_limit = 25

Key knobs:

  • auto_stop_machines = "stop"
    Tells Fly Proxy to stop Machines when they’ve been idle long enough. Stopped Machines incur storage costs, not CPU/RAM.

  • auto_start_machines = true
    Tells Fly Proxy it’s allowed to start Machines automatically on incoming traffic.

  • min_machines_running = 0
    Lets the app scale all the way down to zero running Machines. Exactly what you want for staging.

  • max_machines_running = 1
    For simple staging, this keeps things predictable: one Machine handles everything. You can bump this if your staging load is spikier.

Using [[services]] Instead of [http_service]

If you’re using the older [[services]] syntax, the same settings live there:

[[services]]
  protocol = "tcp"
  internal_port = 8080

  auto_stop_machines = "stop"
  auto_start_machines = true
  min_machines_running = 0
  max_machines_running = 1

  [services.concurrency]
    type = "requests"
    soft_limit = 20
    hard_limit = 25

  [[services.ports]]
    handlers = ["http"]
    port = 80

  [[services.ports]]
    handlers = ["tls", "http"]
    port = 443

If you have multiple services (e.g., HTTP + gRPC), set autostop/autostart on the service(s) where sleeping makes sense.


Step 2: Deploy the Staging App With Autostop Enabled

Once you’ve edited fly.toml, deploy:

flyctl deploy

Verify that you have Machines running:

flyctl machines list

You should see one or more Machines in started state after deploy.

Wait for Idle Stop

Fly Proxy will stop idle Machines after a period of no active connections. You can:

  1. Hit your staging app a few times (browser or curl).

  2. Do nothing for a while (idle timeout varies; think minutes, not seconds).

  3. Re-run:

    flyctl machines list
    

You’ll see Machines transition from startedstopped when the idle timeout triggers.

If you never see them stop, double‑check:

  • auto_stop_machines = "stop" is set on the service that actually receives traffic.
  • You’re not keeping long‑lived connections open (e.g., WebSockets, idle gRPC streams).
  • min_machines_running isn’t set > 0.

Step 3: Confirm Autostart on Next Request

Once your staging Machines are stopped:

  1. Hit the staging URL again:

    curl -v https://staging.myapp.com/
    
  2. The first request after sleeping may be a bit slower while a Machine boots.

  3. Run:

    flyctl machines list
    

You should see a Machine transition from stoppedstartingstarted.

If you see 502s or long hangs:

  • Make sure auto_start_machines = true is set.

  • Ensure your app binds to the correct internal port (internal_port in fly.toml).

  • Check logs:

    flyctl logs
    

Optional: Use Suspend/Resume for Faster Wakeups

Instead of "stop", you can use "suspend" for even faster start‑up:

[http_service]
  internal_port = 8080
  auto_stop_machines = "suspend"
  auto_start_machines = true

Mechanically:

  • "stop" shuts the Machine down completely. You pay for storage; CPU/RAM is released.
  • "suspend" snapshots the Machine’s memory and state. Resume is much faster, but:
    • Not all Machine types/features may be supported.
    • There are limitations described in the “suspend/resume” docs (e.g., some hardware/driver constraints).

For staging that’s hit several times a day, "suspend" can make the first request feel less “cold.” For staging that’s hit once a week, "stop" is usually fine and cheaper.


Features & Benefits Breakdown

Core FeatureWhat It DoesPrimary Benefit
Autostop MachinesStops idle Fly Machines after no active connectionsCuts CPU/RAM costs for staging that isn’t always used
Autostart on TrafficStarts Machines on incoming requests automatically“Serverless-like” feel without extra infrastructure
Per-Service ControlsEnable/disable autostop per [[services]] / [http_service]Sleep staging while keeping prod always-on

Ideal Use Cases

  • Best for staging & QA environments: Because you rarely need them at 3 a.m., and autostop lets them scale to zero when nobody is testing.
  • Best for internal tools & low-traffic dashboards: Because they sit idle most of the day, but should still be one URL away when a teammate needs them.

Limitations & Considerations

  • Cold start latency:
    The first request after a Machine has been stopped (or suspended) will take longer. If your staging tests are extremely latency‑sensitive, you may prefer min_machines_running = 1 during work hours and 0 off‑hours.

  • Long‑lived connections keep Machines alive:
    WebSockets, streaming responses, or long gRPC calls count as “active connections.” If something holds a connection open forever, Fly Proxy won’t consider that Machine idle.

Additional things to keep in mind:

  • Autostop is scoped to services, not arbitrary long‑running tasks. Background jobs that run forever will prevent idling.
  • If you’re also using fly-autoscaler or manual scaling, make sure settings don’t fight each other (e.g., min_machines_running > 0 will keep at least one Machine running).

Pricing & Plans

Autostop/autostart isn’t a separate product or paid add‑on; it’s part of how Fly Machines behave with Fly Proxy. You pay for:

  • CPU & memory usage while Machines are running, metered per second.
  • Persistent storage (Fly Volumes or image storage) even when Machines are stopped.

Turning staging into a “sleeping” app means your bill tracks the actual hours you’re testing, not the hours in a month.

How this typically maps to teams:

  • Solo developers / small teams: Use autostop with min_machines_running = 0 and max_machines_running = 1 for staging. You get very low idle costs with an acceptable cold start hit.
  • Larger teams with shared staging: You might keep min_machines_running = 1 during the day (using schedules or autoscaler tricks) and let it drop to 0 overnight/weekends, balancing responsiveness with cost.

Frequently Asked Questions

Do I need fly-autoscaler just to make staging sleep when unused?

Short Answer: No. Autostop/autostart alone is enough for a single staging app that should scale to zero.

Details:
fly-autoscaler is useful when you want to scale based on metrics (CPU, queue depth, etc.) or keep a specific pool of Machines ready for traffic, often in production. For a simple “put staging to sleep” setup, you just need:

auto_stop_machines = "stop"
auto_start_machines = true
min_machines_running = 0
max_machines_running = 1

Fly Proxy handles idle detection and waking Machines on demand. Add fly-autoscaler later if you want metric‑driven scaling or multiple warm Machines for heavier staging workloads.


Will my data be safe if staging Machines stop or suspend?

Short Answer: Yes, as long as you store state in persistent storage (volumes, Postgres, object storage), not only in memory.

Details:
Stopping or suspending a Machine affects the process, not your persistent data. Make sure any important state lives in:

  • Fly Volumes (NVMe) mounted into your app.
  • Managed Postgres on Fly.io or external databases.
  • Object storage like Tigris or S3-compatible stores.

On restart, your app boots from the same image and mounts the same volume or connects to the same database. In-memory cache and ephemeral filesystem contents will not survive a stop; that’s expected and normal for this pattern.


Summary

Making your staging app sleep on Fly.io is mostly a fly.toml edit: enable auto_stop_machines, turn on auto_start_machines, and let min_machines_running = 0. Fly Proxy handles the rest, stopping idle Machines and waking them up on demand. You keep staging one URL away without paying for an always‑on VM, and you don’t have to drag Kubernetes or a home‑grown scheduler into the picture.


Next Step

Get started with Fly autostop/autostart on your staging app by configuring your fly.toml and deploying in a few minutes:
Get Started