Introduction
There's a difference between reading about cloud architecture and actually building it.
This week, as part of my DevOps Micro Internship (DMI Cohort 2), I deployed the EpicBook web application on Microsoft Azure — a full-stack app with a React frontend, Node.js backend, and a MySQL database. What made this assignment different from previous ones wasn't just the tools. It was the deliberate security decisions I had to make at every step.
This post walks through what I built, the decisions I made, and what actually clicked for me along the way.
What I Built
A two-tier deployment on Azure:
-
Compute layer: Ubuntu
22.04VM (Standard B1s) running the EpicBook app with Nginx as a reverse proxy - Database layer: Azure Database for MySQL Flexible Server, placed in a private subnet with no public internet access
The goal was to get the app live and accessible via the VM's public IP, while keeping the database completely hidden from the outside world.
Step 1: Designing the Network
Before provisioning anything, I set up the network foundation.
I created a Virtual Network (VNet) with the address space 10.0.0.0/16, then carved it into two subnets:
-
10.0.1.0/24— Public subnet for the VM -
10.0.2.0/24— Private subnet for the MySQL database
This separation is the core of secure cloud architecture. The public subnet is where traffic enters. The private subnet is where sensitive data lives — and it should never be directly reachable from the internet.
I then attached Network Security Groups (NSGs) to enforce the rules:
-
Public subnet NSG: Allow HTTP (Port
80) and SSH (Port22) -
Private subnet NSG: Allow MySQL (Port
3306) only from the VM's subnet
That last rule is important. It means even if someone somehow reached the private subnet, they couldn't connect to MySQL unless they were coming from the application VM itself.
Step 2: Provisioning the VM and Installing Dependencies
I deployed the Ubuntu VM into the public subnet, assigned a public IP, and SSHed in.
Then I installed everything the app needed:
sudo apt update && sudo apt upgrade -y
sudo apt install nodejs npm nginx git mysql-client -y
I verified the installations before moving forward:
node -v
npm -v
git --version
A small habit that saves a lot of debugging later.
Step 3: Deploying the EpicBook Application
I cloned the repository and installed dependencies:
git clone https://github.com/pravinmishraaws/theepicbook.git
cd theepicbook
npm install
Then I configured Nginx as a reverse proxy. The key configuration line that matters for React apps is inside the Nginx server block:
server {
listen 80;
server_name _;
root /home/azureuser/theepicbook/build;
index index.html;
location / {
try_files $uri /index.html;
}
}
The try_files $uri /index.html; line ensures React Router works correctly. Without it, refreshing any route other than / returns a 404 — because Nginx looks for a physical file that doesn't exist instead of handing control back to React.
I also used environment variables to pass the database credentials to the Node.js backend. No hardcoded passwords in the codebase.
Step 4: Setting Up the Private Database
I provisioned an Azure Database for MySQL Flexible Server using Private Access (VNet Integration), placing it directly inside the private subnet.
This means the database has no public endpoint. The only way to reach it is from within the VNet — specifically from the application VM.
I imported the SQL dump to initialize the schema, then tested the connection from the VM:
mysql -h epicbook-db1.mysql.database.azure.com \
-u admin123 \
-p bookstore \
-e "SELECT * FROM author LIMIT 5;"
Seeing the author records return in the terminal confirmed the entire chain was working:
VM → private subnet → MySQL → data ✓
Step 5: Final Security Hardening
One last step beyond the basics: I restricted SSH access in the NSG to my specific local IP address only. This means Port 22 is no longer open to the entire internet — only my machine can SSH into the VM.
I also verified the internal Linux firewall (ufw) was not interfering with web traffic:
sudo ufw status
# Status: inactive
What I Learned
1. Network design happens before deployment, not during.
VNets and subnets are not setup steps to rush through. They are the architecture. Everything else builds on top of them.
2. Private access is not optional for databases.
Putting a database in a public subnet with a public endpoint is a real risk that real companies have paid for. VNet integration and NSG rules are how you protect data properly.
3. Environment variables are a non-negotiable habit.
Hardcoding credentials — even temporarily — is a risk. Using .env files from the start costs nothing and prevents a lot.
What's Next
Next up: the AWS portion of this project, and then I'm moving into Agentic AI for DevOps — specifically how AI agents are changing the way infrastructure is managed and automated.
If you're also on the DevOps learning path, feel free to connect: linkedin.com/in/odoworitse-afari
This post is part of my DMI Cohort 2 learning journey with Pravin Mishra — CloudAdvisory.
Top comments (0)