Build, Deploy and Run a Crystal Application

Getting an application running on Fly is essentially working out how to package it as a deployable image. Once packaged it can be deployed to the Fly infrastructure to run on the global application platform.

In this guide we'll learn how to deploy a Crystal application on Fly.

Creating a Lucky App

Our example will be using a basic Lucky app with PostgreSQL. We'll assume you already have lucky+crystal installed.

Once lucky is installed, we can create a new project by running the following command:

lucky init

This will walk you through project options and create a new project.

โœ— lucky init
Welcome to Lucky v0.29.0 ๐ŸŽ‰

Project name?: hello-lucky

Lucky can generate different types of projects

Full (recommended for most apps)

 โ— Great for server rendered HTML or Single Page Applications
 โ— Webpack included
 โ— Setup to compile CSS and JavaScript
 โ— Support for rendering HTML

API

 โ— Specialized for use with just APIs
 โ— No webpack
 โ— No static file serving or public folder
 โ— No HTML rendering folders

API only or full support for HTML and Webpack? (api/full): full

Lucky can be generated with email and password authentication

  โ— Sign in and sign up
  โ— Mixins for requiring sign in
  โ— Password reset
  โ— Token authentication for API endpoints
  โ— Generated files can easily be removed/customized later

Generate authentication? (y/n): y

-----------------------

Done generating your Lucky project

  โ–ธ cd into hello-lucky
  โ–ธ check database settings in config/database.cr
  โ–ธ run script/setup
  โ–ธ run lucky dev to start the server

Running the Application

To run the application, first run script/setup to install dependencies and create the database. Then run lucky dev to start the application.

Connect to the address displayed in the console.

This should bring you to the "Hello Lucky" page.

Install Flyctl and Login

We are ready to start working with Fly and that means we need flyctl, our CLI app for managing apps on Fly. If you've already installed it, carry on. If not, hop over to our installation guide. Once thats installed you'll want to log in to Fly.

Launch the App on Fly

When we run fly launch from the newly-created project directory, the launcher will:

  • Ask you to select a deployment region
  • Set secrets required by Lucky (SECRET_KEY_BASE, for example)
  • Run the Lucky deployment setup task
  • Optionally setup a Postgres instance in your selected region
  • Deploy the application in your selected region

cd hello-lucky
fly launch
Creating app in /Users/me/hello-lucky
Scanning source code
Detected a Lucky app
? App Name (leave blank to use an auto-generated name): hello-lucky
? Select organization: flyio (flyio)
? Select region: mad (Madrid, Spain)
Created app hello-lucky in organization soupedup
Set secrets on hello-lucky: SECRET_KEY_BASE
Installing dependencies
Running Docker release generator
Wrote config file fly.toml
? Would you like to setup a Postgres database now? Yes
Postgres cluster hello-lucky-db created
  Username:    postgres
  Password:    <password>
  Hostname:    hello-lucky-db.internal
  Proxy Port:  5432
  PG Port: 5433
Save your credentials in a secure place, you will not be able to see them again!

Monitoring Deployment

1 desired, 1 placed, 1 healthy, 0 unhealthy [health checks: 2 total, 2 passing] --> v0 deployed successfully

Connect to postgres Any app within the flyio organization can connect to postgres using the above credentials and the hostname "hello-lucky-db.internal." For example: postgres://postgres:password@hello-lucky-db.internal:5432

See the postgres docs for more information on next steps, managing postgres, connecting from outside fly: https://fly.io/docs/reference/postgres/ Postgres cluster hello-lucky-db is now attached to hello-lucky

Would you like to deploy now? Yes Deploying hello-lucky

==> Validating app configuration --> Validating app configuration done Services TCP 80/443 โ‡ข 8080 Remote builder fly-builder-little-glitter-8329 ready ...

...

That's it! Run fly open to see your deployed app in action.

