S.B.
S.B.

Reputation: 367

AWS AppRunner not using .env var in Build Phase - Next.JS

I am trying to migrate from Elastic Beanstalk to AWS App Runner, but AWS App Runner does not seem to consider the environment variable during it's build phase.

Troubleshooting:

  1. npm run build (next build) - Works successfully locally and references my .env vars successfully.
  2. My existing elastic beanstalk implementation (shared below successfully gets the .env vars from github actions and are included in the build process).
  3. AppRunner the environment variables are currently set as plain-text, but language around around the build and start steps indicate that the variables are only available in the start phase, not the build phase.
  4. This works without issue on vercel.

Would really like to use AWS AppRunner out of the box, part of the reason of migrating to app runner was ease of use and quickly pushing code and having it published.

References:

Elastic Beanstalk:

name: Deploy to Elastic Beanstalk

on:
  push:
    branches:
      - main
      - dev
  workflow_dispatch:

env:
  BUCKET_NAME: marketbase-web
  DEV_APP_NAME: Marketbase-web-dev
  MAIN_APP_NAME: marketbase-web-prod-2
  DEV_ENV_NAME: Marketbase-web-dev-env
  MAIN_ENV_NAME: marketbase-web-prod-2-env

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:

    - name: Checkout code
      uses: actions/checkout@v2

    - name: Generate Last Updated Timestamp
      run: echo "NEXT_PUBLIC_LAST_UPDATED=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" > .env.local
      working-directory: ./

    - name: Log environment variables
      run: |
        echo "NEXT_PUBLIC_API_URL=${{ secrets.NEXT_PUBLIC_API_URL }}"
        echo "NEXT_PUBLIC_STANDARD_PRICE_ID=${{ secrets.NEXT_PUBLIC_STANDARD_PRICE_ID }}"

    - name: Set up Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '18'

    - name: Install dependencies
      run: npm install

    - name: Build Next.js project
      run: |
        if [ "${{ github.ref }}" == "refs/heads/dev" ]; then
          export NEXT_PUBLIC_API_URL=${{ secrets.DEV_NEXT_PUBLIC_API_URL }}
          export NEXT_PUBLIC_STANDARD_PRICE_ID=${{ secrets.DEV_NEXT_PUBLIC_STANDARD_PRICE_ID }}
          export NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${{ secrets.DEV_NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY }}
          export NEXT_PUBLIC_M_API_KEY=${{ secrets.DEV_NEXT_PUBLIC_M_API_KEY }}
          export NEXT_PUBLIC_CLOUDFRONT_URL=${{secrets.DEV_NEXT_PUBLIC_CLOUDFRONT_URL}}
          export NEXT_PUBLIC_ENVIRONMENT=${{secrets.DEV_NEXT_PUBLIC_ENVIRONMENT}}
        else
          export NEXT_PUBLIC_API_URL=${{ secrets.PROD_NEXT_PUBLIC_API_URL }}
          export NEXT_PUBLIC_STANDARD_PRICE_ID=${{ secrets.PROD_NEXT_PUBLIC_STANDARD_PRICE_ID }}
          export NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${{ secrets.PROD_NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY }}
          export NEXT_PUBLIC_M_API_KEY=${{ secrets.PROD_NEXT_PUBLIC_M_API_KEY }}
          export NEXT_PUBLIC_CLOUDFRONT_URL=${{secrets.PROD_NEXT_PUBLIC_CLOUDFRONT_URL}}
          export NEXT_PUBLIC_ENVIRONMENT=${{secrets.PROD_NEXT_PUBLIC_ENVIRONMENT}}

        fi
        npm run build
      env:
        NEXT_PUBLIC_API_URL: ${{ env.NEXT_PUBLIC_API_URL }}
        NEXT_PUBLIC_STANDARD_PRICE_ID: ${{ env.NEXT_PUBLIC_STANDARD_PRICE_ID }}
        NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: ${{ env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY }}
        NEXT_PUBLIC_M_API_KEY: ${{ env.NEXT_PUBLIC_M_API_KEY }}
        NEXT_PUBLIC_CLOUDFRONT_URL: ${{ env.NEXT_PUBLIC_CLOUDFRONT_URL }}
        NEXT_PUBLIC_ENVIRONMENT: ${{ env.NEXT_PUBLIC_ENVIRONMENT }}

    - name: Cache
      uses: actions/cache@v3
      with:
        # See here for caching with `yarn` https://github.com/actions/cache/blob/main/examples.md#node---yarn or you can leverage caching with actions/setup-node https://github.com/actions/setup-node
        path: |
          ~/.npm
          ${{ github.workspace }}/.next/cache
        # Generate a new cache whenever packages or source files change.
        key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
        # If source files changed but packages didn't, rebuild from a prior cache.
        restore-keys: |
          ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-        

    - name: Generate version label
      run: echo "VERSION_LABEL=$(git rev-parse --short HEAD)-$(date +%s)" >> $GITHUB_ENV

    - name: Zip the application
      run: zip -r ${VERSION_LABEL}.zip . -x '*.git*' 'node_modules/*'

    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-east-1 # Change to your desired region

    - name: Deploy to Dev Elastic Beanstalk on commit to dev branch
      if: github.ref == 'refs/heads/dev'
      run: |
        aws s3 cp ${VERSION_LABEL}.zip s3://$BUCKET_NAME/dev/${VERSION_LABEL}.zip
        aws elasticbeanstalk create-application-version --application-name $DEV_APP_NAME --version-label ${VERSION_LABEL} --source-bundle S3Bucket="$BUCKET_NAME",S3Key="dev/${VERSION_LABEL}.zip"
        aws elasticbeanstalk update-environment --environment-name $DEV_ENV_NAME --version-label ${VERSION_LABEL}

    - name: Deploy to Main Elastic Beanstalk on commit to main branch
      if: github.ref == 'refs/heads/main'
      run: |
        aws s3 cp ${VERSION_LABEL}.zip s3://$BUCKET_NAME/main/${VERSION_LABEL}.zip
        aws elasticbeanstalk create-application-version --application-name $MAIN_APP_NAME --version-label ${VERSION_LABEL} --source-bundle S3Bucket="$BUCKET_NAME",S3Key="main/${VERSION_LABEL}.zip"
        aws elasticbeanstalk update-environment --environment-name $MAIN_ENV_NAME --version-label ${VERSION_LABEL}

