Migrate from Fly.io to Temps

To move a Fly.io app to a self-hosted Temps server: deploy the same repository (Temps builds your existing Dockerfile), copy your secrets, restore a pg_dump of your Fly Managed Postgres (or legacy Fly Postgres) into a Temps managed PostgreSQL service you link to the project, move Fly Volume files to S3 storage, map your fly.toml settings, then cut DNS over. Both platforms are container-first, so the Dockerfile carries over unchanged. The differences are the deployment model — Fly runs Machines in many regions, Temps runs on one VPS or a multi-node cluster you control — and that Temps app containers have no persistent volumes.

For a feature-by-feature comparison, see Temps vs Fly.io; for what Fly.io costs today, see Fly.io pricing.


What transfers without changes

FeatureFly.ioTemps
DockerfileFly builds and runs itTemps builds and runs it — same Dockerfile
HTTPSAutomaticSame — Let's Encrypt, auto-renewed
Environment variables (secrets)fly secrets setTemps dashboard or bunx @temps-sdk/cli environments vars set
Health checks[checks] in fly.tomlhealth.path in .temps.yaml, checked before a deploy goes live (see health checks)
Private networkingWireGuard meshApps and managed databases share a private Docker network; connect with the variables Temps injects (such as POSTGRES_HOST). Multi-node clusters join worker nodes over a private network you provide (see multi-node)

What needs an alternative

Fly featureTemps equivalent
Fly Managed Postgres, or legacy Fly PostgresTemps managed PostgreSQL or external (Neon, Supabase)
Upstash for Redis on FlyTemps managed Redis
Fly VolumesTemps managed S3 storage for files, or a Docker Compose project with a named volume. App containers have no persistent volumes
TCP/UDP services ([[services]] in fly.toml)No direct equivalent. The Temps proxy routes HTTP and HTTPS (including WebSockets) to one port per app
Multi-region deploymentMulti-node Temps (advanced)
flyctl CLIbunx @temps-sdk/cli (the temps CLI)
fly.tomlTemps project settings and .temps.yaml (see Step 5)
Machine autostop (scale to zero)Fixed replicas per environment; on-demand mode stops idle non-production environments

Pre-migration checklist

  • SSH access to a VPS running Ubuntu 22.04+ (choosing a server)
  • Domain name with DNS access
  • Fly secrets exported (see Step 1)
  • Fly Volumes data backed up (see Step 3)
  • Postgres backup taken (see Step 2)

Step 1: Export secrets (environment variables)

# List secret names — Fly shows names and digests, never values
fly secrets list --app YOUR_APP_NAME

# Secrets are environment variables inside the running Machine,
# so you can read the values there
fly ssh console -a YOUR_APP_NAME -C "printenv"

Or take the values from where you keep them (a .env file, 1Password). Save them as a .env file and import it with bunx @temps-sdk/cli environments vars import .env -p my-app -e production.

Note which secrets reference Fly-specific infrastructure:

Fly secretAction
DATABASE_URLMap to the POSTGRES_URL Temps injects when you link a managed database (see Step 2)
REDIS_URLRemove — linking a Temps managed Redis injects its own REDIS_URL
FLY_APP_NAMERemove — Fly-specific
FLY_REGIONRemove — Fly-specific
FLY_PUBLIC_IPRemove — replace with your VPS IP if needed
PRIMARY_REGIONRemove — not applicable

Step 2: Migrate your database

Export from Fly Managed Postgres

Fly Managed Postgres (MPG) clusters live on Fly's private network. Open a local proxy with fly mpg proxy, which listens on localhost:16380 by default, and use the direct connection string from the cluster's Connection tab in the Fly dashboard with the host replaced by localhost:16380 (Fly docs):

# Open a proxy to your Managed Postgres cluster
fly mpg proxy YOUR_CLUSTER_ID &

# Export the database
pg_dump "postgres://fly-user:PASSWORD@localhost:16380/fly-db" \
  --no-acl \
  --no-owner \
  -Fc \
  -f fly_backup.dump

# Close the proxy
kill %1

Legacy Fly Postgres (unmanaged) clusters

Fly has archived its unmanaged Fly Postgres and no longer supports it, but existing clusters are Fly apps you can still proxy to:

fly proxy 5433:5432 -a YOUR_POSTGRES_APP &
pg_dump "postgresql://postgres:PASSWORD@localhost:5433/YOUR_DB" \
  --no-acl --no-owner -Fc -f fly_backup.dump
kill %1

fly secrets list won't show the password. Use the credentials printed when the cluster was created, or read the connection string from an app attached to it:

fly ssh console -a YOUR_APP_NAME -C "printenv DATABASE_URL"

Set Up the New Database

Option A — Temps managed PostgreSQL (recommended for simplicity). Temps doesn't create a database on deploy: create the service, then link it to the project. Linking creates a database named <project>_<environment> and injects POSTGRES_URL, POSTGRES_HOST, POSTGRES_PORT, POSTGRES_DB, POSTGRES_USER and POSTGRES_PASSWORD into that environment:

bunx @temps-sdk/cli services create -t postgres -n my-app-db -y
bunx @temps-sdk/cli services list                          # note the service ID
bunx @temps-sdk/cli services link --id <service-id> -p my-app
bunx @temps-sdk/cli services env --id <service-id> -p my-app   # POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB

