Skip to content
Search

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:

curl -L https://fly.io/install.sh | sh

If you need to sign up first:

flyctl auth signup

If you already have an account:

fly auth login

Launch an app right away:

flyctl launch --image flyio/hellofly:latest

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:

flyctl certs create api.server.com

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:

# ...
[build]
  dockerfile = "Dockerfile"
# ...

Then run the following command.

fly deploy

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
  • Fly.io is free for up to 3 VMs, and $2 a month per VM beyond that
    • 1 Shared CPU and 256MB RAM, which is enough for a Go app

Connecting via WireGuard

See also

Favorite site

References

author Zer0 sitemap