Fly.io
lastmod 2025-05-23created 2023-03-0419 edits
Deploy App Servers Close to Your Users. Run your full stack apps (and databases!) all over the world. No ops required.
Categories
Getting started
Install flyctl:
If you need to sign up first:
If you already have an account:
Launch an app right away:
The following prompts and logs are printed.
Creating app in /home/user/Project/cloud-api
Using image flyio/hellofly:latest
? Choose an app name (leave blank to generate one): cloud-api
? Select Organization: company (company)
Some regions require a paid plan (fra, maa).
See https://fly.io/plans to set up a plan.
? Choose a region for deployment: Tokyo, Japan (nrt)
Created app 'cloud-api' in organization 'company'
Admin URL: https://fly.io/apps/cloud-api
Hostname: cloud-api.fly.dev
? Would you like to set up a Postgresql database now? Yes
? Select configuration: Development - Single node, 1x shared CPU, 256MB RAM, 1GB disk
Creating postgres cluster in organization company
Creating app...
Setting secrets on app cloud-api-db...
Provisioning 1 of 1 machines with image flyio/postgres-flex:15.2@sha256:8e00d751bb9811bc8511d7129db2cc5a515449cf4a7def8f1d60faacb2be91c6
Waiting for machine to start...
Machine 1781775c592389 is created
==> Monitoring health checks
Waiting for 1781775c592389 to become healthy (started, 3/3)
Postgres cluster cloud-api-db created
Username: postgres
Password: q3IRDGIrX2RnLvk
Hostname: cloud-api-db.internal
Flycast: fdaa:1:a90f:0:1::2
Proxy port: 5432
Postgres port: 5433
Connection string: postgres://postgres:[email protected]:5432
Save your credentials in a secure place -- you won't be able to see them again!
Connect to postgres
Any app within the company organization can connect to this Postgres using the above connection string
Now that you've set up Postgres, here's what you need to understand: https://fly.io/docs/postgres/getting-started/what-you-should-know/
Checking for existing attachments
Registering attachment
Creating database
Creating user
Postgres cluster cloud-api-db is now attached to cloud-api
The following secret was added to cloud-api:
DATABASE_URL=postgres://cloud_api:[email protected]:5432/cloud_api?sslmode=disable
Postgres cluster cloud-api-db is now attached to cloud-api
? Would you like to set up an Upstash Redis database now? Yes
? Select an Upstash Redis plan Free: 100 MB Max Data Size
Your Upstash Redis database cloud-api-redis is ready.
Apps in the company org can connect to at redis://default:[email protected]
If you have redis-cli installed, use fly redis connect to connect to your database.
Redis database cloud-api-redis is set on cloud-api as the REDIS_URL environment variable
Wrote config file fly.toml
? Would you like to deploy now? Yes
==> Building image
Searching for image 'flyio/hellofly:latest' remotely...
image found: img_z1nr0lpjz9v5q98w
==> Creating release
--> release v2 created
--> You can detach the terminal anytime without stopping the deployment
==> Monitoring deployment
Logs: https://fly.io/apps/cloud-api/monitoring
1 desired, 1 placed, 1 healthy, 0 unhealthy [health checks: 1 total, 1 passing]
--> v0 deployed successfully
Once it finishes successfully, connect your own domain.
- Connect a CNAME, or
- connect an A Record (ipv4) (if the ipv4 is a Shared IP, connect it with a CNAME) 1
- connect an AAAA Record (ipv6). (Gabia doesn't support it, so I connected it with a CNAME)
Finally, attach a certificate:
A message saying it is being registered is then printed, as below.
Your certificate for api.server.com is being issued. Status is Awaiting certificates. Make sure to create another certificate for www.api.server.com when the current certificate is issued.
Go to the dashboard on the website, check the status, and connect later.
The generated fly.toml file looks like this.
# fly.toml file generated for cloud-api on 2023-03-16T19:23:55+09:00
app = "cloud-api"
kill_signal = "SIGINT"
kill_timeout = 5
primary_region = "nrt"
processes = []
[build]
image = "flyio/hellofly:latest"
[env]
[experimental]
auto_rollback = true
[[services]]
http_checks = []
internal_port = 8080
processes = ["app"]
protocol = "tcp"
script_checks = []
[services.concurrency]
hard_limit = 25
soft_limit = 20
type = "connections"
[[services.ports]]
force_https = true
handlers = ["http"]
port = 80
[[services.ports]]
handlers = ["tls", "http"]
port = 443
[[services.tcp_checks]]
grace_period = "1s"
interval = "15s"
restart_limit = 0
timeout = "2s"
Dockerfile deployment
To deploy with a Dockerfile, change the [build] section as follows:
Then run the following command.
Logs roughly like the following are printed.
Update available 0.0.485 -> v0.0.492.
Run "flyctl version update" to upgrade.
==> Verifying app config
--> Verified app config
==> Building image
Remote builder fly-builder-cool-meadow-3970 ready
==> Creating build context
--> Creating build context done
==> Building image with Docker
--> docker host: 20.10.12 linux x86_64
Sending build context to Docker daemon 14.48kB
[+] Building 47.1s (12/12) FINISHED
=> [internal] load remote build context 0.0s
=> copy /context / 0.1s
=> [internal] load metadata for docker.io/library/python:3.11.2-slim 3.9s
=> [builder 1/5] FROM docker.io/library/python:3.11.2-slim@sha256:f73b68edac8d591bffe7801eb2c583efe0b1fa081943cf82c6b600450e531480 2.5s
=> => resolve docker.io/library/python:3.11.2-slim@sha256:f73b68edac8d591bffe7801eb2c583efe0b1fa081943cf82c6b600450e531480 0.0s
=> => sha256:161a52751dd68895c01350e44e9761e3965e4cef0f983bc5b6c57fd36d7e513c 1.37kB / 1.37kB 0.0s
=> => sha256:1a5738045625f379eeb688d2d9d10b206fe477d3995b0bef9a14eae4e4db4679 7.92kB / 7.92kB 0.0s
=> => sha256:3f9582a2cbe7197f39185419c0ced2c986389f8fc6aa805e1f5c090eea6511e0 31.41MB / 31.41MB 0.4s
=> => sha256:57d9937f91c017aba99f0f612c832a63021706b0ebe3e2f1cf33c382135ad5fd 1.08MB / 1.08MB 0.1s
=> => sha256:e4c83445263eca20457ce4f6f94097c83bbc920a2a692a47150f7736ecfe37e2 11.95MB / 11.95MB 1.5s
=> => sha256:c90883e1a33d1036ec41cd2da850dc29f3e16ef012a002dc11903d9b4d8f4f87 233B / 233B 1.5s
=> => sha256:89c4bc491d9d30a35c22f2628673a2e0b6b23d846fb15062e72a1f89bac03dfa 3.37MB / 3.37MB 0.9s
=> => sha256:f73b68edac8d591bffe7801eb2c583efe0b1fa081943cf82c6b600450e531480 1.65kB / 1.65kB 0.0s
=> => extracting sha256:3f9582a2cbe7197f39185419c0ced2c986389f8fc6aa805e1f5c090eea6511e0 1.1s
=> => extracting sha256:57d9937f91c017aba99f0f612c832a63021706b0ebe3e2f1cf33c382135ad5fd 0.1s
=> => extracting sha256:e4c83445263eca20457ce4f6f94097c83bbc920a2a692a47150f7736ecfe37e2 0.4s
=> => extracting sha256:c90883e1a33d1036ec41cd2da850dc29f3e16ef012a002dc11903d9b4d8f4f87 0.0s
=> => extracting sha256:89c4bc491d9d30a35c22f2628673a2e0b6b23d846fb15062e72a1f89bac03dfa 0.2s
=> [builder 2/5] RUN apt-get update && apt-get install -y --no-install-recommends gcc 10.9s
=> [stage-1 2/4] WORKDIR /app 0.1s
=> [builder 3/5] WORKDIR /app 0.0s
=> [builder 4/5] COPY . . 0.0s
=> [builder 5/5] RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/.wheels -r requirements.txt && python setup.py bdist_wheel && cp 6.8s
=> [stage-1 3/4] COPY --from=builder /app/.wheels /app/.wheels 0.0s
=> [stage-1 4/4] RUN pip install --no-cache /app/.wheels/* && rm -rf /app/.wheels && addgroup --gid 1001 --system app && adduser --no 21.3s
=> exporting to image 1.5s
=> => exporting layers 1.5s
=> => writing image sha256:415582ed073b6372f9774788b33e4f5fda6c8533baa05d213aefb8bd69f0d767 0.0s
=> => naming to registry.fly.io/cloud-api:deployment-01GVQ0ZRK9HCKJ823C9E4ER24H 0.0s
--> Building image done
==> Pushing image to fly
The push refers to repository [registry.fly.io/cloud-api]
c5f07ba36921: Pushed
a365250f8510: Pushed
104b65258e23: Pushed
c348a6e56a2f: Pushed
53be024e4476: Pushed
e92949a21056: Pushed
7d13900c8624: Pushed
650abce4b096: Pushed
deployment-01GVQ0ZRK9HCKJ823C9E4ER24H: digest: sha256:3053db2265d8635756acf528971adc9fa3eaedb405ba25a886cb8b40b80a49ff size: 2001
--> Pushing image done
image: registry.fly.io/cloud-api:deployment-01GVQ0ZRK9HCKJ823C9E4ER24H
image size: 300 MB
==> Creating release
--> release v1 created
--> You can detach the terminal anytime without stopping the deployment
==> Monitoring deployment
Logs: https://fly.io/apps/cloud-api/monitoring
1 desired, 1 placed, 1 healthy, 0 unhealthy [health checks: 1 total, 1 passing]
--> v1 deployed successfully
Moving a Go project from EC2 to Fly.io: more fun, $9 a month saved
Switched two side projects from hosting on an Amazon EC2 instance to using Fly.io.
This made it possible to delete about 500 lines of Ansible scripts and configuration files, and to save $9 a month.
- Static files moved from a CDN to ETag caching with go:embed
- CRON replaced with a simple background goroutine; environment variables instead of config files
- App architecture unchanged. Still uses the Go net/http server and an SQLite DB
- Go web apps are said to be easy to operate, but there are many small settings the developer has to take care of (SSL and upgrades with Caddy, etc.)
- With Fly.io, none of this is needed
- 1 Shared CPU and 256MB RAM, which is enough for a Go app
Connecting via WireGuard
See also
Favorite site
- Deploy app servers close to your users · Fly
- Deploying a Docker container for free in 5 minutes (feat. fly.io) | 44BITS
- Deploying to Fly.io
- (Fly.io) Deploying a node.js server with Fly.io
- How to Deploy FastApi to Fly.io? | Ahmad Rosid
- Misconceptions about GPUs | GeekNews (Fly.io, GPU)
- Ordinary developers want LLMs rather than GPUs
- [Original] We Were Wrong About GPUs · The Fly Blog
- We were wrong about GPUs | Hacker News