Cut your Fly.io bill by stopping dev Machines overnight
Every weeknight at 8pm, automatically stop your idle dev and staging Fly.io Machines, log each one to a spreadsheet, and post a single Slack recap.
Every weeknight (Monday through Friday) at 8pm, run this workflow to cut my Fly.io bill by shutting down non-production Machines overnight. This is fully deterministic: scan, filter by tag, stop, log, and summarize. Never touch production Machines. Use a cron trigger, since Fly.io has no webhook or poll trigger.
Step 1 — Scan. List all of my Fly.io Machines across the organization. Use the Fly.io List All Machines operation to get every Machine in the org in a single pass. If only an app-scoped token is available, fall back to List Apps and then call List Machines for each app. For each Machine, capture its app name, Machine ID, region, current state, and metadata.
Step 2 — Filter by tag. Read each Machine's metadata (for example an env or environment label). If that label is not present on the list response, call Get Metadata for the Machine to read it. Keep only Machines whose environment label is dev or staging (case-insensitive) AND whose current state is started. Explicitly skip any Machine labeled production or prod, and skip any Machine that has no environment label at all. When in doubt, leave it running. Never stop a production Machine.
Step 3 — Stop. For each remaining Machine, call Stop Machine to power it off. Only currently started Machines are eligible, so already-stopped Machines are skipped and never produce errors. Respect Fly.io rate limits (roughly one request per second per action) when stopping many Machines.
Step 4 — Log. After stopping each Machine, append one row to a Google Sheets audit log using the Append Values operation so there is a durable trail. Each row should contain, in order: timestamp (the run time in ISO 8601), app name, Machine ID, region, and action (the literal value stopped). Append rows after the last row of the configured log tab.
Step 5 — Summarize. Once the scan is complete, post a single end-of-run summary to Slack using the Slack Bot Send a Message operation. The message should state how many Machines were stopped and break the count down by app, for example: Stopped 4 Machines overnight — my-api-staging (2), my-web-dev (2). Post the summary even when nothing was stopped, so I always know the run completed. Send it to the Slack channel I configure.
Additional information
What does this prompt do?
- Runs automatically every weeknight at 8pm to find and shut down your non-production Fly.io Machines.
- Only touches Machines tagged as dev or staging that are currently running, so production is never affected.
- Records every Machine it stops in a Google Sheet (timestamp, app, Machine ID, region, action) for a clean audit trail.
- Posts one Slack summary at the end stating how many Machines were stopped and in which apps.
What do I need to use this?
- A Fly.io account with an organization-scoped access token so the workflow can see every app and Machine in your org.
- Your dev and staging Machines tagged with an environment label (for example env=dev or environment=staging) in their Machine metadata.
- A Google account and a spreadsheet to use as the nightly shutdown log.
- A Slack workspace where our bot can post the end-of-run summary.
How can I customize it?
- Change the schedule, for example run at 7pm instead of 8pm or include weekends.
- Adjust which environment labels count as non-production, for example add qa or preview alongside dev and staging.
- Pick the spreadsheet tab that stores the log and the Slack channel that receives the summary.
Frequently asked questions
Will this ever stop my production Machines?
What happens to a Machine after it is stopped?
How does it know which Machines are dev or staging?
What if nothing needs to be stopped?
Can I also start these Machines again in the morning?
Related templates
Stop paying for idle dev servers overnight.
Put your non-production Fly.io Machines on a nightly shutdown schedule and wake up to a clean audit trail.