DEV Community

Cover image for Deploy a Next.js application on AWS using CDK & Fargate
Sonia
Sonia

Posted on

Deploy a Next.js application on AWS using CDK & Fargate

What is Fargate ?

Capture d’écran 2021-08-12 à 14.18.30.png

Why Next.js on AWS ?

The Next.js documentation says :

The easiest way to deploy Next.js to production is to use the Vercel platform from the creators of Next.js

Well, if you are just getting started with your project, i will say go for Vercel.

But in my case, i had all my projects on AWS using CDK, and i don't wanted to have to manage my Next.js app on another platform.

Let's get started

Let's create the main folder of the project

mkdir nextJsOnAws
cd nextJsOnAws
Enter fullscreen mode Exit fullscreen mode

NextJS part

npx create-next-app
Enter fullscreen mode Exit fullscreen mode

and name it the way you want (i named it front)

We are going to create a Dockerfile ( i am using the official one from the Vercel documentation)

# Install dependencies only when needed

FROM node:alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile

# Rebuild the source code only when needed

FROM node:alpine AS builder
WORKDIR /app
COPY . .
COPY --from=deps /app/node_modules ./node_modules
RUN yarn build && yarn install --production --ignore-scripts --prefer-offline

# Production image, copy all the files and run next

FROM node:alpine AS runner
WORKDIR /app

ENV NODE_ENV production

RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001


COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json

USER nextjs

EXPOSE 3000

CMD ["yarn", "start"]
Enter fullscreen mode Exit fullscreen mode

CDK part

Let's create a new CDK project on a dedicated folder

mkdir cdkPart
cd cdkPart
cdk init --language typescript
Enter fullscreen mode Exit fullscreen mode

Then let's install the package we need

npm install @aws-cdk/aws-ec2 @aws-cdk/aws-ecs @aws-cdk/aws-ecs-patterns
Enter fullscreen mode Exit fullscreen mode

In the lib folder, a file was created called cdk_part-stack, open it and complete it like this

import * as cdk from '@aws-cdk/core';
import * as ec2 from "@aws-cdk/aws-ec2";
import * as ecs from "@aws-cdk/aws-ecs";
import * as ecs_patterns from "@aws-cdk/aws-ecs-patterns";
import { DockerImageAsset } from "@aws-cdk/aws-ecr-assets";

export class CdkPartStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

 const APP_PORT = 3000
 const pathToDockerFile = "../front"

    const vpc = new ec2.Vpc(this, "MyVpc", {
      maxAzs: 2,
    });

    const taskDefinition = new ecs.FargateTaskDefinition(this, "MyTaskDefinition", {
      memoryLimitMiB: 512,
      cpu: 256,
    });

    const dockerFile = new DockerImageAsset(this, 'DockerFileAsset', {
      directory: pathToDockerFile,
      file: 'Dockerfile',
    });

    // cdk will build it and push it to en ecr repository
    const image = ecs.ContainerImage.fromDockerImageAsset(dockerFile);

    const container = taskDefinition.addContainer("MyContainer", {
      image,
      // store the logs in cloudwatch 
      logging: ecs.LogDriver.awsLogs({ streamPrefix: "myexample-logs" })
    });

    container.addPortMappings({
      containerPort: APP_PORT, 
    });

    const cluster = new ecs.Cluster(this, "MyECSCluster", {
      clusterName: "MyECSCluster",
      containerInsights: true,
      vpc,
    });

    const securityGroup = new ec2.SecurityGroup(this, `My-security-group`, {
      vpc: vpc,
      allowAllOutbound: true,
      description: 'My Security Group'
    });

    securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(APP_PORT));

    const fargateService = new ecs_patterns.ApplicationLoadBalancedFargateService(this, 'MyFargateService', {
      cluster,
      publicLoadBalancer: true,
      cpu: 256,
      desiredCount: 1,
      memoryLimitMiB: 512,
      taskDefinition,
      securityGroups: [securityGroup]
    })

    const scalableTarget = fargateService.service.autoScaleTaskCount({
      minCapacity: 1,
      maxCapacity: 2
    })

    scalableTarget.scaleOnCpuUtilization('cpuScaling', {
      targetUtilizationPercent: 70
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

Now time for deploy

cdk synth
Enter fullscreen mode Exit fullscreen mode

Then after that :

cdk deploy
Enter fullscreen mode Exit fullscreen mode

The deploy can be a little long , so be patient :)

Let's go to the console

After the deploy is completed, go to your AWS Console > ECS (for Elastic Container Service)

Capture d’écran 2021-08-18 à 19.32.59.png

Click on MyECSCluster

Capture d’écran 2021-08-18 à 19.34.07.png

Click on the service

Capture d’écran 2021-08-18 à 19.34.53.png

Select the Task Tab

Capture d’écran 2021-08-18 à 19.35.03.png

Click on the Task

Capture d’écran 2021-08-18 à 19.37.35.png

You can see that your container is ... RUNNING :)

Let's see the app !!!

Go to the console > EC2 > LoadBalancer and copy the DNS name of your load balancer

Capture d’écran 2021-08-18 à 19.38.43.png

Paste it in your browser and ...

Capture d’écran 2021-08-18 à 19.40.10.png

Congrats ! You deployed your Next.js app with Fargate and it's running :)

Cleaning

Don't forget to clean after this tutorial, i don't want you to have a huge bill on AWS!

cdk destroy
Enter fullscreen mode Exit fullscreen mode

I hope you liked this tutorial, i will try my best to post some articles about AWS.

If you want, you can follow me on Twitter :)

Top comments (2)

Collapse
 
kamlesh_bar profile image
kamlesh bar

Will this work with NextJS SSR ? and how to manage env variables ?

Collapse
 
zoun profile image
Sonia

You can add env variables like this :

taskDefinition.addContainer('container', {
  image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
  memoryLimitMiB: 1024,
  environment: { // clear text, not for sensitive data
    STAGE: 'prod',
  },
  environmentFiles: [ // list of environment files hosted either on local disk or S3
    ecs.EnvironmentFile.fromAsset('./demo-env-file.env'),
    ecs.EnvironmentFile.fromBucket(s3Bucket, 'assets/demo-env-file.env'),
  ],
});
Enter fullscreen mode Exit fullscreen mode

Honestly, i haven't tried it with SSR, so i cannot give you an answer for that.