# Building Scalable GO Application With Docker, AWS, and GitHub Actions

# Introduction

* In the last part, we successfully put the Go application in a Docker container. Now, we need to deploy it so others can access it.
    
* First, we need to upload our Docker application to Docker Hub. We'll start with that and then set up a GitHub workflow action to help us deploy our application to AWS EC2.
    
* You might wonder why we use GitHub Actions. In real-world projects, things change often, and you can't manually deploy the application every time. To automate this, we use a tool like GitHub Actions.
    
* Let's begin with Docker Hub, and then we'll create the workflow file.
    

---

# Upload Go Application to DockerHub

* * DockerHub is like GitHub but for Docker images. It's an online place where developers can find and share Docker images.
        
        \* You make your custom image on your computer and push it to DockerHub for others to use.
        
        \* As developers, we just need to pull these images using the command `docker pull`, and we're ready to go.
        
        \* Now, let's upload our Go application to DockerHub.Step 1: Login to Dockerhub
        
* First, we need to log into the docker hub in order to upload.
    
* Run the below command.
    

```bash
docker login
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739417596170/b7a870f2-f440-4c08-a73a-6843d6316bf1.png align="center")

* This takes you to the browser where you can log in or sign up. Once you do that, you'll see a message confirming your login was successful.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739417784101/9f64319c-18fb-4a19-a2f9-81bfff37de27.png align="center")
    
    * Once you are logged in, go to `hub.docker.com`
        

## Create Repository

* Just like GitHub needs a repository to store code, Docker Hub needs a repository to store Docker images. Go to the `Repositories` tab in the Navbar.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739417959111/465bb798-4b5e-473a-aa3d-a1c789aa56bc.png align="center")

* And create a public repository. I already have one, so I won't create it again.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739419153043/bda81966-556d-456f-b578-5d9345b65695.png align="center")

## Push Image

* Once you've created the repository, you can push the image we made using the `docker push` command. But first, we need to tag our local image as `latest`, as shown below.
    

```bash
docker tag anonymous-go dhruvnakum/anonymous-go:latest
```

* And now let’s push it
    

```bash
 docker push dhruvnakum/anonymous-go:latest
```

* Replace `dhruvnakum` with your username, and if you named the repository differently, use that name instead.
    
* After you run this, the image will be successfully pushed to the repository we just created.
    
* ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739516794707/deb48b3b-d513-454f-8c96-66a5e1e780bf.png align="center")
    
    Now, let's create a workflow to automate deployment.
    

---

# Creating the GitHub Actions Workflow

* To automate deployment, we will set up a GitHub Actions workflow.
    

### Create the Workflow File

* In the project, create a new file at `.github/workflows/cicd.yml`.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739514002340/754732d8-9a9b-4fcf-80d0-a868ec8f2b7e.png align="center")

### Define the CI/CD Pipeline

```bash
name: Deploy Go Application

on:
  push:
    branches: 
      - ec2

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Source
        uses: actions/checkout@v4
      - name: Create .env file
        run: echo "PORT=${{ secrets.PORT }}" >> .env
      - name: Login to docker hub
        run: sudo docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
      - name: Build docker image
        run: sudo docker build -t dhruvnakum/anonymous-go .
      - name: Push image to docker hub
        run: sudo docker push dhruvnakum/anonymous-go:latest
  deploy:
    needs: build
    runs-on: self-hosted
    steps:
      - name: Delete old anonymous-go container
        run: sudo docker rm -f anonymous-go  
      - name: Pull new anonymous-go docker image
        run: sudo docker pull dhruvnakum/anonymous-go
      - name: Delete old mongo container
        run: sudo docker rm -f mongo-demo
      - name: Pull new mongo docker image
        run: sudo docker pull mongo:8.0
      - name: Delete old network
        run: sudo docker network rm anonymous-go-nw
      - name: Create networks
        run: sudo docker network create anonymous-go-nw
      - name: Run mongo container
        run: sudo docker run --name mongo-demo --network anonymous-go-nw -d mongo:8.0
      - name: Run docker container
        run: |
          sudo docker run --name anonymous-go \
          --network anonymous-go-nw \
          -p 3000:3000 \
          -e MONGODB_URI="mongodb://mongo-demo:27017" \
          -e JWT_SECRET="secret" \
          -e REDIS_SECRET="secret" \
          -e REDIS_ADDR="redis-11068.c261.us-east-1-4.ec2.redns.redis-cloud.com:11068" \
          -e REDIS_PASSWORD="DM4iBq2pTYNQkweBZmwqGKyDYrj872M8" \
          -e PORT=3000 \
          -d dhruvnakum/anonymous-go:latest
```

Let me explain what's happening here:

* First, we named the workflow `Deploy to Go Application`.
    
* To be safe, I created a new branch called `ec2` and pushed all the code to it. We want the workflow to run automatically whenever this branch is updated. Here's how we do it:
    

```bash
on:
  push:
    branches: 
      - ec2
