Orkhan's Blog on software development

Using GitHub Actions to run Vapor deployments

In the previous post, we discussed how to use pre-built Docker images with Laravel Vapor to reduce deployment time. There I also mentioned using GitHub Actions to automate the deployment.

In this post, I want to showcase some examples, including one that uses a private AWS ECR repository as a base image to deploy Vapor applications using GitHub Actions.

First, create a GitHub action workflow file in the .github/workflows directory. For example, deploy.yml. If you have more than one environment, you can create separate workflow files for each environment. deploy-production.yml for production, deploy-staging.yml for staging, etc.

One important note, before everything else, in the examples below whenever you see ${secrets.X} make sure to replace ${ with and } with in your actual workflow file. GitHub automatically removes any mentioned variables when building the site, so I had to place them without variable wrappers to avoid this.

That’s actually really smart from GitHub to protect against accidental exposure of secrets!

Basic deployment using GitHub Actions without custom Docker image

Here’s a basic example of a GitHub Actions workflow file that deploys a Laravel Vapor application to the production environment without using anything custom:

name: Deploy Production

env:
  VAPOR_API_TOKEN: ${secrets.VAPOR_API_TOKEN}

on:
  push:
    branches: [master]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: 18

      - name: Install Yarn dependencies
        run: yarn install

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: 8.3

      - name: Install Composer dependencies
        run: composer install --no-dev

      - name: Deploy Environment
        run: ./vendor/bin/vapor deploy production

Here we give the action a name, and instruct it to run on every push to the master branch, which includes both direct pushes and pull request merges to master.

The important part here is the VAPOR_API_TOKEN secret. First, go to your Vapor Profile, API Token and generate a new token. Then go to your repository “Settings” > “Secrets and variables” > “Actions” on GitHub, and add a new repository secret with the name VAPOR_API_TOKEN and the value of the generated token.

Else should be self-explanatory:

  • We install Node.js dependencies using yarn. Use NPM or whatever you use, or delete this step completely if your project does not need to build assets on deployment
  • We install PHP dependencies using composer
  • Finally, we deploy the application using vapor deploy production

Another important note here, I like to add laravel/vapor-cli as a dependency to the project and lock the version. That’s why we use ./vendor/bin/vapor instead of vapor directly. If you prefer to use the global vapor command, you can replace the dependency installing step with composer global require laravel/vapor-cli and on the deployment step use vapor deploy production.

Using custom Docker image from private AWS ECR repository

In the previous post we talked about using pre-built Docker images with Vapor deployments.

If you used a public repository on AWS ECR or Docker Hub, you should be able to use the above GitHub Actions workflow without any changes.

But if you use a private repository, you need to authenticate with the registry before you can push or pull images. This requires adding a few more steps to the workflow file.

Before running the deployment command we need to:

  • Configure AWS credentials using AWS access key and secret
  • Login to the AWS ECR registry
  • Authenticate Docker client to the ECR registry

Luckily for us, there are official GitHub Actions runners for each of these steps. Our updated workflow file will look like this:

name: Deploy Production

env:
  VAPOR_API_TOKEN: ${secrets.VAPOR_API_TOKEN}

on:
  push:
    branches: [master]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: 18

      - name: Install Yarn dependencies
        run: yarn install

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: 8.3

      - name: Install Composer dependencies
        run: composer install --no-dev

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v3
        with:
          aws-access-key-id: ${secrets.AWS_ACCESS_KEY_ID}
          aws-secret-access-key: ${secrets.AWS_SECRET_ACCESS_KEY}
          aws-region: AWS-REGION-1

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Login Docker to ECR
        uses: docker/login-action@v3
        with:
          registry: ${steps.login-ecr.outputs.registry}
          username: ${steps.login-ecr.outputs.docker_username_1234567890_dkr_ecr_AWS-REGION-1_amazonaws_com}
          password: ${steps.login-ecr.outputs.docker_password_1234567890_dkr_ecr_AWS-REGION-1_amazonaws_com}

      - name: Deploy Environment
        run: ./vendor/bin/vapor deploy production

Configure AWS credentials

In this step, we use the aws-actions/configure-aws-credentials action to configure AWS credentials using the AWS access key and secret. As you can notice we provide this action with AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY secrets. This means you need to add these secrets to your repository settings as well, similar to VAPOR_API_TOKEN.

I recommend you create a dedicated IAM user on your AWS account with the minimum required permissions and use its access key and secret here. AWS has a special user policy called AWSAppRunnerServicePolicyForECRAccess, which allows listing and reading from ECR repositories, it is a perfect policy for this use case.

Also, don’t forget to replace AWS-REGION-1 with your AWS region.

Login to Amazon ECR

This step uses the aws-actions/amazon-ecr-login action to log in to the AWS ECR registry. It does not require any configuration, but it outputs some values that we use in the next step, so we give it an id to reference later.

Login Docker to ECR

Finally, we use the docker/login-action action to authenticate the Docker client to the ECR registry with the credentials provided in the previous step.

This step also uses some configuration. You can leave registry as is, but you need to replace 1234567890 with your AWS account ID and AWS-REGION-1 with your AWS region. Do not modify the rest of the string, including docker_username and docker_password.

If everything is correctly configured, once this workflow runs, it will authenticate with the AWS ECR registry, pull the image, and deploy the application using the pre-built Docker image from the private AWS ECR repository.