Deploy a Next.js App with CI/CD

Build a CI/CD pipeline that auto-deploys a Next.js app to Elastic Beanstalk.

Introduction

30 Second Summary

Every time a developer finishes writing code, someone has to get that code onto a live server where users can access it. Doing this manually is slow, error-prone, and doesn't scale when your team pushes updates multiple times a day.

In this project, you will build a fully automated deployment pipeline that detects when you push code to GitHub and deploys your application to a live URL on AWS without any manual steps.

What You'll Build

A complete CI/CD pipeline using AWS CodePipeline, AWS CodeBuild, and AWS Elastic Beanstalk that automatically builds and deploys a Next.js application whenever you push code to GitHub.

By the end of this project, you'll have:

  • A buildspec.yml and Procfile that define how your app is built and started on AWS.
  • A live Elastic Beanstalk environment running your Next.js app with environment variables configured.
  • A CodePipeline with Source, Build, and Deploy stages that triggers automatically on every GitHub push.
  • Secret Mission: Add a manual approval stage between Build and Deploy to simulate a production release gate.

Are there any prerequisites?

You'll need an AWS account with the AWS CLI installed, a GitHub account with a forked Next.js repository, and Git installed on your machine. No additional tool installations are required beyond these.

Prepare Your Repository for Deployment

Your Next.js application lives in a GitHub repository, but AWS has no idea how to build or run it. Before you can set up a deployment pipeline, you need to add configuration files that give AWS exact instructions.

In this step, you'll clone your forked repository locally, verify it runs, then create two files: a buildspec.yml for AWS CodeBuild and a Procfile for AWS Elastic Beanstalk.

In this step, get ready to:
  • Clone the Next.js repository and verify it runs locally.
  • Create a buildspec.yml file that defines automated build phases.
  • Create a Procfile and push both files to GitHub.
Clone your forked repository

You already have a forked Next.js repository on your GitHub account. Now you need a local copy to work with.

  • Press the Windows key to open your search bar.
  • Type Git Bash and press Enter to open it.
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Clone your forked repository by running this command:
git clone [[YOUR_REPO_URL="https://github.com/your-username/your-nextjs-repo.git"]]
  • Move into the cloned project folder by running:
cd [[REPO_FOLDER="your-cloned-repo-name"]]
  • Install the project dependencies by running:
npm install

You should see packages being downloaded and installed.

  • Start the development server by running:
npm run dev

You should see output indicating the server is running at http://localhost:3000. Open that URL in your browser to confirm the app loads.

  • Once you've confirmed the app works, stop the development server by pressing Ctrl+C in your terminal.

App not loading at localhost:3000?

Make sure npm install completed without errors before running npm run dev. If you see dependency errors, delete the node_modules folder and run npm install again.

If port 3000 is already in use, the terminal will show an error. Close any other applications using that port and try again.

Help me troubleshoot my local Next.js app

Create the buildspec.yml file

AWS CodeBuild needs a file called buildspec.yml to know how to build your application. This file defines the install and build commands that run every time your pipeline triggers.

  • In the root of your project folder (the same folder that contains package.json), create a new file called buildspec.yml.
  • Paste the following content into buildspec.yml:
version: 0.2

phases:
  install:
    runtime-versions:
      nodejs: 20
    commands:
      - echo Installing dependencies...
      - npm ci
  build:
    commands:
      - echo Building the Next.js application...
      - npm run build
artifacts:
  files:
    - "**/*"
cache:
  paths:
    - "node_modules/**/*"

What does this file do?

  • version: 0.2 tells CodeBuild which buildspec schema to use.
  • The install phase sets the Node.js runtime to version 20 and runs npm ci to install dependencies from your lockfile.
  • The build phase runs npm run build to compile your Next.js app into production-ready output.
  • The artifacts section with "**/*" bundles all files (including node_modules and the .next build output) into a ZIP for deployment.
  • The cache section speeds up future builds by caching node_modules between runs.
  • Save buildspec.yml.
  • Confirm the file exists by running:
ls buildspec.yml

You should see buildspec.yml printed in the output.

File not found?