```

* We have two jobs: **Build and Deploy**.
    
* In the **Build job**, we download our project's code, build a Docker image from it, and push it to Docker Hub.
    
* In the **Deploy job**, we pull the Docker image of our project and the Mongo image from Docker Hub, run them inside one container as we did before, and finally, we run that container.
    

---

## Build Job

* We want this job to run on the latest Ubuntu operating system.
    

```bash
jobs:
  build:
    runs-on: ubuntu-latest
```

* Now we want to download our project’s code from GitHub inside this OS, so we write this:
    

```bash
name: Checkout source code
uses: actions/checkout@v2
```

* Since we have environment variables in our code, we need to make them available here. To do this, we must add these secrets in `GitHub Action Secrets`. Go to the project's settings, and under secrets and variables, add these secrets.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739515733028/3ca76d47-72ef-4a61-a07a-620022310337.png align="center")

* Now, to get this from there, we create .env inside this OS and push everything:
    

```bash
- name: Create .env file
  run: echo "PORT=${{ secrets.PORT }}" >> .env
```

* Now that we have all the secrets. We Login to the docker:
    

```bash
- name: Login to docker hub
  run: sudo docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
```

* Then we build the docker image of our project:
    

```bash
- name: Build docker image
  run: sudo docker build -t dhruvnakum/anonymous-go .
```

* Finally, we push the image to the docker hub:
    

```bash
- name: Push image to docker hub
  run: sudo docker push dhruvnakum/anonymous-go:latest
```

---

## Deploy Job

* In this job, everything we write will run on the AWS EC2 instance provided by GitHub.
    
* We want this job to start after the build job finishes successfully.
    

```bash
deploy:
    needs: build
```

* In this job, we first want to delete all the old containers and pull the latest ones. Once that is done, we run the container using `docker run` command.
    

```bash
deploy:
    needs: build
    runs-on: self-hosted
    steps:
      - name: Delete old anonymous-go container
        run: sudo docker rm -f anonymous-go  
      - name: Pull new anonymous-go docker image
        run: sudo docker pull dhruvnakum/anonymous-go
      - name: Delete old mongo container
        run: sudo docker rm -f mongo-demo
      - name: Pull new mongo docker image
        run: sudo docker pull mongo:8.0
      - name: Delete old network
        run: sudo docker network rm anonymous-go-nw
      - name: Create networks
        run: sudo docker network create anonymous-go-nw
      - name: Run mongo container
        run: sudo docker run --name mongo-demo --network anonymous-go-nw -d mongo:8.0
      - name: Run docker container
        run: |
          sudo docker run --name anonymous-go \
          --network anonymous-go-nw \
          -p 3000:3000 \
          -e MONGODB_URI="mongodb://mongo-demo:27017" \
          -e JWT_SECRET="secret" \
          -e REDIS_SECRET="secret" \
          -e REDIS_ADDR="redis-11068.c261.us-east-1-4.ec2.redns.redis-cloud.com:11068" \
          -e REDIS_PASSWORD="DM4iBq2pTYNQkweBZmwqGKyDYrj872M8" \
          -e PORT=3000 \
          -d dhruvnakum/anonymous-go:latest
