Deploying Node.js apps to Fly.io
Tags: node, flyio, githubactions, hosting
Published: 2022-11-17 00:02:29

---
title: Deploying Node.js apps to Fly.io
date: 2022-11-17 00:01:00 UTC
published: true
tags: ['node', 'flyio', 'githubactions', 'hosting']
canonical_url: https://sevic.dev/nodejs-deployment-flyio/
---

This post covers main notes from setting up the Fly.io CLI tool to CI/CD pipeline with [Github actions](https://sevic.dev/notes/github-actions-101/).

### Prerequisites

- Run the following command to install the CLI tool and follow the instructions to add the necessary commands to the appropriate shell configuration file

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

- Create an account or log in if you already have an account

```bash
fly auth signup
fly auth login
```

### Node server

- Run the following commands for packages setup

```bash
npm init -y
npm i express
```

- Write basic server

```js
// server.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 8080;

app.get('/', (_, res) => res.send('Hello world'));

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
```

- Configure `start` script

```json
// package.json
{
  "scripts": {
    "start": "node server.js"
  }
}
```

### Project setup

Choose the project name and region for deployment. The following command will create a project and generate a configuration file. Project URL will be `<PROJECT_NAME>.fly.dev`

```bash
fly launch
```

### Environment variables

#### Configuration variables

Generated `fly.toml` file contains `env` section for environment variables which can be publicly available like URLs, HTTP timeout values, etc.

```toml
// ...
[env]
  PORT = "8080"
  NEW_VARIABLE = "value"
// ...
```

#### Secrets

The following commands are used for setting up the secrets like API keys, credentials, etc.

```bash
fly secrets set KEY=value
fly secrets list
fly secrets unset KEY
```

### Databases

#### Redis

Run the following commands to provision the Redis database and connect with it. Eviction is disabled by default.
URL can be found with the status command. Set it as a secret.

```bash
fly redis create
fly redis connect
fly redis list
fly redis status <database-name>
```

#### Postgres

Run the following command to bootstrap a Postgres database cluster.

```bash
fly postgres create
```

The previous command will prompt the connection string only once. Set it as a secret.

```js
// knexfile.js
module.exports = {
  // ...
  production: {
    client: 'postgresql',
    connection: process.env.DATABASE_URL,
    // ...
  },
};
```

Release command can run migrations.

```toml
# fly.toml
[deploy]
  release_command = "npm run migrate"
```

Test the database connection with the following command.

```bash
fly postgres connect -a <postgres-app-name>
```

##### Backup

Fly.io performs daily snapshots of the provisioned volumes. Snapshots are volume-specific. Postgres database can be restored out of the provided snapshot.

```bash
fly volumes list -a <postgres-app-name>
fly volumes snapshots list <volume-id>
fly postgres create --snapshot-id <snapshot-id>
```

### Scaling

Every app runs inside a virtual machine, and the following command shows the VM size and how many app instances are running.

```bash
fly scale show
```

#### Vertical scaling

- The following command shows the list of VM types and their memory allocations

```bash
fly platform vm-sizes
```

- Run the following command to upgrade the VM to one of the types from the previous command, memory allocation in MBs can be specified as well

```bash
fly scale vm <type> --memory 1024
```

- Run the following command to increase VM memory, 1024MB in this case

```bash
fly scale memory 1024
```

#### Horizontal scaling

Run the following command to increase the number of app instances, 3 in this case

```bash
fly scale count 3
```

Autoscaling is disabled by default. Run the following commands to configure it.

```bash
fly autoscale show
fly autoscale set min=3 max=6
```

### Debugging

- Get the real-time logs

```bash
fly logs
```

- Get the info about the app, services, and IP addresses

```bash
fly info
```

- Get the state of the project

```bash
fly status
```

- Get the app IP addresses

```bash
fly ips list
```

- Run bash on the app

```bash
fly ssh console
```

- Restart the app

```bash
fly apps restart <app-name>
```

### Deployment

#### Manual deployment

The following commands are used to deploy and open the deployed version.

```bash
fly deploy --no-cache
fly open
```

#### Continuous deployment with Github actions

A project auth token is needed. Run the following command to generate it. Add the generated key as a Github action secret in the Settings &#8594; Secrets &#8594; Actions page

```bash
fly auth token
```

Add `.github/workflows/config.yml` file with the following configuration.

```yml
name: CI/CD pipeline

on:
  push:
    branches:
      - main

env:
  FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    container: node:20.9.0-alpine3.17

    steps:
      - name: Github checkout
        uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - run: npm ci

      - run: npm run lint

      - run: npm test

      - run: npm audit

      - name: Setup Fly.io config
        uses: superfly/flyctl-actions/setup-flyctl@master

      - name: Deploy to Fly.io
        run: flyctl deploy --remote-only --no-cache
```

### Boilerplate

Here is the [link](https://sevic.dev/nestjs-starter?ref=devto-flyio) to the boilerplate I use for the development.