Make sure you created buildspec.yml in the project root (the same directory as package.json), not inside a subfolder. Check that the filename is exactly buildspec.yml with no extra extensions like .txt.

Help me find my buildspec.yml file

✔️ Awesome, I've got everything!

Great. Double check you've saved buildspec.yml before moving on.

ⓧ I'd like to double check the full code

Here is the complete buildspec.yml file:

version: 0.2

phases:
  install:
    runtime-versions:
      nodejs: 20
    commands:
      - echo Installing dependencies...
      - npm ci
  build:
    commands:
      - echo Building the Next.js application...
      - npm run build
artifacts:
  files:
    - "**/*"
cache:
  paths:
    - "node_modules/**/*"
Create a Procfile and push your changes

Elastic Beanstalk needs to know how to start your application after deployment. A Procfile gives it the exact startup command.

  • In the same project root, create a new file called Procfile (no file extension).
  • Paste the following content into Procfile:
web: npm run start

What does the Procfile do?

The web: prefix tells Elastic Beanstalk this is the main web process. The command npm run start launches your Next.js app in production mode after npm run build has already been run by CodeBuild.

  • Save Procfile.
  • Confirm both new files exist by running:
ls buildspec.yml Procfile

You should see both filenames printed in the output.

  • Stage, commit, and push both files to your GitHub repository by running these commands:
git add buildspec.yml Procfile
git commit -m "Add buildspec.yml and Procfile for AWS deployment"
git push origin main

You should see output confirming the push to your main branch was successful.

Push rejected or authentication error?

If git push fails with an authentication error, make sure you're signed into GitHub. Git on Windows may prompt you to authenticate via a browser window. Follow the prompt to authorize access.

If the push is rejected because the remote has changes you don't have, run git pull origin main first, then try pushing again.

Help me fix my git push error

✔️ Awesome, I've got everything!

Great. Double check you've pushed both files by visiting your repository on GitHub and confirming buildspec.yml and Procfile appear in the root directory.

ⓧ I'd like to double check the full code

Here is the complete Procfile:

web: npm run start

Your repository is now ready for AWS. Next up, you'll create the Elastic Beanstalk environment where your app will actually run.

Create the Elastic Beanstalk Application and Environment

Your code is ready in GitHub with a buildspec.yml and Procfile telling AWS how to build and run it. Now you need somewhere for the app to actually live.

AWS Elastic Beanstalk handles the infrastructure for you. It provisions an EC2 instance, sets up a reverse proxy, and gives you a public URL. You just tell it what platform to use and it takes care of the rest.

In this step, get ready to:
  • Create an Elastic Beanstalk application and environment running Node.js 20.
  • Configure the environment variables your Next.js app needs.
  • Verify the environment is healthy and accessible via its public URL.
Create the Elastic Beanstalk application and environment

Elastic Beanstalk groups your infrastructure under an application, and each application has one or more environments. You will create both now using the AWS console wizard.

  • Open the AWS Management Console and sign in to your AWS account.
  • In the top search bar, type Elastic Beanstalk and select the service from the results.
  • Click Create application.
  • For Application name, enter a name like nextjs-app.
  • Under Configure environment, choose Web server environment.
  • For Platform, select Node.js from the dropdown. The Platform branch and Platform version fields auto-populate with the latest Node.js 20 on Amazon Linux 2023 defaults.
  • For Application code, confirm that Sample application is selected.
  • For Presets, select Single instance (free tier eligible).
  • Click Next.

Why single instance?

The Single instance preset creates one EC2 instance with an Elastic IP and no load balancer. A load-balanced environment would create an Application Load Balancer, which is not free-tier eligible.

Single instance keeps you firmly within the AWS Free Tier while still giving you a public URL to access your app.

The wizard now moves to the Configure service access page. This is where you assign IAM roles that let Elastic Beanstalk manage EC2 instances on your behalf.

  • For Service Role, choose Use an existing service role if one is available. If no service role exists, select Create role and follow the prompts to create one.
  • For EC2 instance profile, select aws-elasticbeanstalk-ec2-role from the dropdown if it appears.