```

> Before we move ahead I forgot to change the URL in the main from localhost to `0.0.0.0` . So make sure you have this inside main.go at the end.
> 
> ```bash
> log.Fatal(r.Run("0.0.0.0:" + os.Getenv("PORT")))
> ```

---

# Setting Up AWS EC2 Instance

* Before deploying the project, we need to set up an EC2 instance.
    
* To do this, log in to the AWS Management Console and go to the EC2 dashboard.
    
* Use this link: [https://aws.amazon.com/ec2/](https://aws.amazon.com/ec2/)
    
* It might ask for your card details, but don't worry; they won't charge you unless you exceed the limit, which you won't be charged for for this purpose.
    
* Search for EC2 and click on it.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739576315008/4743aac7-5276-4ada-a4b2-839861719665.png align="center")
    
* Now click on the “Launch Instance.”
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739576363008/886edd48-54f3-46df-a3bc-77e9eb6e3708.png align="center")

* Now, give the instance name and select the Ubuntu image
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739576476093/51e24e8e-7158-4890-a481-ef8162406067.png align="center")

* Also, select Key pair. If you don’t have one. You can create it by clicking on the Create new key pair button.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739576528760/9a33561f-da16-4cc2-9fb9-ef9558b87978.png align="center")

* Once it is done, click on the Launch Instance button on the right
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739576587591/5d1b7476-7395-4aa4-a996-e05561354490.png align="center")

* Since our app is running on port 3000, go to the Security Group settings and allow inbound traffic on **port 3000** to make sure the application is accessible.
    
* To do that, click on the Instance ID
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739576672455/daa31b8d-6768-4e61-a14b-1cd1dd729f85.png align="center")

* Then click on the Security tab and there click on Security Group
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739576717820/dcbadb45-e7d2-4430-8284-5c4c19204800.png align="center")

* Click on Edit inbound rules and then add custom TCP with Port range 3000 as shown below and save the rules.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739576774337/ad884b6d-df63-41f7-b114-81c37a401cd0.png align="center")

* Now, we are ready to run the instance.
    

## Setting up Docker inside EC2 Instance,

* Our instance is ready. First, we need to make sure Docker is installed. To do this, let's connect to the instance. Click the Connect button after selecting the instance.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739576897708/00324931-7359-47cf-afe2-00d882b64f6b.png align="center")

* Once you do that you will see a window like this:
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739576982747/bcad9c57-c111-40ec-9f73-ee94ceef7df2.png align="center")

* To confirm whether docker is there or not, run `docker` command, you will most likely see this:
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739577088622/017a2ea5-fbdc-4f14-9666-b58a9cca731c.png align="center")
    
* Now, to install everything, run the below commands
    

```bash
sudo apt-get update && sudo apt-get install docker.io -y && sudo systemct| start docker && sudo chmod 666 /var/run/docker.sock && sudo systemcti enable docker && docker --version
```

* Once it is done, run `docker version` to see if it’s installed correctly.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739577318335/0482a21a-55c0-4d7d-ad5c-4adc5b558c26.png align="center")

## Register a Self-Hosted GitHub Runner

* A **self-hosted runner** is simply a computer or server that you own (like an AWS EC2 instance) that runs GitHub Actions instead of using GitHub’s default machines.
    
* To set up, go to the project settings, and inside Actions, you will see this runner tab.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739577522960/083333ac-9bc5-421d-81ee-16df3bada386.png align="center")

* Click on \`New self-hosted runner:
    
* Select Linux
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739577617221/1caf496c-c9d8-42c4-bfc4-32efc7d84a73.png align="center")

* And run all the commands you see there inside your EC2.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739577640356/6ca70f3e-e61a-47ab-8c98-8a301644664a.png align="center")

* Once you run the last step `./run.sh` command, you will see a runner with the status idle :
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739577761453/8e1d7651-a258-447e-aab8-4101d725c6f9.png align="center")

* But there is one problem, that runner does not run on detached mode in our instance, If you kill it by pressing CTRL+C, you wont be able to run it.
    
* To resolve this issue, just run the below command
    

```bash
sudo ./svc.sh install
```

```bash
sudo ./svc.sh start
```

* Now, your running is running in the background, and you can perform other operations inside your instance.
    
* Finally, we are done with it. Now, we can test our application.
    

## Testing Your Application

* To test, commit to the `ec2` branch that we created. Once you do that, Go to the Actions tab, and there you will see both `build` `deploy` services are running.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739578220374/e8282b00-49f0-4118-8ad2-6bbb86a45e2b.png align="center")

* Once it is done, you will see both marked as green ticks, which means that everything went well and the docker is successfully up inside the ec2.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739578520776/9dd5ade3-30fb-404d-b77e-5e92968dc2e0.png align="center")

* To test, go to the instance we created and find the `Public IPv4 address`. Copy it, paste it into Postman, and run any endpoint in your application.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739578733192/af6db6bd-4ce9-49ee-8b9d-8ad0c739dd4d.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739578557983/130f27ee-5289-43a1-9a3c-3a0da8e237f3.png align="center")

* As you can see it’s working now.
    

![a man is sitting at a table with his arms outstretched in front of a crowd with the words let 's celebrate !!](https://media1.tenor.com/m/sTbvauq7zVoAAAAC/lest-celebrate-happy.gif align="center")

* Phew! I know that seems like a lot at first. But once you follow these steps, you'll get comfortable with it.
    

---

# Wrapping Up

* If you've made it to the end of this guide, you truly deserve a big thank you. So, thank you for sticking with it and reading all the way through. I hope you've gained a lot of valuable insights from this single blog post, covering everything from creating a GitHub Workflow to understanding Actions, setting up a Runner, and working with AWS EC2.
    
* Keep diving deeper into these topics and explore other related concepts, as they are essential for cloud development in today's fast-paced tech world. Mastering these skills will undoubtedly enhance your ability to build and deploy applications efficiently.
    
* I look forward to sharing more knowledge with you in the next blog post! Until then, keep learning and experimenting with new technologies.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1711803084752/aa266313-a315-41a8-9538-8ff4370577fb.gif?auto=format,compress&gif-q=60&format=webm&auto=format,compress&gif-q=60&format=webm&auto=format,compress&gif-q=60&format=webm&auto=format,compress&gif-q=60&format=webm&auto=format,compress&gif-q=60&format=webm&auto=format,compress&gif-q=60&format=webm&auto=format,compress&gif-q=60&format=webm align="center")

Reference: [https://www.youtube.com/watch?v=sSAWMr\_-Co4&t=409s](https://www.youtube.com/watch?v=sSAWMr_-Co4&t=409s)

* Connect with me on [**LinkedIn**](https://www.linkedin.com/in/dhruv-nakum-4b1054176/)**,** [**Github**](https://github.com/red-star25)**, and** [**Twitter**](https://twitter.com/dhruv_nakum)**.**