Try a few other commands:

  • fly logs - Tail your application logs
  • fly status - App deployment details
  • fly status -a hello-lucky-db - Database deployment details
  • fly deploy - Deploy the application after making changes

Inside fly.toml

The fly.toml file now contains a default configuration for deploying your app. In the process of creating that file, flyctl has also created a Fly-side application slot of the same name, "hello-lucky". If we look at the fly.toml file we can see the name in there:

app = "hello-lucky"

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

[env]

[experimental]
  allowed_public_ports = []
  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]]
    handlers = ["http"]
    port = 80

  [[services.ports]]
    handlers = ["tls", "http"]
    port = 443

  [[services.tcp_checks]]
    grace_period = "1s"
    interval = "15s"
    restart_limit = 0
    timeout = "2s"

The flyctl command will always refer to this file in the current directory if it exists, specifically for the app name/value at the start. That name will be used to identify the application to the Fly platform. The rest of the file contains settings to be applied to the application when it deploys.

We'll have more details about these properties as we progress, but for now, it's enough to say that they mostly configure which ports the application will be visible on.

Deploying to Fly

To deploy changes to your app, just run just run:

flyctl deploy

This will lookup our fly.toml file, and get the app name hello-lucky from there. Then flyctl will start the process of deploying our application to the Fly platform. Flyctl will return you to the command line when it's done.

Alternatively, you can use GitHub Actions to deploy your app to Fly

Viewing the Deployed App

Now the application has been deployed, let's find out more about its deployment. The command flyctl info will give you all the essential details.

fly status
App
  Name     = hello-lucky
  Owner    = personal
  Version  = 0
  Status   = running
  Hostname = hello-lucky.fly.dev

Deployment Status
  ID          = 8d35eade-8c68-499e-e0a9-c6829e3c1c9d
  Version     = v0
  Status      = successful
  Description = Deployment completed successfully
  Instances   = 1 desired, 1 placed, 1 healthy, 0 unhealthy

Instances
ID              PROCESS VERSION REGION  DESIRED STATUS  HEALTH CHECKS           RESTARTS        CREATED
235f9667        app     0       ord     run     running 1 total, 1 passing      0               1m2s ago

If you want to know what IP addresses the app is using, try flyctl ips list:

flyctl ips list
TYPE ADDRESS             REGION CREATED AT
v4   66.51.123.94        global 3m20s ago
v6   2a09:8280:1::3:23f4 global 3m20s ago

Connecting to the App

The quickest way to browse your newly deployed application is with the flyctl open command.

flyctl open
Opening http://hello-lucky.fly.dev/

Your browser will be sent to the displayed URL. Fly will auto-upgrade this URL to a HTTPS secured URL.

Multi-Region Deployment With Postgres (Optional)

Fly also supports multi-region deployments.

To deploy to multiple regions, first Create a PostgreSQL Cluster, then follow these steps:

  1. Configure your primary region by setting the env PRIMARY_REGION in your fly.toml

    [env]
    PRIMARY_REGION = "ord"
    
  2. Add the superfly/fly.cr shard shard to your project

    dependencies:
    fly:
    github: superfly/fly.cr
    version: ~> 0.1
    
  3. Require fly/pg/error_handler and fly/avram after avram

    # src/shards.cr
    # ...
    require "avram"
    # ...
    require "fly/pg/error_handler"
    require "fly/avram"
    # ...
    
  4. Add the Fly::PG::ErrorHandler middleware to your list of middlewares, after the Lucky::ErrorHandler

    # src/app_server.cr
    # ...
    def middleware : Array(HTTP::Handler)
      [
        # ...
        Lucky::ErrorHandler.new(action: Errors::Show),
        Raven::Lucky::ErrorHandler.new,
        Fly::PG::ErrorHandler.new,
        # ..,.
      ]of HTTP::Handler
    end
    # ...
    
  5. Deploy with fly deploy

  6. You can see replays in the fly log

Arrived at Destination

You have successfully built, deployed, and connected to your first Crystal application on Fly.