Ever felt like deploying your React app was too simple on platforms like Netlify or Heroku? Maybe you're ready to peek under the hood and see what's really happening when your code goes from localhost to production. Today we're rolling up our sleeves and getting into infrastructure-as-a-service deployment—DNS, virtual machines, SSL certificates, the whole nine yards.
This guide walks you through deploying a React application using cloud virtual machines, complete with automated deployment through GitHub Actions. It's cloud-agnostic, meaning whether you're team AWS, GCP, Azure, or Alibaba Cloud, these steps will work for you.
Instead of clicking "Deploy" on a platform that handles everything behind the scenes, you'll wire together the pieces yourself: GitHub repositories talking to servers, DNS pointing traffic to the right place, Nginx serving your application, and Certbot securing everything with HTTPS. By the end, you'll have a production-ready deployment pipeline that updates automatically whenever you push code.
First things first—you need a server. Here's where to get started depending on your cloud provider:
Alibaba Cloud ECS: Check their console documentation for instance creation
AWS EC2: Launch from the EC2 dashboard
Google Cloud Compute Engine: Use the GCP console
Azure VM: Create through the Azure portal
For this walkthrough, we're using Ubuntu as the operating system. If you pick something else, just know the commands might differ slightly.
When configuring your cloud virtual machine, keep costs reasonable by starting with a modest instance size. For AWS, something like t3.micro works perfectly for testing. The key configuration points: choose Ubuntu as your OS, create a key pair for SSH access, and configure your security group to allow HTTP (port 80), HTTPS (port 443), and SSH (port 22) traffic.
One important security note: don't allow SSH from anywhere. Limit it to specific IP ranges you control. This simple step blocks countless automated attacks.
Once your server is running and you have its public IP address, you need to point your domain at it. In your DNS management panel, create an A record with "@" as the data field and your server's public IP as the value.
This DNS configuration ensures visitors can reach your app through your custom domain instead of memorizing an IP address. For developers working with cloud infrastructure regularly, 👉 Alibaba Cloud's Elastic Compute Service offers reliable virtual machines with simplified DNS integration, making domain setup more straightforward for production deployments.
SSH into your fresh server and install everything you'll need:
bash
sudo apt update
sudo apt upgrade -y
sudo apt install git nginx -y
sudo apt install snapd -y
sudo snap install certbot --classic
Here's what each piece does:
Git pulls your code from GitHub
Nginx acts as your web server, routing traffic to your app
Certbot generates free SSL certificates so your site runs on HTTPS
For private repositories, your server needs permission to clone your code. Generate an SSH key on the server:
bash
ssh-keygen -t ecdsa
Just press enter through all the prompts. Then copy the public key:
bash
cat ~/.ssh/id_ecdsa.pub
Add this key to your GitHub repository under Settings → Deploy keys. This gives your server read-only access to pull code updates.
For continuous deployment to work, GitHub Actions needs to SSH into your server. Copy your private key:
bash
cat ~/.ssh/id_ecdsa
In your GitHub repository, go to Settings → Secrets and variables → Actions, then create a new secret named SSH_PRIVATE_KEY with this key as the value.
Also add the public key to authorized hosts on your server:
bash
cat ~/.ssh/id_ecdsa.pub >> ~/.ssh/authorized_keys
Clone your repository to the server:
bash
git clone git@github.com:/
Install Node.js and npm. We'll use Node version 20.13.1 through nvm:
bash
sudo apt install nodejs npm -y
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
nvm install 20.13.1
Install PM2 globally to manage your application process:
bash
sudo npm -g install pm2
PM2 keeps your app running in the background and automatically restarts it if something crashes. When managing Node.js applications on cloud servers, process managers like PM2 become essential for maintaining uptime and reliability.
Create a deployment script in your home directory:
bash
cd ~
vim deploy_script.sh
Add this content (replace placeholders with your actual values):
bash
#!/bin/bash
APP_DIR="/home//"
REPO_URL="git@github.com:/.git"
if [ ! -d "$APP_DIR" ]; then
git clone $REPO_URL
fi
cd $APP_DIR
git restore .
git pull origin main
npm install
pm2 restart || pm2 start "npm start" --name
Make it executable:
bash
chmod +x deploy_script.sh
Run it to deploy your app:
bash
./deploy_script.sh
Your React app is now running on port 3000, but it's only accessible through the server's IP on that port. Time to fix that.
Edit Nginx's default configuration:
bash
sudo vim /etc/nginx/sites-available/default
Replace everything with:
nginx
server {
listen 80;
server_name ;
location / {
proxy_pass http://localhost:3000;
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;
}
}
Reload Nginx:
bash
sudo systemctl reload nginx
Now you can access your site through your domain, but it's still using HTTP. Let's add SSL.
Run Certbot to generate and install an SSL certificate:
bash
sudo certbot -d --nginx
Certbot automatically modifies your Nginx configuration to handle HTTPS and redirects HTTP traffic to HTTPS. The certificates renew automatically, so you won't have to worry about expiration.
Your site is now live with proper HTTPS security.
The final piece is continuous deployment. Create .github/workflows/deploy.yaml in your repository:
yaml
name: Deploy React App
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install SSH key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ecdsa
chmod 600 ~/.ssh/id_ecdsa
ssh-keyscan -H <server-ip> >> ~/.ssh/known_hosts
- name: Deploy to VM
run: |
ssh ubuntu@<server-ip> './deploy_script.sh'
Replace <server-ip> with your actual server IP address.
Now whenever you push to the main branch, GitHub Actions automatically connects to your server and runs the deployment script. Your changes go live without manual intervention.
You now have a production deployment pipeline that handles the complete journey from code commit to live application. When you understand how DNS, web servers, SSL certificates, and deployment automation fit together, you gain flexibility that platform-as-a-service solutions can't match. For teams scaling their infrastructure needs, 👉 Alibaba Cloud provides comprehensive compute services with competitive pricing that support custom deployment architectures like this one.
This infrastructure gives you control over every aspect of your deployment while maintaining the convenience of automated updates. Whether you're deploying side projects or production applications, you now have the foundation to build on.