Option B — External Postgres: Create a database at Neon, Supabase, or any hosted provider and use its connection string as NEW_DATABASE_URL below.

Restore

A Temps managed database isn't reachable from the internet: on the Temps server it listens on 127.0.0.1, on a host port assigned from 5432 (docker ps on the server shows it). Copy the dump to the server and restore it there, using the values from services env:

scp fly_backup.dump user@YOUR_VPS_IP:/tmp/
ssh user@YOUR_VPS_IP

NEW_DATABASE_URL="postgresql://<POSTGRES_USER>:<POSTGRES_PASSWORD>@127.0.0.1:<host-port>/<POSTGRES_DB>"
pg_restore \
  --clean \
  --if-exists \
  --no-owner \
  -d "$NEW_DATABASE_URL" \
  /tmp/fly_backup.dump

Verify the restore by checking row counts on your most important tables before cutting over. Then point the app at it: change your code to read POSTGRES_URL, or set DATABASE_URL to the same value in the environment's variables.


Step 3: Move Fly Volume data

Fly Volumes are persistent disks attached to your Machines. Temps app containers have no persistent volumes: files written inside the container are lost on the next deploy. Choose one of:

  • Object storage (recommended for uploads and user files). Create a Temps managed S3 storage service (bunx @temps-sdk/cli services create -t s3 -n my-app-files -y) and link it to the project, which injects S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY, S3_BUCKET and AWS-style aliases. Change the app to store files through an S3 client, then upload the extracted files to the bucket with any S3 client.
  • Docker Compose project with a named volume. If the app needs a real filesystem (SQLite, LiteFS-style setups, a search index), deploy it as a Docker Compose project and declare a named volume at the path your app uses.

Either way, copy your volume data off Fly first, while the app is still running:

# SSH into your Fly Machine
fly ssh console -a YOUR_APP_NAME

# Compress the volume data
tar -czf /tmp/volume_backup.tar.gz /path/to/your/volume

# Exit the console
exit

# Copy the archive to your local machine
fly ssh sftp get /tmp/volume_backup.tar.gz ./fly_volume_backup.tar.gz -a YOUR_APP_NAME

Then extract the archive and upload its files to the S3 bucket, or copy them into the Compose project's named volume.


Step 4: Reuse your Dockerfile

Your existing Dockerfile works on Temps without changes. Temps detects it automatically when you connect your repository.

If your fly.toml sets a custom Dockerfile path or build context, set the same values under Project → Settings → Build & deploy. There is no separate build-arguments screen: Temps passes the environment's variables to the build as --build-arg values (see build arguments), so add each build arg as an environment variable. Don't put secrets in build args; they end up in the image history.

# fly.toml — what you had
[build]
  build-target = "production"
  [build.args]
    NODE_ENV = "production"

In Temps, add NODE_ENV=production as an environment variable. Temps has no build-target setting and builds the Dockerfile's last stage, so make the production stage the last one.


Step 5: Map fly.toml to Temps settings

fly.toml settingTemps equivalent
internal_portDefault Port under Project → Settings → Build & deploy. Set it to the same value. Temps routes traffic to it and sets PORT to it; if unset, traffic goes to the image's EXPOSE port, then 3000 (see Ports)
[[services.http_checks]] / [checks]health.path in .temps.yaml, checked before a deploy goes live
[processes]One process per project, the image's CMD. Run extra process groups as services in a Docker Compose project
min_machines_runningA fixed replica count per environment
auto_stop_machinesOn-demand mode for non-production environments; production stays running

Step 6: Install Temps and deploy

Install Temps on your server:

curl -fsSL https://temps.sh/deploy.sh -o deploy.sh
less deploy.sh
bash deploy.sh

Then:

  1. Go to Projects → New Project in the Temps dashboard
  2. Connect your Git repository
  3. Temps detects your Dockerfile automatically
  4. Add your environment variables, and link the managed database from Step 2
  5. Click Deploy

Your app gets a Temps URL on sslip.io (for example my-app.203-0-113-1.sslip.io), or on your own wildcard domain if you configured one. Test it before cutting over DNS.


Step 7: Cut over DNS

Once your app works on the Temps preview URL:

  1. Go to Project → Settings → Domains → Add Domain
  2. Enter your domain
  3. Add the DNS records Temps shows you

In your DNS provider:

TypeNameValue
Aapp (or @ for root)YOUR_SERVER_IP

Lower your DNS TTL to 60 seconds before the cutover so you can revert quickly.

Keep your Fly app running for 48 hours after cutting over DNS as a fallback.


Multi-region

Fly.io's main advantage over a single-server setup is global distribution. On Temps, apps run on a single VPS by default. Options if you need multi-region:

  • CDN for static assets — put Cloudflare in front of your Temps instance for free
  • Multi-node Temps — see the Multi-node guide for running Temps across multiple servers
  • Keep non-latency-sensitive apps on Temps, latency-critical on Fly — Temps and Fly are not mutually exclusive

For most apps, a single well-located VPS with a CDN is sufficient.


Next steps

Last updated

Was this page helpful?