A hands-on guide to automating infrastructure provisioning and containerised application deployment with GitHub Actions and Terraform.
💡 Introduction
Welcome to the world of pipelines and automation 🚀. In this guide, we’re going to uncover an exciting project where we deploy a Node.js + Redis web app on AWS using Terraform and GitHub Actions for seamless integration.
This walkthrough is designed with beginners in mind—so even if you’re just starting with DevOps or cloud automation, you’ll be able to follow along step by step.
The application itself is simple but practical: a Request Counter app that tracks the number of visits and stores the data in Redis. But the real magic isn’t the app—it’s the way we’ll set up Infrastructure as Code (IaC) with Terraform, integrate it with GitHub Actions CI/CD, and see how everything ties together.
So without further ado, let’s roll up our sleeves and get hands-on with some practical knowledge 💡.
🛠 Pre-requisites
Before diving into the project, let’s make sure our host system is ready with all the essentials. Here’s what you’ll need:
AWS Account + IAM User with Admin Access (Only for testing purposes, in prod, always use PLOP). Since we’ll be spinning up infrastructure on AWS, you need an IAM user with proper permissions and AWS CLI configured locally.
👉 If you’re new to this, don’t worry—I’ve explained the entire setup process (IAM user, AWS CLI, and Terraform installation) in one of my earlier blogs: Learn How to Deploy a Three-Tier Application on AWS EKS Using Terraform with Best PracticesDocker & Docker Compose: Our application runs in containers, so Docker and Docker Compose are a must. You can install them easily from their official documentation: Install Docker
✅ Once these are configured, we’re all set to start building and automating this project!
🚀 Getting Started with the Project
The complete code for this project is available on my GitHub repository:
👉 nginx-node-redis
To begin, fork this repository under your own GitHub username and then clone it locally using the commands below:
git clone https://github.com/<your-username>/nginx-node-redis.git
cd nginx-node-redis/
⚡ Why this repo?
The project originally comes from Docker’s official dockersamples
collection. It’s a simple Node.js + Redis request counter application with an NGINX load balancer in front. Every time you refresh the page, the counter increments and you also see the hostname (web1
or web2
) that served your request.
I’ve taken that base project and leveled it up to make it more attractive and production-friendly.
🔑 Key Highlights of the Project
-
web/server.js
→ Basic Node.js app that connects to Redis on port6379
and returns:- The number of visits (increments with every refresh)
- The hostname (
web1
orweb2
) serving the request - Runs on port
5000
- Now enhanced with a beautiful HTML UI instead of plain text 🎨
-
nginx/nginx.conf
→ Custom configuration with:- An upstream load balancer pointing to
web1:5000
andweb2:5000
- Proxy pass rules to evenly distribute traffic
- Dockerfile that replaces the default NGINX config with our custom one
- An upstream load balancer pointing to
-
docker-compose.yml
→ Glues everything together:- Redis container (database)
-
Two Node.js containers (
web1
andweb2
) - NGINX container (acting as a reverse proxy and load balancer)
✅ My Enhancements
✨ Improved UI in
web/server.js
📦 Added Terraform configuration (
terra-config/
) to deploy the app on AWS⚡ Added GitHub Actions workflow (
.github/workflows/main.yml
) for CI/CD automation
With these changes, the project is no longer just a demo—it’s a mini production-grade system that you can deploy with one push.
🐳 Testing Locally with Docker Compose
Before deploying this app on AWS with Terraform, let’s test it locally to make sure everything works as expected.
Navigate to the project root directory and run the following command:
docker-compose up --build
✅ This will:
Build Docker images for Redis, Node.js web servers, and NGINX
Start up all the containers in the correct order
Expose the application through NGINX on port 80
If everything is set up properly, you’ll see logs confirming the containers are running. Most importantly, look for this message in your terminal:
The web server is listening on Port 5000.
Now, open your browser and go to 👉 http://localhost:80
🎉 You should see the application live!
Every time you refresh the page, the counter will increment by
+1
The hostname will change between web1 and web2, showing that load balancing is working perfectly
This step confirms that the Docker + NGINX + Redis setup is solid before we move on to cloud deployment.
⚡ Integrating GitHub Actions and Terraform
Now that our app works locally, let’s automate deployment with GitHub Actions + Terraform. This will allow us to:
Provision infrastructure on AWS automatically
Deploy the app onto an EC2 instance
Run health checks
Tear down resources after testing (to save 💰 on AWS bills)
🔑 Step 1: Add AWS Secrets
Go to your GitHub Dashboard → open the
nginx-node-redis
repoNavigate to Settings > Secrets and Variables > Actions
-
Add the following Repository Secrets (from the IAM user you created earlier):
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
🏗 Step 2: Terraform Configuration
Inside the terra-config/
main.tf
file:
Uses the default VPC
Fetches the latest Ubuntu AMI
Creates a Security Group with port 22 (SSH) and port 80 (HTTP) open
Provisions a t2.micro EC2 instance using that SG and AMI
Includes a user-data script that installs Docker, Docker Compose, and runs our app
📌 In short: Terraform handles all infra creation and boots up an EC2 instance that runs our Request Counter app.
⚠️ Important Change:
In the user_data
section of main.tf
, update the GitHub repo URL to point to your forked repo:
git clone https://github.com/<your-github-username>/nginx-node-redis.git
⚙️ Step 3: GitHub Actions Workflow
The .github/workflows/main.yml
pipeline is triggered on push or pull requests. Here’s what happens step by step:
✅ Checkout the repository
⚙️ Setup Terraform
📂 Run
terraform init
,terraform validate
,terraform plan
🚀 Apply infrastructure with
terraform apply
⏳ Wait 90 seconds → allows user-data to finish setting up Docker + app
🌍 Fetch the Public IP of the EC2 instance
🔎 Health check → ensure the app is live
⏱ Keep the application running for 5 minutes
🧹 Auto teardown with
terraform destroy
→ ensures no unused AWS resources are left behind
▶️ Step 4: Trigger the Workflow
Once you’ve updated your repo URL in main.tf
, commit and push changes:
git status
git add terra-config/main.tf
git commit -m "Updated GitHub repo URL"
git push origin main
This push will trigger the GitHub Actions workflow, automatically deploying your app to AWS.
🚀 Testing the Deployment on AWS
Once everything is set up, it’s time to watch the magic happen ✨.
Go to your GitHub Repository Dashboard
Click on Actions
-
Find your recent commit – for example:
"Updated GitHub repo URL"
Open the workflow run → you’ll see the terraform job executing step by step.
🟢 Wait until the workflow reaches the “Keep App Running Stage”. At this point, Terraform has already created your EC2 instance, installed Docker & Docker Compose, and started the application.
👉 Click on the Public URL shown in the logs, and… Viola! 🎉
Your Request Counter App is now live on an AWS EC2 instance 🚀.
You can interact with the app for 5 minutes
Every refresh increments the counter
Requests are distributed between web1 and web2 via the Nginx load balancer
⏳ After 5 minutes, GitHub Actions will automatically run terraform destroy
to clean up resources and avoid unnecessary AWS charges 💸.
🔄 Real-Life Scenario: Updating Your App on the Fly
Now that our app is live, let’s simulate a real production change.
Imagine your manager says:
"Hey, can you update the heading from Welcome to Request Counter to Welcome to My Amazing Request Counter?"
Here’s how simple it becomes with GitHub Actions + Terraform:
Open the
web/server.js
file in your project-
Find the
<h1>
HTML tag and update it:
<h1>Welcome to My Amazing Request Counter</h1>
-
Push the changes to your repository:
git status git add web/server.js git commit -m "Heading Changed" git push origin main
🎉 That’s it! GitHub Actions will pick up the changes, trigger the workflow, and within 5 minutes your updated heading will be live on the EC2 instance.
⚡ This is the true power of CI/CD pipelines — no manual SSH into servers, no redeploying by hand. Just push your code and let Terraform + GitHub Actions handle the rest 🚀.
🏁 Conclusion
In this blog, we took a simple Node.js + Redis counter application and supercharged it with Nginx, Docker, Terraform, and GitHub Actions.
What started as a local demo quickly transformed into a cloud-ready, automated CI/CD pipeline:
🚀 Docker + Docker Compose handled local testing and containerization
⚡ Terraform provisioned AWS infrastructure seamlessly
🔄 GitHub Actions automated deployments and teardown, giving us a clean, cost-effective workflow
🎯 And most importantly — we saw how easy it is to make real-time changes that go live with just a
git push
.
This project proves how infrastructure as code + automation can save developers hours of repetitive work and make production-ready workflows more reliable.
Thanks for following along — I hope this inspires you to build your own Terraform + GitHub Actions pipelines!
📬 Let’s Connect:
🌐 Website: praveshsudha.com
💼 LinkedIn: Pravesh Sudha
🐦 Twitter/X: @praveshstwt
📺 YouTube: Pravesh Sudha
Top comments (0)