Fly.io socket server auto-stop

Why the WebSocket server gets suspended on Fly.io and how to prevent it

The problem

The circular-socket app on Fly.io keeps getting suspended. When all WebSocket clients disconnect, the machine stops after a few minutes of inactivity, and the app shows as "suspended" in the dashboard.

This happens because of two config values in fly.toml:

apps/socket/fly.toml
[http_service]
  auto_stop_machines = true    # Fly stops the machine when idle
  min_machines_running = 0     # Allows ALL machines to stop

Why this is bad for WebSocket servers

auto_stop is designed for HTTP APIs where the proxy can wake machines on incoming requests. But for WebSocket servers:

  1. When all clients disconnect, Fly's proxy detects "excess capacity" and stops the machine
  2. Since min_machines_running = 0, all machines can be stopped
  3. The app becomes "suspended" — no machine is running at all
  4. New WebSocket connections may fail or have significant latency during cold start
  5. Socket.io clients get connection errors until the machine finishes booting

The timeline (from real logs)

19:20:53Z  Last user disconnected
19:25:31Z  "App circular-socket has excess capacity,
            autostopping machine 784e964c47ed68.
            0 out of 1 machines left running"
19:25:31Z  SIGINT → graceful shutdown → exit code 0

Less than 5 minutes of inactivity and the machine is gone.

The fix

1. Update fly.toml

apps/socket/fly.toml
[http_service]
  auto_stop_machines = false   # Never auto-stop
  auto_start_machines = true   # Still auto-start if somehow stopped
  min_machines_running = 1     # Always keep 1 machine running

[scaling]
  min_machines = 1             # Matches the http_service setting

2. Apply without redeploying

If you just need to fix a suspended machine without a full deploy:

# Update machine config (disable autostop)
fly machine update --app circular-socket --autostop=off --skip-start -y <MACHINE_ID>

# Start the machine
fly machine start --app circular-socket <MACHINE_ID>

# Verify
fly status --app circular-socket

3. Deploy from monorepo root

The Dockerfile expects monorepo context, so deploy from the repo root:

cd /path/to/cute
fly deploy --config apps/socket/fly.toml

Running fly deploy from apps/socket/ fails because paths like COPY packages/db ./packages/db can't find files relative to the socket directory.

Cost

A shared-cpu-1x with 512MB running 24/7 on Fly.io costs ~$3-5/month. Worth it for a WebSocket server that needs to be always available.

When auto-stop IS appropriate

  • HTTP API servers: Fly's proxy can wake machines transparently on incoming requests
  • Cron/batch workers: Machines that only need to run periodically
  • Preview environments: Low-traffic staging apps

It's not appropriate for:

  • WebSocket servers: Clients expect persistent connections
  • Any service with long-lived connections: gRPC streams, SSE, etc.