Vercel – How to deploy to multiple environments (test, staging, production + more) with their own env variables using GitHub Actions

June 30, 2023 Dev Ops

Vercel is an excellent service to host and deploy your front-end application to. There are a lot of great features such as GitHub integration, monorepo support giving you the option to select a subdirectory to deploy, support for multiple bundling packages, e.g. Parcel, Next.js, Vite, Angular etc. and multiple environments. However, with the latter, it initially seems that it’s only possible to have two environments, Preview and Production (and Development for local dev).

This isn’t entirely true as I will show you in this blog, and I’ll show you how to do it with GitHub Actions (although it isn’t 100% required to use Actions and you can instead use the Vercel CLI if you wish)

Setup

In this guide, we will be using Minsu Lee’s (amondnet) GitHub Action for Vercel deployments (see: amondnet/vercel-action). You can create a GitHub Actions workflow using this action as the main step in the workflow. Workflows go in your GitHub repository in the root directory .github/workflows/{insert-deploy-workflow-name}.yaml

Here’s an example from the readme:

name: Deploy Application
on: [pull_request]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: amondnet/vercel-action@v20 #deploy
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }} # Required
          github-token: ${{ secrets.GITHUB_TOKEN }} #Optional 
          vercel-args: '--prod' #This is what will enable us to have separation of environments
          vercel-org-id: ${{ secrets.ORG_ID}}  #Required
          vercel-project-id: ${{ secrets.PROJECT_ID}} #Required 
          working-directory: ./frontend-app-directory #If you are in a monorepo where you have backend code and frontend code, you can use this to only deploy your frontend code in the sub directory
          alias-domains: ${{ vars.DOMAIN }} #Domain name for your deployment

For the tokens and IDs, you will have to set up an account and project on Vercel and you should be able to find the details in the settings of the project and organisation. If I remember correctly, the vercel-org-id will be known as the “Team ID” in the organisation settings.

You will put these secrets in GitHub Actions secrets which can be found in your repository settings tab, then find “Secrets and Variables > Actions” in the sidebar.

You can find out more details on how to set up by reading the documentation, but what I will focus on is the ability to simulate multiple environments.

Choose environments using Actions inputs

on:
    workflow_dispatch:
        inputs: # GitHub Actions inputs will allow you to select environments
            releaseType:
                description: 'Release Type'
                required: true
                default: 'test'
                type: choice
                options:
                    - test
                    - staging
                    - prod

You can use “inputs” in your GitHub Actions workflow and refer to it in your action to determine which environment to deploy to. Replace the on: property in the previous example with the above. You can then refer to it using ${{ inputs.releaseType }} and use it in conditionals for environment specific configurations. E.g. vercel-args: ${{ inputs.releaseType == ‘prod’ && ‘--prod’ || ‘’ }}

Alternatively, you could have one workflow for each environment.

Deploying to a chosen environment

Environment variables (environment-specific)

To separate environments, we will first create variables for the GitHub Actions. See the example below:

You can click the green “New repository variable” button to create a variable. As you can see in the screenshot I’ve already created some which will be passed into vercel-args property in the GitHub action mentioned earlier. vercel-args will be the arguments that are used by the action to insert when running the vercel deployment. We can leverage this vercel-args attribute of the action to pass in environment-specific environment variables, each variable will be passed in as using the --build-env option and then {VARNAME}={value} so --build-env {VARNAME}={value}. And you can have as many of these options as you want.

To deploy to production, make sure to also add in the --prod option to the arguments.

An example would be:

vercel-args: ${{ inputs.releaseType == 'prod' && vars.PROD_ARGS || inputs.releaseType == 'staging' && vars.STAGE_ARGS || inputs.releaseType == 'test' && vars.TEST_ARGS || '' }}

This will ensure that the selected environment will get its own environment variables.

Environment-specific domains

We will use a similar strategy for environment-specific domains. Ensure that you have set up the CNAME in your DNS provider to point each domain you would like to cname.vercel-dns.com.

So for example, in your Domain Name, let’s say, company.com, you can add a CNAME record for test.company.com by going into the DNS records for company.com and adding a CNAME record

To pass in the domain, we will use the alias-domains property of the GitHub Action. You can put the domain name as a hardcoded string in the workflow, or alternatively create a repository variable (which is recommended as you can change it easily).

alias-domains: ${{ inputs.releaseType == 'prod' && 'app.company.com' || inputs.releaseType == 'staging' && 'app.stage.company.com' || inputs.releaseType == 'test' && 'app.test.company.com' || '' }}

Or with GitHub variables:

alias-domains: ${{ inputs.releaseType == 'prod' && vars.PROD_DOMAIN || inputs.releaseType == 'staging' && vars.STAGE_DOMAIN || inputs.releaseType == 'test' && vars.TEST_DOMAIN || '' }}

Complete example

Here’s a complete example, combining all the parts for a workflow:

name: Deploy Application
on:
    workflow_dispatch:
        inputs: # GitHub Actions inputs will allow you to select environments
            releaseType:
                description: 'Release Type'
                required: true
                default: 'test'
                type: choice
                options:
                    - test
                    - staging
                    - prod
    pull_request: #This is to automatically deploy on a pull request
        branches:
            - main
        paths:
            - 'frontend-app/**'
    push: #This is to automatically deploy on a push to main
        branches:
            - main
        paths:
            - 'frontend-app/**'
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: amondnet/vercel-action@v20 #deploy
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }} #Required
          github-token: ${{ secrets.GITHUB_TOKEN }} #Optional 
          vercel-args: ${{ inputs.releaseType == 'prod' && vars.PROD_ARGS || inputs.releaseType == 'staging' && vars.STAGE_ARGS || inputs.releaseType == 'test' && vars.TEST_ARGS || '' }} #Environment Specific Arguments
          vercel-org-id: ${{ secrets.ORG_ID }}  #Required
          vercel-project-id: ${{ secrets.PROJECT_ID }} #Required 
          working-directory: ./frontend-app
          alias-domains: ${{ inputs.releaseType == 'prod' && vars.PROD_DOMAIN || inputs.releaseType == 'staging' && vars.STAGE_DOMAIN || inputs.releaseType == 'test' && vars.TEST_DOMAIN || '' }} #Environment Specific Alias Domain

See bolded code for the main blocks that make the multiple environment deployments work.

Leave a Reply

Your email address will not be published.