Cute Dog

Deploying DoodleDeck to AWS: A Step-by-Step Guide

Jan 31, 2025

DoodleDeck Landing Page

Introduction

DoodleDeck is a collaborative whiteboarding application that enables users to ideate and create together on a shared digital canvas. This project is structured as a monorepo, built using Turborepo, and consists of three main applications.

Project Structure

The project follows a monorepo architecture, with separate applications for the frontend, HTTP backend, and WebSocket backend. Additionally, there are shared packages for common utilities, database configurations, UI components, and TypeScript settings.

Folder Structure

doodledeck/
│── apps/
   ├── frontend
   ├── http-backend
   ├── ws-backend
│── packages/
   ├── backend-common
   ├── common
   ├── db 
   ├── eslint-config
   ├── typescript-config
   ├── store
   ├── ui 
│── .gitignore
│── package.json
│── README.md

Components of the Application

  1. Frontend - Built using Next.js and Tailwind CSS.
  2. HTTP Backend - Uses Express.js for handling API requests.
  3. WebSocket Backend - Built using the ws library for real-time communication.

Deploying the Backends on AWS EC2

For deploying the backends, I used an AWS EC2 instance along with Nginx as a reverse proxy to handle both HTTP and WebSocket requests efficiently.

Installing node and pnpm on the EC2 instance

Refer: How to install node on EC2 Ubuntu instance

Installing Nginx

First, I installed Nginx on the EC2 instance:

sudo apt update
sudo apt install nginx -y

Configuring Nginx as a Reverse Proxy

sudo vi /etc/nginx/nginx.conf
events {
        # Event directives...
}

http {
    server {
        listen 80;
        server_name httpbe.doodlelabs.space;
        # HTTP Backend
        location / {
            proxy_pass http://localhost:4001;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
    }

    server {
        listen 80;
        server_name wsbe.doodlelabs.space;
        # WebSocket Backend
        location /websocket {
            proxy_pass http://localhost:8080;
            proxy_http_version 1.1;
            proxy_set_header X-Real_IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
    }
}

Setting Up SSL with Certbot

To secure the backend servers with HTTPS, I installed Certbot and configured it with Nginx.

sudo apt install certbot python3-certbot-nginx

Then, I generated an SSL certificate for the backend domain:

sudo certbot --nginx -d httpbe.doodlelabs.space 
sudo certbot --nginx -d wsbe.doodlelabs.space

To ensure automatic renewal of the certificates, I ran a dry-run test:

sudo certbot renew --dry-run

There will be changes in your Nginx configuration file. To view them run:

sudo vi /etc/nginx/nginx.conf

Architecture Overview

Below is a diagram of how Nginx is configured as a reverse proxy to route traffic between the HTTP backend and the WebSocket backend.

Nginx Reverse Proxy for two backend servers HTTP and WebSocket

To verify if the websocket is working

npm i -g wscat
wscat -c ws://wsbe.doodlelabs.space/websocket

Output

Connected (press CTRL+C to quit)

Additionally, PM2 is used for managing backend processes efficiently:

Managing Backend Processes with PM2

To keep the backend services running persistently, I used PM2, a process manager for Node.js applications.

First, I installed PM2 globally:

npm install -g pm2

Then, I started the backend service using PM2:

pm2 start --name "http-backend" pnpm -- run dev
pm2 start --name "ws-backend" pnpm -- run dev
PM2 process manager

Deploying the frontend on AWS Amplify

Since the frntend is written in Next.js, I chose to deploy it on AWS Amplify as it supports server-side rendering frameworks.

Setting Up the Amplify Project

I created a new Amplify project and connected it to the GitHub repository where the frontend code is hosted.

Configuring the Build Settings

amplify.yml

version: 1
applications:
  - frontend:
      phases:
        preBuild:
          commands:
            - corepack enable
            - pnpm install 
        build:
          commands:
            - cd apps/excalidraw-frontend
            - pnpm run build
      artifacts:
        baseDirectory: apps/excalidraw-frontend/.next
        files:
          - "**/*"
      cache:
        paths:
          - node_modules/**/* 
          - apps/excalidraw-frontend/node_modules/**/*
      buildPath: /
    appRoot: apps/excalidraw-frontend

Adding custom domain

Bought a domain on Namecheap, linked it to amplify using Amazon Route 53 and added those name servers in my domain dashboard.

Key Learnings

  1. Turborepo Simplifies Monorepo Management - Using Turborepo allowed for efficient builds and dependency management across multiple applications.
  2. Nginx as a Reverse Proxy - Configuring Nginx to handle both HTTP and WebSocket requests improved reliability.
  3. PM2 for Process Management - Keeping backend services running with PM2 helped maintain uptime and manage deployments easily.
  4. Certbot for Free SSL - Setting up SSL certificates with Certbot was straightforward and ensured secure communication.
  5. AWS Amplify for Frontend Deployment - Deploying the frontend on AWS Amplify simplified the deployment process especially Next.js. If it was a react app, I would have used S3 and Cloudfront.

GitHub

canvas.doodlelabs.space

Next blog -> Setting up CI/CD Pipeline for DoodleDeck

mounish
M

Mounish Vatti's Assistant

Ask me anything