Deploy a Node Express Application to Production
Tags: tutorial, javascript, docker, webdev
Published: 2021-06-22 13:15:02

## Introduction

In this guide, we will explain how to deploy a Node.js Express application to production. We will write a simple Node.js API using the Express framework, see how to Dockerize the application,
version it on GitHub, and create a GitHub Actions workflow to perform the CI/CD and deploy the application on Koyeb each time you push changes on a specific branch.

By deploying the Node app on Koyeb, you benefit from native autoscaling, automatic HTTPS (SSL), auto-healing, and global load-balancing across our edge network with zero configuration.

## Requirements

To successfully follow and complete this guide, you need:
 - [Docker](https://docs.docker.com/get-docker/) installed on your machine
- A [Koyeb account](https://app.koyeb.com) to deploy and run the Node.js Express application
- The [Koyeb CLI](https://github.com/koyeb/koyeb-cli) installed to interact with the Koyeb from the command line
- Have [configured Docker for use with GitHub Packages](https://docs.github.com/en/packages/guides/configuring-docker-for-use-with-github-packages)
- A [GitHub account](https://www.github.com) with an empty repository we will use to version the application and perform the CI/CD with GitHub Actions

## Steps

To deploy a Node Express Application to Production, you need to follow these steps:

1. [**Create a simple Node.js API using the Express app or use an existing one**](#create-a-simple-node-js-api-using-the-express-framework-application-or-use-an-existing-one)
2. [**Write the Dockerfile and build the Docker image**](#write-the-dockerfile-and-build-the-docker-image)
3. [**Push the Docker image to the GitHub container registry**](#automate-deployment-with-git-hub-actions)
3. [**Deploy the Dockerized Node.js app on Koyeb**](#deploy-the-dockerized-node-js-app-on-koyeb)
4. [**Automate deployment with GitHub Actions**](#automate-deployment-with-git-hub-actions)

## Create a simple Node.js API using the Express framework application or use an existing one

If you already have an existing Node.js application you want to dockerize, you can jump to the next step.

Create a new directory to store our Node.js application:

```bash
mkdir node-express
cd node-express
```

The next step is to create and initialize the package.json file. The package.json contains various metadata and gives npm information to identify the project, handles the project's dependencies, etc.

In your terminal run the following command and complete as below:

```bash
npm init

This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (node-express)
version: (1.0.0)
description: A simple Node.js with Express framework application
entry point: (index.js) app.js
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /Users/koyeb/demo/node-express/package.json:

{
  "name": "node-express",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this OK? (yes)
```

As our application uses the Express framework, we need to add it as a dependency of our project. In your terminal, run:

```
npm install express --save
```

The project environment ready, we can now start writing our application. In this guide, we create a basic application that will return the port and git commit id the application is using on requests received to the root URL `/`. All other routes will respond with a 404 error.

Create and open a file named `app.js` and copy the content below:

```js
const express = require('express')
const app = express()
const port = process.env.PORT || 3000

app.get('/', (req, res) => {
  res.json({
    commit_id: process.env.COMMIT_ID || 'unknown',
    port
  })
})

app.listen(port, () => {
  console.log(`App listening at http://localhost:${port}`)
})
```

Launch the app running `node app.js` and request the `/` endpoint running:

```bash
curl http://localhost:3000/
{"commit_id":"unknown","port":3000}
```

The Node app responds with the port the application is listening to "3000" and commit_id set at "unknown" for the moment.

## Write the Dockerfile and build the Docker image

To Dockerize our Node.js app, you need to create a `Dockerfile` in your project folder containing the content below.

```dockerfile
FROM node:lts as runner
WORKDIR /node-express
ENV NODE_ENV production
ARG COMMIT_ID
ENV COMMIT_ID=${COMMIT_ID}
COPY . .
RUN npm ci --only=production
EXPOSE 3000
CMD ["node", "app.js"]
```

To build and properly tag the Docker image execute the following command:

```bash
docker build . -t ghcr.io/<YOUR_GITHUB_USERNAME>/node-express:prod
```

Once the build is over, you can run a container using the image locally to validate everything is working as expected running:

```bash
docker run -p 3000:3000 ghcr.io/<YOUR_GITHUB_USERNAME>/node-express:prod
```

As in the previous step, you can perform a curl request to ensure the app is running as expected:

```bash
curl http://localhost:3000/
{"commit_id":"unknown","port":3000}
```

## Push the Docker image to the GitHub container registry

With our image built, we can now push it to the GitHub container registry. We will then use this image to deploy the application on Koyeb.

```bash
docker push ghcr.io/<YOUR_GITHUB_USERNAME>/node-express:prod
```

Within a few minutes, you will see your Docker image available on the GitHub container registry: `https://github.com/<YOUR_GITHUB_USERNAME>?tab=packages`.

## Deploy the Dockerized Node.js app on Koyeb

To deploy our dockerized Node.js application on Koyeb, start by creating a Koyeb Secret to store your Github container registry configuration.
Replace `<REPLACE_ME_WITH_GH_USERNAME>` with your GitHub username and `<REPLACE_ME_WITH_GH_TOKEN>` with a valid GitHub token having registry read/write permissions and execute the command below.

```bash
echo \
'{ \
  "auths": { \
    "ghcr.io": { \
      "username": "<REPLACE_ME_WITH_GH_USERNAME>", \
      "password": "<REPLACE_ME_WITH_GH_TOKEN>" \
    } \
  } \
}' | koyeb secrets create gh-registry-credentials
```

We can now deploy the Node.js application on Koyeb Serverless Platform running:

```bash
koyeb app init node-express --docker "ghcr.io/<REPLACE_ME_WITH_GH_USERNAME>/node-express:prod" --ports 3000:http --routes /:3000 --docker-private-registry-secret gh-registry-credentials
```

Within a few minutes, your application will be live and accessible at `https://node-express-<REPLACE_ME_WITH_GH_USERNAME>.koyeb.app`.

## Automate deployment with GitHub Actions

In the previous steps, we discovered how to dockerize and deploy a Node.js application on Koyeb.

In this section, we will see how to automate the deployment of ou application each time a change is pushed to the branch `main` of your repository using GitHub Actions.

In your git repository, create a folder to store our GitHub Actions workflow:

```
mkdir -p .github/workflows
cd .github/workflows
```

Create a new file named `workflow.yaml` inside the directory we created in the previous step and paste the snippet below:

```yaml
name: CI

on:
  push:
    branches:
      - main

env:
  GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }}

jobs:
  build-push:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Seed env
        run: |
          echo $GITHUB_SHA | awk '{ printf "SHORT_SHA1=%.7s\n", $1 }' >> $GITHUB_ENV
          basename $GITHUB_REF | awk '{ printf "BRANCH_NAME=%s\n", $1 }' >> $GITHUB_ENV
      - name: Docker build
        run: docker build --rm=false --build-arg COMMIT_ID=$GITHUB_SHA -t ghcr.io/<YOUR_GITHUB_USERNAME>/node-express:prod . # REPLACE <YOUR_GITHUB_USERNAME> with your GitHub username.
      - name: Docker login
        run: echo $GHCR_TOKEN | docker login ghcr.io -u <YOUR_GITHUB_USERNAME> --password-stdin # REPLACE <YOUR_GITHUB_USERNAME> with your GitHub username.
      - name: Docker push
        run: docker push ghcr.io/<YOUR_GITHUB_USERNAME>/node-express:prod # REPLACE <YOUR_GITHUB_USERNAME> with your GitHub username.
      - name: Deploy on Koyeb
        uses: koyeb-community/koyeb-actions@v2
        with:
          api_token: "${{ secrets.KOYEB_TOKEN }}"
      - run: koyeb services redeploy --app=node-express node-express
```

In your GitHub repository settings, click **Secrets** in the left-side menu and create new secrets:

- `GHCR_TOKEN` containing a valid GitHub token having registry read/write permissions to push the image to the registry.
- `KOYEB_TOKEN` containing a valid Koyeb token to redeploy the application.

Commit and push your GitHub actions workflow, your GitHub Actions workflow is being executed. Each time a change is pushed on the `main` branch, a Docker image is built and push to the GitHub registry with the tag `prod`. Once the image pushed,
a new deployment is triggered and deployed on the Koyeb Serverless platform.

## Conclusion

In this guide, we explained how to containerize a Node.js application and deploy it on Koyeb. We created a GitHub Actions workflow to build and deploy the application each time a change occurs.
By deploying on Koyeb, your application is secured with native TLS encryption and benefits from all the Koyeb Serverless features including autoscaling, auto-healing, and a high-performance edge network.

If you have any questions or suggestions to improve this guide,
feel free to reach out to us on [Slack](https://slack.koyeb.com).