KMPizza
Kotlin Multiplatform + Pizza = ❤️

Step 31: Deploy Ktor backend on Fly.io

“Where should I move my hobby rpject from Heroku?” After researching the alternatives, I’ve settled with Fly.io. It looks like a fairly easy (compared to Google Cloud Platform) option and has good plans for hobby projects.

Also I love their UI.
Isn’t this art and the haikus inspiring?
It took me 5 minutes to log in just because I was refreshing the page for more haikus 😀

fly haiku

The best thing for your small pet project is that every Fly.io plan has a free allowance, which (according to documentation) includes:

Additional resources are billed at the usage-based pricing (see documentation).

To deploy KMPizza backendx on Fly.io, we’ll need the docker image from the previous step.

The steps to get started are well described here.
I’ll summarize what I’ve done on my Mac:

First install flyctl tool: brew install flyctl

Then sign up flyctl auth signup

I found that signing up with GitHub would be the most convenient for me.

sign up

You can “Try Fly.io for free”, but you’ll still need to add you credit card data afterwards, so I advise you to do it right away.

get started

After signing up and logging in you create a new Fly App.
Fly.io is perfect for deploying docker images. That’s why in the previous step you learned how to use Docker containers.

Go back to KMPizza project. To deploy on Fly your app needs a fly.toml file with instructions how to deploy your app. In command line go to the root of the project and create this file with:

fly launch

You’ll see something like:

fly launch

In the next step, as you already have a postgres database separately with ElephantSQL reply no:

 
? Would you like to set up a Postgresql database now? No
? Would you like to set up an Upstash Redis database now? No

Also reply no to whether you want to deploy right now:

 
? Would you like to deploy now? (y/N) N

Finally in the command line you’ll see

Your app is ready! Deploy with flyctl deploy

After these steps in the project root you’ll have a fly.toml file with a default configuration to deploy your app, something like this:

 
app = "kmpizza"
kill_signal = "SIGINT"
kill_timeout = 5
processes = []

[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"

As you can see, it has the basic information for deployment on Fly.io: the app’s name, used ports, concurrent connection limits etc.

Now build your Docker image locally (if you haven’t done it in the previous step yet) and deploy the image on Fly with:

fly deploy --local-only

In the end you’ll see something like:

deploy locally

But the deployment fails. Ooops.

failed deployment

If you take a look at the logs you’ll see our old grumpy friend, the NullPointerException:

Caused by: java.lang.NullPointerException: getenv("bucketName") must not be null

To set your secrets for Fly.io use all the environment variables you need for the deployment (just like every time before) in the following command:

flyctl secrets set SECRET_ONE=secret1 SECRET_TWO=secret2

For KMPizza it’s something like

flyctl secrets set bucketName=kmpizza secretKey=MYVERYSECRETKEY accessKey=MYVERYSECRETKEY region=eu-central-1 DB_NAME=ElephantDb DB_USER=ElephantUser DB_PASS=ElephantPassword

Run fly deploy --local-only again:

success

Yay! It worked.

Check out your app with flyctl status

fly status

One of the most useful pieces of information here is your hostname: kmpizza.fly.dev.
You can also check it in the user friendly Fly web UI, and browse through the app overview, set secrets, manage certificates and billing.

web app UI

To visit your app either run flyctl open or type https://kmpizza.fly.dev/pizza in a browser.
You’ll see an initial page with KMPizza greeting you.

Finally go back to your KMPizza project in Android Studio and in KtorApiImpl.kt use the new backend url:

 
val prodUrl = "https://kmpizza.fly.dev/"

Now I’m hungry and I’ll go grab a slice of pizza.