No values in the EC2 instance profile dropdown?

If the dropdown is empty, you need to create a new IAM role. Click Create role. For Trusted entity type, choose AWS service. For Use case, choose Elastic Beanstalk – Compute.

Verify that permissions include AWSElasticBeanstalkWebTier, AWSElasticBeanstalkWorkerTier, and AWSElasticBeanstalkMulticontainerDocker. Click Create role, then return and refresh the list.

Help me create the EC2 instance profile role for Elastic Beanstalk.

  • Once both roles are selected, click Skip to Review on the Configure service access page.
  • On the Review page, verify your choices and click Submit at the bottom.

Elastic Beanstalk begins creating your environment. This takes a few minutes. Wait until the environment health indicator turns green before continuing.

What is Elastic Beanstalk doing right now?

Behind the scenes, Elastic Beanstalk is launching a t2.micro EC2 instance, configuring an NGINX reverse proxy, assigning a public URL, and deploying the sample Node.js application. All of this happens without you touching any server configuration.

Configure environment variables

Your Next.js app expects several environment variables at runtime. You will add them through the Elastic Beanstalk configuration page now that the environment is healthy.

  • In the Elastic Beanstalk console navigation pane, click Environments, then select your environment name.
  • In the left navigation pane, click Configuration.
  • In the Updates, monitoring, and logging configuration category, click Edit.
  • Scroll down to the Runtime environment variables section.
  • Click Add environment variable. For Source, select Plain text.
  • Add the following four environment variables, clicking Add environment variable before each new entry:
  • Name: DATABASE_URL / Value: your database connection string (use a placeholder like placeholder if you do not have a database yet).
  • Name: AUTH_SECRET / Value: any random string for now (this is used by NextAuth for session encryption).
  • Name: NEXTAUTH_URL / Value: your environment URL, for example http://your-env-name.
  • Name: PORT / Value: 8080.
  • After entering all four variables, click Apply at the bottom of the page.

Where do I find my environment URL?

Your environment URL appears at the top of the environment dashboard page, just below the environment name. It looks like http://your-env-name.us-east-1.elasticbeanstalk.com. Copy and paste this as the value for NEXTAUTH_URL.

Why set PORT to 8080?

Elastic Beanstalk's NGINX reverse proxy forwards incoming traffic to your application on a specific port. Setting the PORT environment variable tells both the proxy and Next.js which port to use for communication.

The environment will update and restart to apply the new variables. Wait for the health status to return to green.

Verify the sample application is accessible

Before connecting your pipeline, confirm the environment itself is working by visiting the sample app that Elastic Beanstalk deployed as a placeholder.

  • In the Elastic Beanstalk console, navigate to your environment dashboard.
  • Click the URL displayed at the top of the page (under the environment name). This opens in a new browser tab.
  • You should see the Elastic Beanstalk sample application page confirming your environment is running.

Environment health is red or gray?

If the environment shows degraded or severe health, check the Events tab in the environment dashboard for error messages. Common causes include missing IAM permissions or a failed instance launch in the selected region.

Make sure you selected the Single instance preset and that your account has not exceeded the free-tier EC2 instance limit.

Help me fix my Elastic Beanstalk environment health issues.

Your Elastic Beanstalk environment is live and healthy. Next up, you will create the CodePipeline that automatically pushes your code from GitHub through a build stage and deploys it here.

Create the CodePipeline with Source, Build, and Deploy Stages

Your Elastic Beanstalk environment is healthy and ready to receive deployments. Now you need to connect the dots between your GitHub repository and that environment.

In this step, you will create an AWS CodePipeline that automatically pulls code from GitHub, builds it with AWS CodeBuild, and deploys the result to your Elastic Beanstalk environment.

In this step, get ready to:
  • Connect your GitHub account to AWS using a GitHub App connection.
  • Create a CodeBuild project to build your Next.js app.
  • Create a three-stage CodePipeline that automates your entire deployment workflow.
Create a new pipeline and connect GitHub as the source

