Automated preview deployments from GitHub to AWS in 10 minutes
Tags: aws, githubactions, devops, opensource
Published: 2023-06-12 15:44:01

Preview deployments are a way to spin temporary environments from branches or pull requests. They're used to allow teammates and product managers to visually review your work before changes are merged, without relying on code only.

## 3 ways to get preview deployments

To use preview deployments in your development workflow, you have 3 solutions:
* your hosting provider has preview deployments sorted out (e.g. Heroku / Vercel / Render / Scalingo). Easy to setup, but you get vendor lock-in, and some providers are costly.
* you find a 3rd-party SaaS to provide the service for you. Setup can be complicated if they use custom definition files, it's costly, and your code is now at risk in another provider's infrastructure.
* you build or use a solution that doesn't rely on 3rd-parties. This could be a GitHub Action, Jenkins pipeline, semi-automated script etc.

Building a custom solution is costly in terms of development and maintenance, so today I want to talk about PullPreview, a GitHub Action which makes preview deployments very easy to setup.

## PullPreview

[PullPreview](https://pullpreview.com) is an [open-source GitHub Action](https://github.com/pullpreview/action) which has the following features:

* works with any app that can be booted with Docker Compose (i.e. all).
* can be triggered by applying a label on a pull request.
* deploys code directly from GitHub onto AWS (Lightsail).
* servers are started in your own AWS account.
* SSH access to the servers.
* you can use your own custom domain if you want.
* environments are persistent across deployments.
* fully integrated into GitHub UI, no need to go through another app.

## Let's get started

### Add AWS credentials to your repo

PullPreview requires AWS credentials to deploy servers, so let's [create a new user](https://us-east-1.console.aws.amazon.com/iamv2/home?region=us-east-1#/users/create).

![Create new AWS user for pullpreview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nuf7c13ohlzz64hgo7ko.png)

For some obscure reason AWS doesn't provide a default policy for Lightsail, so you will need to create a new Policy for this user, with the following content:

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "lightsail:*",
            "Resource": "*"
        }
    ]
}
```

![Create new IAM policy for pullpreview user](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggdd30t4oarrm0pj0ttw.png)


Then attach the newly created policy and create the user:


![Attach policy and create user](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/98lutbm048o93cjh3s3l.png)


Final step is to retrieve an access key and secret access key for that new user, and declare those keys into your repository settings on GitHub, as new Repository Secrets.


![Add credentials as repo secrets](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/voimn4j5v8u2kht5m5p7.png)

### Label, workflow file, and example Docker Compose app

The PullPreview action will be triggered only for pull requests that have a specific label applied. By default the label name is `pullpreview`, so let's create it in your GitHub repository. Use any color you like.

![Create pullpreview label on GitHub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v7xcbnxqw749bcsij5e2.png)

Now, create a new branch, and add the following files:

(1) the pullpreview workflow file:

```yaml
# .github/workflows/pullpreview.yml
name: PullPreview
on:
  pull_request:
    types: [labeled, unlabeled, synchronize, closed, reopened]

jobs:
  deploy:
    permissions:
      contents: read # to fetch code (actions/checkout)
      deployments: write # to delete deployments
      pull-requests: write # to remove labels
      statuses: write # to create commit status
    name: deploy
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
    - uses: actions/checkout@v2
    - uses: pullpreview/action@v5
      # see https://github.com/pullpreview/action/wiki/Inputs
      with:
        admins: your-github-username
        compose_files: docker-compose.pullpreview.yml
        ports: 80,443
        default_port: 443
      env:
        AWS_ACCESS_KEY_ID: "${{ secrets.AWS_ACCESS_KEY_ID }}"
        AWS_SECRET_ACCESS_KEY: "${{ secrets.AWS_SECRET_ACCESS_KEY }}"
        AWS_REGION: "us-east-1"
```

(2) an example docker-compose file, with SSL support:

```yaml
# docker-compose.pullpreview.yml
services:
  # easily set up SSL termination
  proxy:
    image: caddy:2
    restart: unless-stopped
    command: "caddy reverse-proxy --from '${PULLPREVIEW_URL}' --to web:4567"
    depends_on:
      - web
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "/data"

  # simple ruby web server for demo purposes, which connects to a postgres DB
  web:
    restart: unless-stopped
    depends_on:
      - db
    build:
      context: .
      dockerfile_inline: |
        FROM ruby:3.2
        RUN gem install sinatra pg puma
        CMD ruby -rsinatra -rpg -rpuma \
          -e'get("/"){ "Hey PullPreview user, here is the postgres version: #{PG.connect(ENV["PGURI"]).exec("SELECT VERSION()").getvalue(0,0)}" }'
        EXPOSE 4567
    environment:
      PGURI: "postgres://postgres:p4ssw0rd@db/postgres"
      APP_ENV: production

  db:
    restart: unless-stopped
    image: postgres:13
    environment:
      POSTGRES_PASSWORD: p4ssw0rd
```


### Let the magic begin

Now commit the previous files, push your new branch, and open a pull request with the `pullpreview` label applied. GitHub will start executing the PullPreview job, and a new preview environment will be up and running a few minutes later!

![Open PR and add label](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zdf8lnrcvnmvv7otmxbf.png)


![Environment deploying](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sw1c3yy4ngtfiww38kg9.png)


![Environment deployed](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oydhkitcfgw8noxpy3dd.png)


![Accessing the temporary environment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/95abm1aeimz8rxwz1n1s.png)


## Going further

Next steps would be to replace the example `docker-compose.ci.yml` file with your own Docker Compose file, and see if the environment gets successfully deployed as well! You might need a few attempts to get to the point where your environment is fully setup, but remember that you can always SSH into the temporary server if the need arises!

## Conclusion

We've only scratched the surface of what PullPreview can do. The fact that it runs entirely within the GitHub ecosystem and in your own AWS account allows for a wide variety of use cases:
* access restriction from specific IP ranges
* custom domains
* various instance types
* support for private docker registries
* support for multiple docker compose files
* support for multiple preview environments per pull request
* support for seed data
* etc.

You should definitely have a look at the [documentation](https://github.com/pullpreview/action/wiki).

Also note that the PullPreview action is free to use for personal use, but comes with a commercial license (300€/year) for businesses. This ensures that the action is maintained and new features added.