Automating Deployments to Heroku with GitHub Actions and Docker: Streamlining the Deployment Process
Tags: webdev, docker, github, javascript
Published: 2023-08-08 14:43:11

## Tutorial: Automating Heroku Deployments with GitHub Actions and Docker: Streamlining the Deployment Process

In this tutorial, we'll delve into the process of utilizing GitHub Actions to automate the deployment of Docker images to Heroku. By the end of this tutorial, you'll have the know-how to achieve continuous deployment for a Fastify + TypeScript application.

## Dependencies

```bash
pnpm add -D typescript @types/node ts-node ts-node-dev
pnpm add fastify prisma @prisma/client
```

## Script Configuration

```json
{
  "scripts": {
    "dev": "ts-node-dev --respawn --transpile-only --ignore-watch node_modules src/index.ts",
    "start": "ts-node src/index.ts",
    "build": "tsc"
  }
}
```

## Setting Up the Backend

We'll start by setting up a simple backend since our primary goal lies elsewhere.

```js
import fastify from 'fastify';

const app = fastify();

app.get('/', async (request, reply) => {
  return { hello: 'world' };
});

export const createServer = async () => {
  const PORT: number = Number(process.env.PORT) || 3001;
  const HOST: string = process.env.HOST || '0.0.0.0';

  try {
    await app.listen(PORT, HOST);
    console.log(`Server is listening at ${HOST}:${PORT}`);
  } catch (error) {
    console.error('Error starting the server:', error);
    process.exit(1);
  }
};

createServer();
```

## Dockerfile Creation

Next, let's craft a Dockerfile for our application. We'll employ the Docker multi-stage build approach, which allows us to create a production image that's considerably smaller than the development one, as we don't require all the development dependencies.

```dockerfile
FROM node:16

WORKDIR /app

COPY package.json ./
COPY pnpm-lock.yaml ./
COPY tsconfig.json ./

COPY . .

RUN npm install -g pnpm
RUN pnpm install

RUN npx prisma generate

CMD ["pnpm", "run", "start"]
```

## docker-compose.yml Setup

Now, we'll create a docker-compose file to facilitate development.

```yml
version: "3.9"
services:
  server_api:
    stdin_open: true
    build:
      context: .
      dockerfile: Dockerfile
    ports:
        - "3001:3001"
    container_name: server_api
    restart: always
    volumes:
      - .:/app
```

## Understanding GitHub Actions

GitHub Actions is a powerful tool provided by GitHub that enables you to automate various workflows, including building, testing, and deploying applications. It allows you to define custom workflows using YAML files, which can be triggered by events such as pushes, pull requests, or even on a schedule.

## Creating the Workflow

Let's proceed to establish the GitHub Actions workflow. For this, create a file named `deploy.yml` within the `.github/workflows` directory.

```yml
name: Deploy to Heroku

on:
  push:
    branches: [master]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Build, Push and Release a Docker container to Heroku
        uses: gonuit/heroku-docker-deploy@v1.3.3
        with:
          email: ${{ secrets.HEROKU_EMAIL }}
          heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
          heroku_app_name: ${{ secrets.HEROKU_APP_NAME }}
          dockerfile_directory: ./
          dockerfile_name: Dockerfile
          docker_options: "--no-cache"
          process_type: web
```
In this workflow:

1. We define the workflow's name and specify that it should trigger on pushes to the `master` branch.
2. The workflow runs on an `ubuntu-latest` virtual environment.
3. We set up Node.js using the `actions/setup-node` action.
4. Dependencies are installed using `pnpm`.
5. We build a Docker image for our Fastify app.
6. We log in to the Heroku container registry.
7. The Docker image is pushed to Heroku.
8. Finally, the Docker image is released on Heroku.

Remember to replace `your-heroku-app-name` with your actual Heroku app name.

## Finalizing

In this tutorial, we've covered a comprehensive process for automating the deployment of a Dockerized Fastify + TypeScript application to Heroku using GitHub Actions. We started by setting up the backend, creating a Dockerfile, and configuring the `docker-compose.yml` file for easier development. We introduced GitHub Actions, explaining its significance in automating workflows. Finally, we created a GitHub Actions workflow that builds, pushes, and releases a Docker container to Heroku upon pushing changes to the `master` branch.

By embracing this automated deployment process, you can significantly enhance the efficiency and reliability of your application deployments, saving valuable time and minimizing manual errors.