
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
- Frontend - Built using Next.js and Tailwind CSS.
- HTTP Backend - Uses Express.js for handling API requests.
- 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.

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

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
- Turborepo Simplifies Monorepo Management - Using Turborepo allowed for efficient builds and dependency management across multiple applications.
- Nginx as a Reverse Proxy - Configuring Nginx to handle both HTTP and WebSocket requests improved reliability.
- PM2 for Process Management - Keeping backend services running with PM2 helped maintain uptime and manage deployments easily.
- Certbot for Free SSL - Setting up SSL certificates with Certbot was straightforward and ensured secure communication.
- 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.
Next blog -> Setting up CI/CD Pipeline for DoodleDeck