How to deploy Serverless applications using Github Actions
Tags: serverless, github, aws, tutorial
Published: 2020-01-02 00:35:53


In this article I'll show you how to deploy Serverless applications using [Github Actions](https://github.com/features/actions). Github Actions allow you to test, build and deploy your applications in easy way.

## Why Github Actions?

Previously I worked a lot with [Laravel Framework](https://laravel.com/) and used [Envoyer](https://envoyer.io/) for all my deployments. I wanted to find a similar tool for [Serverless Framework](https://serverless.com), so I made a list of important features I'd like to see in a deployment tool:

*   Easy to use;
*   Not too expensive;
*   Ability to setup for client's projects;
*   Ability to manage environment variables (like passwords and secret api keys);
*   Should work with Github;
*   Flexible configuration;

I've tested a number of tools and decided to go with Github Actions for the following reasons:

*   Its provided by Github;
*   Its completely free;
*   Its pretty flexible;
*   I can easily configure custom steps for each project using YAML configuration file;
*   Github provides a secure way to store environment variables;

## So, how deployment process looks like?

In most cases deployment of Serverless application consists of number of steps:

1.  Checkout a repository.
2.  Configure container (install Node, NPM, Serverless Framework, etc).
3.  Connect serverless to provider (AWS/Azure/Cloudflare/etc).
4.  Create environment file with all our secret variables.
5.  Install NPM Dependencies.
6.  Deploy application.
7.  Optional - Build and deploy frontend assets to S3.

Just for example, let's setup automatic deployment to **development** environment **on push to master** and deployment to **production** on **version tag release** (e.g. v1.0.0, v1.2.0, etc).

## Let's do some demo

For demo purposes we'll be using a Node.js application hosted on AWS. You may easily adopt the workflow to work with other languages and service providers.

### Demo application

First, let's create a simple application using Serverless framework using the following command:

```
serverless create --template aws-nodejs
```

Then let's create a env.yml file to store secret variables. This file should never be pushed to our git repository as it contains private api keys and other sensitive information. However, it's a good idea to store in git repository a copy of env file (e.g. **sample.env.yml**) with some placeholder data, so it will be easier to create an actual env.yml file.  

In our case, let's store **appName** value in our **env.yml** file:

```
prod:
    appName: "Serverless App"
    
default:
    appName: "DEV Serverless App"
```    

As you can see, for each application environment we'll be using different values. So when application will be deployed to production, the app name will be "**Serverless App**", and when application will be deployed to development environment, the app name will be "**DEV Serverless App**".

Then, let's modify our **serverless.yml** file:

```
service: gh-actions-demo
    
custom:
    defaultStage: dev
    environment: ${file(env.yml):${self:provider.stage}, file(env.yml):default}
    
provider:
    name: aws
    runtime: nodejs10.x
    stage: ${opt:stage, self:custom.defaultStage}
    environment:
      appName: ${self:custom.environment.appName}
    
    
functions:
    index:
      handler: handler.index
      events:
        - http:
            path: /
             method: get
```
    

A few notes regarding the file above:

In **custom** section we define default stage which will be used on application deployment, and loaded variables from **env.yml** file depending on application stage.

In **provider** section we added a **stage** key (if no stage is passed via command line - default stage from **custom** section will be used by default). And created **appName** environment variable, so we'll be able to use it in our Lambda functions. The value of that variable is defined in our **env.yml** file and passed from **custom** section.

And finally, we defined **index** function in handler.js with the following content:

```
    'use strict';
    
    module.exports.index = async event => {
      let html = `
        <h1>Welcome to ${process.env.appName}</h1>
        <p>Stage: ${event.requestContext.stage}</p>`;
    
      return {
        statusCode: 200,
        headers: {
            'Content-Type': 'text/html',
        },
        body: html
      };
    
    };
```     

As you can see, this function outputs simple text. When deployed to production it will show "Welcome to Serverless App. Stage: prod" and when deployed to dev it will show "Welcome to DEV Serverless App. Stage: dev".

### Create Github Actions

There are few ways to create Github Actions: you may go to your Github Repository, click Actions and add a new workflow. Or you can define a workflow yaml files and store them in **.github/workflows** in your application.

In our case we'll create a two workflows. One of them will be used to deploy application to **dev environment** on each push to **master**:

<script src="https://gist.github.com/maxkostinevich/9672966565aeb749d41e49673be4e7fa.js?file=dev.yml"></script>

And the second workflow will be used to deploy application to **prod environment** every time a new tag with specific pattern is released:

<script src="https://gist.github.com/maxkostinevich/9672966565aeb749d41e49673be4e7fa.js?file=prod.yml"></script>

### How to store secrets on Github

To store secret environment variables, go **Settings** of your Github repository and choose **Secrets** from the left sidebar.

First, we need to store AWS Key and AWS Secret in order to be able to deploy our application using Serverless Framework.

Second, we need to store secret variables listed in our **env.yml** file. We can do a little trick here: rather than storing each secret environment variable separately, we can store the content of entire env.yml file as show on the screenshot below:

![Github Actions Secrets](https://maxkostinevich.s3.eu-west-2.amazonaws.com/website/how-to-deploy-serverless-applications-using-github-actions/gh-actions-secrets.png)



And then use that content to create env.yml file during our workflow process:

```
- name: Create env file
  run: | 
    cat > env.yml << EOF
    ${{ secrets.ENV }}
    EOF
```    

**Please note:** all secrets stored in Github are encrypted. You will not be able to view or edit them once created. So if you need to update any secret key, you need to delete old variable first, and then create a new variable with the same name and updated value.

### Managing Github Actions

You can see the status of all triggered Github Actions:

![List of all triggered Github Actions](https://maxkostinevich.s3.eu-west-2.amazonaws.com/website/how-to-deploy-serverless-applications-using-github-actions/gh-actions-list.png)

To see the details of specific action, just click on it:

![Details of completed action](https://maxkostinevich.s3.eu-west-2.amazonaws.com/website/how-to-deploy-serverless-applications-using-github-actions/gh-actions-status.png)

And finally, you can see URL of your deployed application by expanding "Deploy Lambda functions" step:

![URL of the deployed application](https://maxkostinevich.s3.eu-west-2.amazonaws.com/website/how-to-deploy-serverless-applications-using-github-actions/gh-actions-details.png)

That's all! As you can see, Github Actions make deployment process of Serverless applications really easy.

---
This post was originally published on [my website](https://maxkostinevich.com/blog/how-to-deploy-serverless-applications-using-github-actions/).