> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.knapsack.cloud/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.knapsack.cloud/_mcp/server.

# Deploy Previews for GitLab

## Overview

**Deploy Previews for GitLab are made possible through the Heroku CLI and GitLab's Environments feature.**

Whenever you open a merge request on your GitLab repository, Heroku will automatically spin up a disposable app, reflecting the changes proposed in your merge request.

### GitLab Environments

* GitLab's environments allow you to track deployments dynamically.
* Each merge request can have its own environment.
* **You need to define environments in your** `.gitlab-ci.yml` **file.**
* **Ephemeral environments** (like review apps) can be spun up and later torn down with a stop job (`action: stop`).

***

### Heroku CLI

* The Heroku CLI allows you to dynamically generate deployments.
* Each Deploy Preview that is generated with the Heroku CLI will be tracked within GitLab Environments.
* Please [install the Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli) before continuing with the setup process.

***

### Setup and Configuration

#### <u>Step 1</u>: Generate a long-lived auth token with the Heroku CLI & store it in GitLab variables

**After installing and logging in with the Heroku CLI you'll need to generate a long-lived auth token.**

1. Run `heroku authorizations:create` to generate a token that expires in 1 year.
2. **Copy the token and also <u>create a reminder to generate a new token before the current one expires</u>.**
3. **Back in GitLab —** Navigate to **Settings** » **CI/CD** and expand the **"Variables"** section.
4. Click **"Add variable"**, set **visibility** to **"Masked and hidden"**, set **key** to **"HEROKU\_API\_KEY" (no quotes)**, with the value you copied from the CLI command earlier.
5. Click **"Add variable"** to save it.

#### <u>Step 2</u>: If you don't already have the variable HEROKU\_GIT\_URL for your production app configured, set that up now

**Now that we're going to be both publishing to your production application and dynamically generating Deploy Previews we want to make sure we have the** `HEROKU_GIT_URL` **variable configured correctly.**

1. Validate if the variable exists.
2. If not, create the variable using the same settings as in the last step above. For the **key** you'll use **"HEROKU\_GIT\_URL" (no quotes)** with the value being the Git URL of your production app.
3. Your production application should reside within your new Heroku Team. To locate your Git URL you'll need to navigate to your Heroku Dashboard and then click into your production app. From there you'll click the "Settings" tab and your Heroku Git URL will be located there.
4. After setting the `HEROKU_GIT_URL` variable we can move onto configuring the `.gitlab-ci.yml` file.

#### <u>Step 3</u>: New `.gitlab-ci.yml` configuration

**With our variables configured correctly let's setup the** `.gitlab-ci.yml` **to match the below configuration. Be sure to replace** `your-team-here` **references (theres only 1) with your actual team name provided earlier on during this process.**

```yaml
stages:
  - build
  - deploy_main
  - deploy_review
  - cleanup

variables:
  HEROKU_APP_NAME: ks-$CI_PROJECT_NAMESPACE-$CI_PROJECT_NAME-$CI_MERGE_REQUEST_IID

### Build Step for Production Deployments (main branch) ###
Build_Main:
  image: node:20
  stage: build
  script:
    - npm install
    - npm run build
  cache:
    key: knapsack-build
    paths:
      - node_modules
  only:
    - main

### Production Deployments (main branch) ###
Deploy_Main:
  stage: deploy_main
  before_script:
    - echo "machine git.heroku.com login _apikey password $HEROKU_API_KEY" > ~/.netrc
  script:
    - git fetch
    - git checkout main
    - git remote add heroku $HEROKU_GIT_URL
    - git push heroku main
  only:
    - main

### Build Step for Review Apps (Merge Requests) ###
Build_Review:
  image: node:20
  stage: build
  script:
    - npm install
    - npm run build
  cache:
    key: knapsack-build
    paths:
      - node_modules
  rules:
    - if: $CI_MERGE_REQUEST_ID

### Deploy Step for Review Apps (Merge Requests) ###
Deploy_Review:
  stage: deploy_review
  before_script:
    - curl https://cli-assets.heroku.com/install.sh | sh
    - export HEROKU_API_KEY=$HEROKU_API_KEY
    - git config --global user.email "ci@example.com"
    - git config --global user.name "GitLab CI"
    - echo "machine git.heroku.com login _apikey password $HEROKU_API_KEY" > ~/.netrc
    - git fetch origin $CI_COMMIT_REF_NAME
    - git checkout $CI_COMMIT_REF_NAME
  script:
    - heroku apps:info --app $HEROKU_APP_NAME || heroku apps:create $HEROKU_APP_NAME --team your-team-here
    - heroku git:remote -a $HEROKU_APP_NAME
    - git push heroku $CI_COMMIT_REF_NAME:main --force
    - heroku ps:scale web=1
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    url: https://$HEROKU_APP_NAME.herokuapp.com
    on_stop: Stop_Review
  rules:
    - if: $CI_MERGE_REQUEST_ID

### Stop Review Step for Review Apps (Merge Requests) ###
Stop_Review:
  stage: cleanup
  before_script:
    - curl https://cli-assets.heroku.com/install.sh | sh
    - export HEROKU_API_KEY=$HEROKU_API_KEY
  script:
    - heroku apps:destroy --app $HEROKU_APP_NAME --confirm $HEROKU_APP_NAME || echo "App not found, skipping cleanup"
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    action: stop
  rules:
    - if: $CI_MERGE_REQUEST_ID
      when: manual
```

#### Step 4: Save the changes, push, and test!

**Let's get these changes merged in so we can test!**

1. After these changes are merged — let's create a new branch with a minor change.
2. Create an MR and verify the environment is created. Environments can be viewed via **Operate** » **Environments**.
3. When the build completes you should be able to view your first Deploy Preview! 🚀
4. Merge the MR and a couple things should happen:
   1. The Deploy Preview should destroy the review app within Heroku and clean up any resources used.
   2. The normal deploy to production should activate and move forward as expected. 🎉🎉🎉
5. All done!