Fly.io Local Dev Loop
Overview
Fast local development workflow for Fly.io apps: build and test Docker containers locally, proxy remote Fly services (Postgres, Redis) to localhost, and use fly deploy for integration testing.
Prerequisites
- Local Docker tooling, a non-production environment, synthetic fixtures, and a directory excluded from source control for runtime secrets.
- Approved read-only/sandbox access for any remote proxy; development must not proxy production data by default.
Output
Create a local validation receipt with image/config version, fixture set, health result, and redacted failures. Never commit connection strings, proxy credentials, database contents, or user data.
Error Handling
- Stop a proxy or local test that targets production or exposes credentials; notify the environment owner if access was attempted.
- Treat configuration or schema mismatches as review items and clean up temporary containers/fixtures through approved processes.
- Revoke test credentials if they were disclosed in a terminal capture or artifact.
Examples
Run a container locally with a fictional database URL and verify the health endpoint. Use an isolated staging proxy only for a read-only synthetic fixture, then stop the proxy and confirm no remote credential or data was written to the repository.
Instructions
Step 1: Local Docker Testing
# Build and run locally — same Dockerfile used by Fly
docker build -t my-app .
docker run -p 3000:3000 \
-e NODE_ENV=development \
-e DATABASE_URL="postgres://localhost:5432/dev" \
my-app
# Test
curl http://localhost:3000/health
Step 2: Proxy Remote Fly Services
# Proxy Fly Postgres to localhost:5432
fly proxy 5432 -a my-db &
# Now use local tools against remote Fly Postgres
psql "$DATABASE_URL"
npx prisma studio # Prisma GUI works against proxied DB
# Proxy Redis
fly proxy 6379 -a my-redis &
redis-cli -h localhost -p 6379
Step 3: Development fly.toml
# fly.dev.toml — dev overrides (not committed)
app = "my-app-dev"
primary_region = "iad"
[env]
NODE_ENV = "development"
LOG_LEVEL = "debug"
[http_service]
internal_port = 3000
auto_stop_machines = "off" # Keep running for debugging
min_machines_running = 1
[[vm]]
cpu_kind = "shared"
cpus = 1
memory = "256mb" # Smaller for dev
Step 4: Fast Deploy Cycle
# Deploy to dev app
fly deploy -a my-app-dev --config fly.dev.toml
# Watch logs while testing
fly logs -a my-app-dev --no-tail &
# SSH in for debugging
fly ssh console -a my-app-dev
# Quick restart after config change
fly apps restart my-app-dev
Dev Scripts
{
"scripts": {
"dev": "tsx watch src/index.ts",
"docker:build": "docker build -t my-app .",
"docker:run": "docker run -p 3000:3000 --env-file .env.local my-app",
"fly:dev": "fly deploy -a my-app-dev --config fly.dev.toml",
"fly:proxy:db": "fly proxy 5432 -a my-db",
"fly:logs": "fly logs -a my-app-dev",
"fly:ssh": "fly ssh console -a my-app-dev"
}
}
Resources
Next Steps
See flyio-sdk-patterns for Machines API client patterns.