Config in AWS AppRunner:

AWS App Runner Config

next.config.js:

module.exports = {
    env: {
      NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL || 'default',
      NEXT_PUBLIC_CLOUDFRONT_URL: process.env.NEXT_PUBLIC_CLOUDFRONT_URL || 'default',
      NEXT_PUBLIC_ENVIRONMENT: process.env.NEXT_PUBLIC_ENVIRONMENT || 'default',
      NEXT_PUBLIC_LAST_UPDATED: process.env.NEXT_PUBLIC_LAST_UPDATED || 'default',
      NEXT_PUBLIC_M_API_KEY: process.env.NEXT_PUBLIC_M_API_KEY || 'default',
      NEXT_PUBLIC_STANDARD_PRICE_ID: process.env.NEXT_PUBLIC_STANDARD_PRICE_ID || 'default',
      NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY || 'default',
    },
  };

I've even tried using a manual apprunner file and it still will not include the environment variables in the build (they actually disappear from the AppRunner UI until I switch back to the configuration as opposed to apprunner.yaml file.

Upvotes: 1

Views: 240

Answers (1)

S.B.
S.B.

Reputation: 367

While I have not been able to directly answer the above, my workaround to deploying Next.JS on AppRunner has been to have github actions deploy to ECR and then use the container in AppRunner as opposed to the source reposistory.

GH Actions and Dockerfile for reference.

Github actions (root/.github/workflows/deploy.yml)

name: Build and Push Docker Image to ECR

on:
  push:
    branches:
      - main
      - dev
  workflow_dispatch:

env:
  AWS_REGION: us-east-1  # Change to your AWS region
  ECR_REPOSITORY_NAME_DEV: your-ecr-name-space/your-service-dev  ECR_REPOSITORY_NAME_MAIN: your-ecr-name-space/your-service-main

jobs:
  build-and-push:
    runs-on: ubuntu-latest

    steps:
      # 1. Checkout the repository
      - name: Checkout code
        uses: actions/checkout@v2

      # 2. Generate Last Updated Timestamp
      - name: Generate Last Updated Timestamp
        run: echo "NEXT_PUBLIC_LAST_UPDATED=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" > .env.local

      # 3. Set environment variables based on branch using repository variables
      - name: Set environment variables based on branch
        run: |
          if [ "${{ github.ref }}" == "refs/heads/dev" ]; then
            echo "NEXT_PUBLIC_VAR=${{ vars.DEV_NEXT_PUBLIC_VAR}}" >> $GITHUB_ENV
          else
            echo "NEXT_PUBLIC_VAR=${{ vars.PROD_NEXT_PUBLIC_VAR }}" >> $GITHUB_ENV
          fi

      # 4. Set up Node.js
      - name: Set up Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '18'

      # 5. Install dependencies
      - name: Install dependencies
        run: npm install

      # 6. Build Next.js project
      - name: Build Next.js project
        run: npm run build

      # 7. Configure AWS Credentials
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}

      # 8. Get AWS Account ID
      - name: Get AWS Account ID
        id: aws_account
        run: echo "AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)" >> $GITHUB_ENV

      # 9. Set ECR repository URI
      - name: Set ECR repository URI
        run: |
          echo "ECR_REPOSITORY_URI=${{ env.AWS_ACCOUNT_ID }}.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com/${{ env.ECR_REPOSITORY_NAME }}" >> $GITHUB_ENV

      # 10. Login to Amazon ECR
      - name: Login to Amazon ECR
        uses: aws-actions/amazon-ecr-login@v1

      # 11. Build Docker image
      - name: Build Docker image
        run: |
          docker build -t $ECR_REPOSITORY_URI:${{ github.sha }} \
            .

      # 12. Push Docker image to Amazon ECR
      - name: Push Docker image to Amazon ECR
        run: |
          docker push $ECR_REPOSITORY_URI:${{ github.sha }}

      # 13. (Optional) Tag the image as 'latest'
      - name: Tag and push latest Docker image
        run: |
          docker tag $ECR_REPOSITORY_URI:${{ github.sha }} $ECR_REPOSITORY_URI:latest
          docker push $ECR_REPOSITORY_URI:latest

Dockerfile (no extension) in root of app

# Use the official Node.js 18 image as the base
FROM node:18

# Set the working directory inside the container
WORKDIR /app

# Accept build arguments for environment-specific configurations
ARG NEXT_PUBLIC_VAR

# Set environment variables based on the build arguments
ENV NEXT_PUBLIC_VAR=${NEXT_PUBLIC_VAR}

# Copy package.json and package-lock.json to leverage Docker cache
COPY package*.json ./

# Install project dependencies
RUN npm install

# Copy the rest of the application code to the container
COPY . .

# Build the Next.js application for production
RUN npm run build

# Expose port 3000 to allow external access
EXPOSE 3000

# Define the default command to run the application
CMD ["npm", "start"]

Upvotes: 0

Related Questions