A practical, project-based roadmap for developers who want to understand what happens after git push — from Linux and Nginx to Docker, CI/CD, AWS, Terraform, and Kubernetes.
You write your code, run it on localhost:3000, test everything, and it works.
Then you run:
git add .
git commit
git pushAnd then what?
Where does your application actually run?
How does a user's browser reach it?
How does your domain connect to your application?
Where does HTTPS come from?
What happens if your Node.js process crashes?
And do you really want to SSH into a server and manually deploy every time you make a change?
These are questions every developer should have some understanding of.
I'm not saying you need to become a DevOps engineer.
If you're a frontend, backend, or full-stack developer, there will often be a separate DevOps or infrastructure team handling a lot of this stuff.
But you should still understand what happens to your application after you write the code.
Otherwise, deployment becomes a black box.
You can build the application, but the moment something breaks in production, you don't know where to start looking.
That's why I put together this project-based roadmap.
The idea is simple:
Don't try to learn everything at once.
Start with a Linux server.
Deploy one application.
Then add Nginx.
Then a domain and HTTPS.
Then process management.
Then Docker.
Then CI/CD.
And only after understanding those fundamentals, move towards AWS, Terraform, and Kubernetes.
Each project builds on the previous one.
| # | Project | What You'll Learn |
|---|---|---|
| 01 | Deploy a Next.js/React App on Linux | SSH, Linux, Node.js, ports, processes |
| 02 | Put Nginx in Front | Reverse proxy, ports, server blocks |
| 03 | Add a Domain + HTTPS | DNS, A records, TLS, certificates |
| 04 | Keep the App Running with PM2 | Process management, logs, restarts |
| 05 | Dockerize the Application | Dockerfile, images, containers, ports |
| 06 | Full-Stack App with Docker Compose | Multiple containers, networking, database |
| 07 | Automate Deployment with GitHub Actions | CI/CD, secrets, build and deployment |
| 08 | Build the Complete Deployment Stack | Nginx, Docker, database, HTTPS, CI/CD |
| 09 | Break and Debug the Application | Logs, processes, ports, networking, config |
| 10 | Deploy the Stack on AWS | EC2, Security Groups, VPC basics |
| 11 | Manage Infrastructure with Terraform | Infrastructure as Code, state, reproducibility |
| 12 | Introduction to Kubernetes | Pods, Deployments, Services, Ingress |
Stack: Next.js / Node.js → Ubuntu Linux server
Start with a very simple application.
The goal isn't to build another complicated project.
The goal is to understand what actually happens when you take code from your laptop and run it on another machine.
Your Laptop
↓
GitHub
↓
Linux Server
↓
Node.js
↓
Next.js
↓
Port 3000By the end, you should be able to say:
"I can take my application from my laptop and run it on a remote Linux server."
That's the first milestone.
Now your application is running, but you're accessing it directly through something like:
http://SERVER_IP:3000Let's change that.
User
↓
Nginx :80
↓
Next.js :3000Nginx will receive the incoming request and forward it to your application.
This is where you'll start understanding what a reverse proxy actually does.
proxy_passThe important part isn't memorizing an Nginx configuration.
It's understanding why Nginx sits between the user and your application.
Now let's make the application feel more like a real website.
Instead of:
http://SERVER_IP:3000you'll have:
https://yourdomain.comThe flow becomes:
Browser
↓
Domain
↓
DNS
↓
Server IP
↓
Nginx
↓
Next.jsThis is where you'll understand how a domain actually finds your server.
Now let's create a real problem.
Run your application from an SSH session and then disconnect.
Or let the process crash.
What happens?
Your application can stop.
This is where a process manager like PM2 becomes useful.
Linux
↓
PM2
↓
Node.js
↓
Next.jsThe important thing here is understanding why process managers exist, not just learning a few PM2 commands.
So far, we've installed Node.js directly on the server.
Now let's change the approach.
We'll package the application into a Docker image and run it inside a container.
Dockerfile
↓
Docker Image
↓
Container
↓
Next.jsThe main idea is simple:
Instead of depending on how the server is configured, we package the application and its runtime environment together.
A real application usually has more than one service.
For example:
Frontend
↓
Backend
↓
PostgreSQLNow we'll run these services using Docker Compose.
Docker Compose
↓
+----------+----------+
↓ ↓ ↓
Next.js Backend PostgreSQLThis is where Docker starts becoming much more practical.
You're no longer running one container.
You're managing a small application stack.
At this point, you might still be doing this:
git push
↓
SSH
↓
git pull
↓
install
↓
build
↓
restartDoing this manually every time gets annoying pretty quickly.
So now we'll automate it with GitHub Actions.
git push
↓
GitHub Actions
↓
Test
↓
Build
↓
Deploy
↓
ServerThe goal isn't to copy a YAML file from somewhere.
The goal is to understand what actually happens between git push and production deployment.
Now we connect the pieces we've learned so far.
User
↓
Domain
↓
HTTPS
↓
Nginx
↓
Docker
├── Frontend
├── Backend
└── Database
GitHub
↓
GitHub Actions
↓
Deployment
↓
ServerThis is the point where everything starts connecting.
You'll understand how:
You aren't learning isolated tools anymore.
You're understanding the complete deployment flow.
This one is probably the most useful project in the whole series.
Don't build another application.
Take the one you already have and break it.
For example:
Then debug it.
Start with:
Problem
↓
Logs
↓
Process
↓
Network
↓
Configuration
↓
FixThis teaches you something that tutorials usually don't:
How to find the problem when things stop working.
Because knowing how to deploy an application is useful.
Knowing how to figure out why it's not working is even more useful.
Only after understanding the basic deployment flow should you move to AWS.
Now take what you've already built and deploy it on an EC2 instance.
The important thing here is that AWS shouldn't feel like a completely different world.
You already understand Linux, networking, ports, Nginx and Docker.
AWS is now giving you infrastructure around those concepts.
That's why I prefer learning the basics first.
Now imagine creating your infrastructure manually every time.
Create a server.
Configure networking.
Create security rules.
Configure resources.
It works, but repeating all of that manually isn't ideal.
With Terraform, you can define your infrastructure as code.
Terraform Code
↓
AWS Infrastructure
↓
EC2 + Network + Resourcesterraform planterraform applyThe main idea is:
Your infrastructure can be managed like code.
And Kubernetes comes last.
Not first.
By this point, you already understand:
Now Kubernetes will actually have some context.
You'll start with:
The goal isn't to memorize hundreds of Kubernetes commands.
The goal is to understand what problem Kubernetes is solving and where it fits into the deployment stack.
The roadmap looks like this:
Linux
↓
Nginx
↓
Domain + HTTPS
↓
PM2
↓
Docker
↓
Docker Compose
↓
CI/CD
↓
Production Deployment
↓
Debugging
↓
AWS
↓
Terraform
↓
KubernetesEach project adds one more layer.
You're not trying to become a DevOps engineer overnight.
You're building enough deployment knowledge to understand what happens to your application after you write it.
Because eventually, the important question isn't just:
"Does my application work?"
It's also:
"Where is it running, how is it reaching users, and what do I check when it breaks?"
That's the gap this roadmap is meant to fill.