The pipeline wizard walks you through configuring each stage. You will start by naming the pipeline and connecting your GitHub repository as the source.

  • In the AWS Management Console, search for CodePipeline and open the CodePipeline console.
  • Click Create pipeline.
  • On the Step 1: Choose creation option page, under Creation options, select Build custom pipeline.
  • Click Next.

Why is V2 the only pipeline type available?

AWS CodePipeline V2 is the only type you can choose in the console. V2 pipelines include additional features like Git tag triggers and pipeline-level variables. V2 provides 100 free action execution minutes per month, which is more than enough for this project.

  • On the Step 2: Choose pipeline settings page, in Pipeline name, enter a name for your pipeline (e.g., nextjs-pipeline).
  • In Service role, choose New service role. This allows CodePipeline to create a service role in IAM with the permissions it needs.
  • Leave the settings under Advanced settings at their defaults. The Artifact store set to Default location automatically creates an S3 bucket to store your pipeline artifacts.
  • Click Next.

Now you will configure the source stage to pull code from your GitHub repository.

  • On the Step 3: Add source stage page, in Source provider, choose GitHub (via GitHub App).
  • Under Connection, click Connect to GitHub. This opens a new page to set up the connection.

What is a GitHub App connection?

AWS uses the GitHub App connection method (via CodeStar Connections) to securely link your AWS account to GitHub. This replaces the older OAuth token method. It installs a small app on your GitHub account that handles authentication and webhook-based change detection automatically.

  • On the Connect to GitHub page, your connection name is pre-filled. Click Connect to GitHub.
  • On the access request page that appears, click Authorize AWS Connector for GitHub.
  • Under GitHub Apps, click Install a new app.
  • On the Install AWS Connector for GitHub page, choose the GitHub account where your forked repository lives.
  • Leave the defaults and click Install.
  • You are returned to the Connect to GitHub page. The connection ID for your new installation appears under GitHub Apps. Click Connect.

GitHub authorization page not appearing?

Make sure your browser is not blocking pop-ups from the AWS console. You may also need to sign in to GitHub in a separate tab first if you are not already logged in.

If you see a permissions error, confirm you are the owner of the GitHub account or organization where the repository lives.

  • Back on the source stage page, in Repository name, select your forked Next.js repository from the dropdown.
  • In Default branch, choose main.
  • Leave the Output artifact format set to CodePipeline default.
  • Click Next.
Configure the build stage with CodeBuild

The build stage compiles your Next.js application using the buildspec.yml you pushed to your repository earlier. You will create a new CodeBuild project from within the pipeline wizard.

  • On the Step 4: Add build stage page, in Build provider, choose AWS CodeBuild.
  • Click Create project. A new window opens for CodeBuild project configuration.

What is a CodeBuild project?

A CodeBuild project defines the build environment and instructions for compiling your code. It uses a Docker container with the tools you specify (Node.js, in our case) and follows the commands in your buildspec.yml file.

  • In Project name, enter a name for your build project (e.g., nextjs-build).
  • In Environment image, choose Managed image.
  • For Operating system, choose Amazon Linux.
  • For Runtime, choose Standard.
  • For Image, select the latest available image from the dropdown.
  • For Service role, choose New service role.
  • Under Buildspec, confirm that Use a buildspec file is selected. This tells CodeBuild to use the buildspec.yml from your source code.
  • Click Continue to CodePipeline. This creates the CodeBuild project and returns you to the pipeline wizard.

Why build.general1.small?

The build.general1.small compute type is selected by default. It provides 100 free build minutes per month (Always Free tier). A typical Next.js build takes 2-4 minutes, so you can trigger dozens of builds within the free tier.

  • Back on the build stage page, confirm your new project name appears in the Project name field.
  • Click Next.
  • On the Step 5: Add test stage page, click Skip test stage. Accept the warning by clicking Skip again.
Add the deploy stage and create the pipeline

The final stage tells CodePipeline where to send the build output. You will point it at the Elastic Beanstalk application and environment you created in the previous step.

  • On the Step 6: Add deploy stage page, in Deploy provider, choose AWS Elastic Beanstalk.
  • In Application name, select the Elastic Beanstalk application you created in Step 2.
  • In Environment name, select the environment you created in Step 2.
  • Click Next.
  • On the Step 7: Review page, review all your pipeline settings. You should see three stages listed: Source (GitHub), Build (CodeBuild), and Deploy (Elastic Beanstalk).
  • Click Create pipeline.

