Building an application is one thing. Taking that application from a development environment and making it actually work in production is a completely different experience.
I learned this while working on Meezo, a food delivery application that started from an idea I had. I invited one of my friends to work on the project with me, and together we developed the application. When it came to deployment and cloud infrastructure, however, I handled that part myself.
At the beginning, I had almost no practical AWS experience.
I didn’t have a step-by-step course that taught me everything I needed. Instead, I learned by actually trying to deploy the application, facing problems, researching them, and fixing them one by one. I used ChatGPT heavily as a learning assistant. Whenever I got stuck, I would take screenshots of the AWS console, errors, or configurations and use them to understand what was happening and what I needed to change.
That process taught me much more than simply following a tutorial.
Why I Chose AWS
I chose AWS because I wanted to work with an industry-standard cloud platform and gain real-world experience with cloud infrastructure.
For Meezo, reliability and the ability to scale were also important. A food delivery application can have very different traffic patterns depending on the number of users and orders, so I didn’t want to think only about getting the application running. I also wanted to understand how a system could be deployed and scaled properly.
More importantly, I wanted to learn AWS by actually using it.
My First Architecture: Docker, ECR, ECS and Fargate
The first production deployment of the Meezo backend was built around containers.
I created the Dockerfile myself and containerized the Node.js backend. The Docker image was then pushed to Amazon Elastic Container Registry (ECR).
From there, I deployed the container using Amazon ECS with AWS Fargate.
The basic flow looked like this:
GitHub → Docker → Amazon ECR → Amazon ECS → AWS Fargate
I also used an Application Load Balancer (ALB) in front of the ECS service. The application was connected to a custom domain and HTTPS was configured using AWS Certificate Manager.
The PostgreSQL database was hosted separately using Supabase rather than inside AWS.
This was my first time dealing with concepts such as containers, task definitions, load balancers, target groups, health checks, IAM permissions, networking and production configuration at this level.
My First Real Production Problem
One of the first problems I faced was something that looked simple but stopped the deployment from working correctly: the ECS health check was failing.
At first, seeing an unhealthy task was confusing. The container was running, but the load balancer was not considering it healthy.
After investigating the configuration, I found that the health-check path was incorrect.
I corrected the health-check path in the ALB target group configuration.
After that, the target changed from Unhealthy to Healthy, and the service started working as expected.
It was a small configuration mistake, but it taught me an important lesson:
A container being “running” does not necessarily mean that your application is healthy from the perspective of the infrastructure around it.
This was one of the first moments where I started understanding how production systems differ from simply running an application on localhost.
Learning About CI/CD
I also wanted to avoid manually rebuilding and deploying the application every time I changed the code.
So I implemented a GitHub Actions workflow.
The deployment process became approximately:
GitHub Push → Docker Build → ECR Login → Docker Image Push → Deployment
This helped me understand the value of CI/CD in a real project.
Instead of treating deployment as something I had to repeat manually, I started thinking about deployment as part of the software development workflow itself.
It also forced me to understand IAM permissions, because the deployment process needed appropriate permissions to interact with AWS services.
Security, Networking and Configuration
Another area that took me a significant amount of time was networking.
Security Groups, ports, application access, Nginx, domain configuration and HTTPS all had to work together.
I had to understand questions such as:
- Which ports should be accessible?
- Which traffic should be allowed?
- How does the domain reach my application?
- How does HTTPS reach the server?
- How does the reverse proxy communicate with the Docker container?
- What permissions are actually required?
These were concepts I had previously encountered mostly as theory.
Deploying Meezo made them practical.
From Fargate to EC2: Learning the Cost Side of Cloud
Getting the application running was not the end of the story.
After deployment, I started looking at the actual AWS costs using AWS Cost Explorer.
The monthly cost was around $120, and the Application Load Balancer was one of the significant contributors I noticed.
That made me realize something important:
Cloud architecture is not only about technical performance. It is also about understanding cost.
I started looking for a more cost-effective approach for the current stage of the project.
Eventually, I moved the backend deployment from Fargate to an EC2-based setup.
Instead of running the container through Fargate, I ran the Docker container directly on an EC2 instance.
I kept the deployment automated with GitHub Actions, while using SSH when I needed direct access to the server.
For the new setup, I also configured Nginx and a new domain with HTTPS.
The cost reduction was not dramatic, but it was enough to teach me an important lesson: the best architecture depends not only on what is technically possible, but also on the scale, requirements and budget of the application.
What I Learned From This Experience
The biggest value of this project was not just getting Meezo online.
I learned several things that I would probably have understood very differently if I had only studied them theoretically.
I learned how to:
- Containerize a Node.js application using Docker
- Create and manage Docker images
- Push images to Amazon ECR
- Deploy containers using ECS and Fargate
- Configure an Application Load Balancer
- Work with health checks and target groups
- Configure IAM permissions and policies
- Set environment variables for production
- Set up CI/CD using GitHub Actions
- Monitor applications using CloudWatch
- Work with domains, HTTPS and Nginx
- Understand AWS networking and Security Groups
- Analyze cloud costs using AWS Cost Explorer
- Think about scalability and traffic
- Make architecture decisions based on both performance and cost
But there was another lesson that was even more important.
You Don’t Need to Know Everything Before You Start
When I started the deployment, I didn’t know all of these things.
I learned them because I had a real problem to solve.
When something didn’t work, I investigated it. When I didn’t understand an AWS configuration, I learned about it. When I received an error, I tried to understand why it happened instead of simply trying random fixes.
I also used ChatGPT as a learning assistant throughout the process. When I encountered a confusing AWS console screen or an error, I would share a screenshot and ask for help understanding it.
The important part was that I wasn’t just copying commands. I was gradually learning what each service and configuration was actually doing.
From a Student Project to a Production Experience
Meezo started with an idea.
My friend and I worked together to turn that idea into an application. But deploying it gave me a completely different perspective on software development.
Before this experience, concepts like Docker, load balancers, IAM, security groups, CI/CD and cloud infrastructure could easily feel like separate technologies to study.
During deployment, I realized that they are connected pieces of one system.
A small configuration mistake can stop a service from becoming healthy. A security rule can prevent an application from being reached. A load balancer can affect both architecture and cost. A deployment pipeline can save time but also requires correct permissions and configuration.
That is what made this experience valuable for me.
I didn’t learn AWS by memorizing services.
I learned AWS by trying to make a real application work.
And for me, that was the biggest lesson of the entire deployment journey.
Top comments (0)