Fly.io Logs
Stream logs from Fly.io apps into Gonzo for real-time analysis, filtering, and AI-powered insights.
Stream logs from Fly.io apps into Gonzo with a lightweight jq transform. Fly's CLI streams structured JSON over NATS, but your app's output is double-encoded inside the message field. The pipe below unwraps it so Gonzo sees clean, flat JSON.
Quick Start
fly logs -a <app-name> -j \
| jq --unbuffered -c '
{timestamp, region, instance}
+ (try (.message | fromjson) // empty)' \
| gonzoThe -j flag gives you structured JSON. The jq transform extracts timestamp, region, and instance from the envelope, parses the inner message for your app's real fields (level, message, custom fields), and silently drops non-JSON lines (init logs, Firecracker boot messages).
Prerequisites
Gonzo installed
Fly CLI installed (
brew install flyctl)jqinstalled (brew install jq)
Log Sources
Fly streams multiple sources through a single pipe. The jq transform with // empty keeps only your app's JSON output.
App
app
Your app's stdout/stderr, plus Fly init messages
Runner
runner
Image pulls, Firecracker config, machine start/stop
Proxy
proxy
Machine reachability, startup timing
To include platform logs (useful during deploy debugging), swap // empty for // {message: .message}:
Usage Patterns
Real-time streaming (default):
Filter by region:
Filter by machine:
Buffer dump (no streaming):
--no-tail dumps the current log buffer and exits. For historical queries with date ranges, use Fly's Grafana log search (30-day retention).
With local AI (logs never leave your machine):
Machines and Auto-Stop
Fly Machines auto-stop when there's no inbound traffic. Log streams end when machines stop — this is expected, not a Gonzo or pipe issue. Machines restart on the next request. To keep logs flowing, send periodic requests or set auto_stop_machines = false in fly.toml.
Structured Logging Tips
Emit single-line JSON to stdout for the best Gonzo experience. Multi-line JSON objects are split into separate log entries by Fly's pipeline.
Fly tags all stderr output with "level": "info" at the envelope level, same as stdout. If you rely on stderr for error output (e.g. Python's logging defaults), the envelope level won't reflect real severity. Use structured JSON with an explicit level field instead.
Troubleshooting
No logs appearing
Check fly status -a <app>. Machines may be stopped — hit your app's URL to wake them.
Only build/boot logs, no app logs
Firecracker VM boot takes a few seconds. Wait 10–15s after deploy.
Envelope level always "info"
Expected. Fly doesn't map app log level to envelope. The jq transform extracts the real level.
Logs stall in the pipe
Add --unbuffered to jq. Without it, output buffers and logs appear in delayed bursts.
Init messages mixed with app logs
Fly's init process is tagged provider: "app". The // empty pattern drops these (they're not JSON).
Trial account machines killed after 5m
Add a payment method at fly.io/trial. Free allowance covers small apps.
Time to complete: 5 minutes Prerequisites: Fly CLI, jq, Gonzo installed Full guide: guides/FLY_USAGE_GUIDE.md
Last updated