After you click Create pipeline, the pipeline immediately starts its first execution. You will see the pipeline view with all three stages showing their progress.

Pipeline creation failed?

If you see a permissions error, make sure you selected New service role for both the pipeline and the CodeBuild project. AWS needs to create IAM roles with the correct permissions.

If the GitHub connection is listed as Pending, go back to the Connection dropdown on the source stage and confirm the connection shows as Available.

Your pipeline is now live and running its first execution. In the next step, you will monitor this execution and test the full CI/CD loop by pushing a code change.

Test the Full CI/CD Pipeline

Your pipeline is fully assembled with three stages connected end to end. It already kicked off its first execution the moment you finished creating it in the previous step.

Now you'll verify that first run completes successfully, then prove the automation works by pushing a real code change. If the pipeline catches the change and deploys it without you touching the AWS console, the full CI/CD loop is confirmed.

In this step, get ready to:
  • Monitor the first pipeline execution through all three stages.
  • Push a code change to GitHub and confirm the pipeline triggers automatically.
  • Verify the updated application is live at your Elastic Beanstalk URL.
Monitor the first pipeline execution

The pipeline's first run is already in progress. Each stage runs sequentially. Source pulls your code from GitHub, Build compiles it with CodeBuild, and Deploy pushes the built artifacts to Elastic Beanstalk.

  • Return to the CodePipeline console. You should already see your pipeline listed.
  • Click on your pipeline name to open the execution view.
  • Watch the graphical representation showing each stage. A green checkmark appears on a stage when it completes successfully.
  • Wait for all three stages (Source, Build, Deploy) to show green checkmarks.

What should I see?

The pipeline view shows three boxes arranged left to right: Source, Build, and Deploy. Each box starts as blue (in progress), then turns green (succeeded) or red (failed).

A full run typically takes 3-5 minutes. The Build stage takes the longest because it installs dependencies and compiles your Next.js app.

Build stage failed?

Click View details on the failed Build action to open the build details. Scroll down to the Build logs section to see what went wrong.

  • If you see a YAML parsing error, double-check that your buildspec.yml indentation uses spaces (not tabs) and matches the structure from Step 1.
  • If you see npm ci failures, confirm that your repository has a package-lock.json file committed.
  • If the build times out, check that you selected build.general1.small compute type when creating the CodeBuild project.

Fix the issue, push the fix to GitHub, and the pipeline will automatically retry. Help me debug my CodeBuild failure

Push a code change to trigger the pipeline

A working pipeline is only useful if it responds to new code automatically. The GitHub App connection you set up earlier sends a webhook to CodePipeline every time you push to the main branch.

  • Open the home page file of your Next.js app in your code editor. This is typically app/page.tsx or pages/index.tsx depending on your project structure.
  • Find a visible piece of text on the page (such as a heading or paragraph) and change it to something new. For example, update a heading to say Deployed with CI/CD.
  • Save the file.
  • Commit and push your change to GitHub by running these commands in your terminal:
git add .
git commit -m "Update home page text"
git push origin main

What do these commands do?

  • git add . stages all changed files for commit.
  • git commit -m "Update home page text" creates a snapshot of your changes with a descriptive message.
  • git push origin main sends the commit to GitHub, which triggers the webhook to CodePipeline.
  • Return to the CodePipeline console and click on your pipeline name.
  • Within a minute, you should see a new execution start automatically in the Source stage.

Pipeline not triggering?

  • Confirm you pushed to the main branch (not a different branch). The pipeline only watches the branch you configured.
  • Check that the GitHub connection in the pipeline's Source stage still shows as Available. If it shows Error, reconnect through Settings > Connections in the Developer Tools console.
  • Wait up to 60 seconds. Webhook delivery is near-instant, but the console may take a moment to refresh.

Help me troubleshoot why my pipeline isn't triggering on push

