How to deploy your app on AWS with Docker
Tags: docker, devops, aws, showdev
Published: 2020-05-03 05:17:49

I will be explaining how to deploy your dockerized app in AWS using a Fargate instance, the process for an EC2 instance should be very similar, so this guide will also work for you.

---

In a previous [article](https://dev.to/edgargonzalez525/how-to-easily-dockerize-your-nestjs-app-46nf), I explained how to dockerize a NestJs app, for the purpose of this guide I will be using the same codebase to create the Docker image that will be deployed in AWS.

To follow these steps I will assume you have the following:

1. A Docker script to build your image.
2. The [AWS cli](https://aws.amazon.com/cli/) configured in your machine.
3. Some basic knowledge of AWS and Docker.

---

#Step 1 - Docker repository

You need to create a Docker repository in your AWS account, it can be done executing the command below:

`aws ecr create-repository --repository-name=<YOUR_REPOSITORY_NAME>`

After your repository is created go to `Elastic Container Service -> Amazon ECR -> Repositories` in your AWS account. There look for your newly created repository and click on it. Once your repo is opened click on the `View push commands` button. You will get something like this:

![View push commands](https://dev-to-uploads.s3.amazonaws.com/i/2lc79v2ndd5cusm1fszx.png)

**Copy all these commands, as you will need them later on to build, push and deploy your app.**

To avoid copying these commands every time we build our app, we can create some scripts in our `package.json` to automate the process.

> If you are not deploying a JS app, you can use a shell script or some other tool you feel comfortable with.

---

#Step 2 - Automated script

To make our lives easier we will setup the following scripts in the `package.json` file:

```json
...
"aws:login": "aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin <ID>.dkr.ecr.us-west-2.amazonaws.com/nestjs",
"docker:build": "docker build -t nestjs .",
"docker:tag": "docker tag nestjs:latest <ID>.dkr.ecr.us-west-2.amazonaws.com/nestjs:latest",
"docker:push": "docker push <ID>.dkr.ecr.us-west-2.amazonaws.com/nestjs:latest",
...
```

> Remember to replace all these commands with the ones you copied from your repository.

Let me explain what each one of these commands do:

1. `aws:login` - Executed the login command so you can properly authenticate with your AWS cli.
2. `docker:build` - Build your Docker image using your local `Docker` script.
3. `docker:tag` - Tag your recently created image with the name you copied from your repository.
4. `docker:push` - Push your image to the AWS server.

I will also add a new script, to execute all the previous commands in secuence (yes, I'm a bit lazy, I don't want to type all those commands every time I build my image).

```json
...
"aws:build": "npm run aws:login && npm run docker:build && npm run docker:tag && npm run docker:push",
...
```

Now if you run the `aws:build` command (`npm run aws:build`), you will build, tag and push your Docker image into your AWS account. We will use this on the next steps to deploy your app. After executing the command this is how my repository looks:

![Repository images](https://dev-to-uploads.s3.amazonaws.com/i/nr1nst5fb4ha576hf3vj.jpg)

---

# Step 3 - Create Task Definition

Before creating your task definition make sure you have a role with the [AmazonECSTaskExecutionRolePolicy](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html).

Now, you need to create a Task definition that will be later used by a service to create the instances of your Docker image. To create a Task definition go to `Elastic Container Service -> Amazon ECS -> Task Definitions`, and click on the `Create new Task Definition` button and follow the next steps:

* Select launch type compatibility - Fargate.

![Task definition type](https://dev-to-uploads.s3.amazonaws.com/i/bxiikf205sqddbvdasrv.png)

* Set your task name and execution role.

![Task creation](https://dev-to-uploads.s3.amazonaws.com/i/3scralxwj6i3pws3p8ox.png)

* Set task size (this will change depending on your needs).

![Task size](https://dev-to-uploads.s3.amazonaws.com/i/piwfpf6vv2cxr8vtw7oo.png)

* Create task container (for the purpose of the guide I won't go deep on each of the options you can specify here, I will keep it simple for now).

> For the Image field you need to specify the URL of your repository.

![Task container](https://dev-to-uploads.s3.amazonaws.com/i/u60bjxosiqk03tu6gdov.png)

* Save your task.

---

#Step 4 - Create Cluster

On this step you will create the cluster that will be used to create the services to deploy your app.

* Select Networking Only cluster as we will be creating Fargate instances.

![Cluster creation](https://dev-to-uploads.s3.amazonaws.com/i/82dfumggzc6op8fojb6x.png)

* Set cluster name, check the "Create VPC" checkbox and save.

![Cluster name](https://dev-to-uploads.s3.amazonaws.com/i/wrxctv2yot87lb3lz4gs.png)

---

#Step 5 - Create Service

Go to the services tab inside your recently created cluster and click on the `Create` button.

* Set your service type as Fargate, your task definition and cluster as the ones you created, for the service name I usually have the `-service` suffix to identify them easier. And for this case we will set the number of instances as 1 (this will depend on your needs).

![Fargate service](https://dev-to-uploads.s3.amazonaws.com/i/cgls5gzi7i065inrmx48.png)

* Set the VPC value with the one that was created with the cluster and set some subnets where your service will be available, also set the "Auto-assign public IP" property to "Enabled". For the rest of the fields you can keep them as their default values and click the `Next step` button.

![Service creation](https://dev-to-uploads.s3.amazonaws.com/i/oxqn1q37gsjuuxxpnqs8.png)

* For the Auto-scaling options, keep the default values and click the `Next step` button.

* Click `Create Service` and wait for your service to be created.

Now that you have created your service, let's check our deployed app. First you need to go to the task that was created by your service.

![Service Task](https://dev-to-uploads.s3.amazonaws.com/i/0pvfo9jl86zvxvzwote3.png)

Here you can find the Public IP address of your instance. And looking for that IP directly on your browser should show your running APP.

![Service Task IP](https://dev-to-uploads.s3.amazonaws.com/i/o67djzbvoqntttxz62mh.png)

**Running APP**

![Running App](https://dev-to-uploads.s3.amazonaws.com/i/ydwlpubwhr34luqxr521.png)

> NOTE: if your app is not running on the port `80` you will need to open the port your container is exposing on the security group that your service is using, this can be done on your security group inbound rules.


Awesome!! You made it!! now your app is running on a Fargate container in AWS, but, what if we need to make some changes in our app? How do we update our Docker image and re-deploy the new changes?. I will talk about it in the next step.

---

#Step 6 - Re-Deploy your app with new changes.

Imagine that we got a new requirement for a new feature on our app, and we need to push those changes to our Fargate container, for that there are a couple of steps we need to follow:

1. Build Docker image with new changes.
2. Tag Docker image.
3. Push Docker image.
4. Deploy container with the new changes.

The first 3 steps we got them covered on the scripts we created before, we only need one last script.

```json
"aws:deploy": "aws ecs update-service --cluster test-cluster --service nestjs-service --force-new-deployment --region=<YOUR_REGION>",
```

This command will force your service to re-create with the latest changes from the Docker image. After making new changes on your app you can run the following command:

`npm run aws:build && npm run aws:deploy`

This command will build, tag, push and re-create the container with the latest changes.

You can find the source code for this guide [here](https://github.com/edgargonzalez525/nestjs-cron).