Confirm your change is live

Once the Deploy stage turns green, your updated code is running on Elastic Beanstalk. This is the moment that proves the full loop works.

  • Wait for all three stages of the second execution to complete with green checkmarks.
  • Open a new browser tab and navigate to your Elastic Beanstalk environment URL (the same URL from Step 2, e.g., http://your-env-name.us-east-1.elasticbeanstalk.com).
  • Confirm that the text change you made is now visible on the live page.

What just happened?

You pushed code to GitHub. CodePipeline detected the push via the GitHub App webhook, pulled the latest source, handed it to CodeBuild for compilation, and deployed the result to Elastic Beanstalk.

This is exactly how professional teams ship code. Every future push to main follows this same automated path without any manual intervention.

Page still showing old content?

  • Hard-refresh your browser with Cmd+Shift+R (macOS) or Ctrl+Shift+R (Windows) to bypass the cache.
  • Check that the Deploy stage shows green in the CodePipeline console. If it is still in progress, wait for it to finish.
  • If Deploy failed, click View details on the Deploy action. Common causes include environment health issues or Elastic Beanstalk still processing the previous deployment.

Help me figure out why my deployed app isn't showing my latest changes

Secret mission

Add a Manual Approval Stage

In real production environments, teams never deploy code straight to users without a human check. Your pipeline currently ships every push automatically. Add a manual approval gate between Build and Deploy so that deployments only proceed when someone reviews and approves the change.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project uses AWS Free Tier eligible resources, but the EC2 instance in your Elastic Beanstalk environment will incur charges if your account is past the 12-month free tier window.

Cost warning

If your AWS account is past the 12-month free tier window, the t2.micro instance costs approximately $0.0116/hour (~$8.50/month). Always terminate the Elastic Beanstalk environment when you are done to stop EC2 charges.

Resources you used:

  • Elastic Beanstalk environment (EC2 t2.micro instance running continuously, free-tier eligible for 12 months).
  • CodePipeline (V2, 100 free action execution minutes/month).
  • CodeBuild project (100 free build minutes/month on build.general1.small).
  • S3 artifact bucket (auto-created by CodePipeline for pipeline artifacts).
  • GitHub connection (CodeStar Connections/CodeConnections).

Keep everything running

No action needed. Choose this if you are still actively building or want to practice pushing more changes through the pipeline.

  • Monitor your free tier usage in the AWS Billing console to avoid surprise charges.
  • Your Elastic Beanstalk environment, CodePipeline, and CodeBuild project will remain active.
  • The GitHub connection stays available for future pipelines.

Pause - I'll come back to this later

Elastic Beanstalk environments cannot be paused directly. Costs only accrue from the running EC2 instance.

  • To stop ongoing EC2 charges, terminate the Elastic Beanstalk environment (follow the Delete tab instructions for the environment only).
  • Keep your CodePipeline and CodeBuild project intact so you can recreate the environment and redeploy later.
  • Your source code on GitHub remains untouched regardless of what you do in AWS.

Delete - I don't want to use this again

Remove all project resources to start fresh. Follow these steps in order.

  • Terminate the Elastic Beanstalk environment:
  • Delete the Elastic Beanstalk application:
  • Delete the CodePipeline:
  • Delete the CodeBuild project:
  • Empty and delete the S3 artifact bucket:
  • Delete the GitHub connection (optional, only if you will not use it for other pipelines):

That's a Wrap!

That's a Wrap!

Nice work! You just built a fully automated CI/CD pipeline that deploys your Next.js application to a live AWS environment every time you push code to GitHub.

You've learned how to:

  • Build a complete CI/CD pipeline using AWS CodePipeline, CodeBuild, and Elastic Beanstalk to automate your deployment workflow.
  • Configure a buildspec.yml and Procfile to define how your application is built and started in the cloud.
  • Test the full pipeline loop: push code to GitHub, trigger an automatic build, and deploy to a live environment with no manual intervention.
  • Secret Mission: Added a manual approval gate to simulate production deployment controls used by professional engineering teams.

Ready to quiz yourself?