Introduction
In the previous posts, I shared an overview of POS Lite, explained how I designed the backend with Spring Boot and showed how I built the frontend with React, Next.js and Material UI.
In this fourth post, I want to focus on deployment.
Deploying a full stack application is an important step because it connects everything together: the frontend, the backend, the database, environment variables, domains, SSL and server configuration.
For POS Lite, I used the following deployment setup:
- Backend deployed on AWS EC2
- Backend containerized with Docker
- Frontend deployed on Vercel
- Domain and DNS managed with Cloudflare
- Database using PostgreSQL
The goal was to move the project from local development to a production-like environment.
Deployment Goals
Before deploying the project, I wanted to define a few goals.
The deployment needed to be:
- Simple enough to manage
- Close to a real production setup
- Separated between frontend and backend
- Secure enough to use HTTPS
- Easy to update as the project evolves
- Good enough to present as a professional portfolio project
I did not want the project to only work locally on my machine.
I wanted to make it available online and learn what it takes to deploy a real full stack application.
Architecture Overview
The production setup looks like this:
User
↓
Cloudflare
↓
Vercel Frontend
↓
AWS EC2 Backend
↓
PostgreSQL Database
The frontend is hosted on Vercel.
The backend runs on an AWS EC2 instance using Docker.
Cloudflare manages the domain, DNS and SSL configuration.
The frontend communicates with the backend through HTTPS requests.
This setup helped me understand how different services work together in a real deployment flow.
Why I Used Vercel for the Frontend
The frontend of POS Lite was built with React and Next.js, so Vercel was a natural option for deployment.
Vercel made it easier to:
- Connect the project from GitHub
- Deploy the frontend automatically
- Configure environment variables
- Handle frontend builds
- Serve the application globally
The deployment flow was simple:
- Push the frontend project to GitHub
- Import the repository into Vercel
- Configure environment variables
- Deploy the application
The most important frontend environment variable was the backend API URL.
Example:
NEXT_PUBLIC_API_URL=https://api.example.com
This allows the frontend to know where the backend is running.
Why I Used AWS EC2 for the Backend
For the backend, I wanted more control over the server environment.
That is why I decided to deploy the Spring Boot backend on AWS EC2.
Using EC2 helped me practice:
- Creating a virtual server
- Connecting through SSH
- Installing dependencies
- Running Docker containers
- Configuring ports
- Managing backend deployment manually
This was more challenging than deploying the frontend, but it helped me understand the infrastructure side of a full stack application.
Preparing the EC2 Instance
The backend runs on an Ubuntu EC2 instance.
The first step was to create the instance and open the required ports.
Some important ports are:
22 SSH
80 HTTP
443 HTTPS
8080 Backend application
After connecting to the instance through SSH, I installed Docker.
A simplified installation flow looks like this:
sudo apt update
sudo apt install docker.io -y
sudo systemctl enable docker
sudo systemctl start docker
Once Docker was running, the server was ready to run the backend container.
Dockerizing the Spring Boot Backend
To deploy the backend more consistently, I used Docker.
The idea was to package the Spring Boot application into a container so it could run the same way on the server.
A simplified Dockerfile looks like this:
FROM eclipse-temurin:17-jdk-alpine
WORKDIR /app
COPY target/pos-lite.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
This Dockerfile does a few important things:
- Uses a Java runtime image
- Creates an application directory
- Copies the compiled JAR file
- Exposes the backend port
- Runs the Spring Boot application
Docker helped make the deployment process more predictable.
Building and Running the Backend Container
After creating the Dockerfile, the backend image can be built with:
docker build -t pos-lite-backend .
Then the container can be started with:
docker run -d \
--name pos-lite-backend \
-p 8080:8080 \
--env-file .env \
pos-lite-backend
The --env-file option is useful because sensitive configuration should not be hardcoded inside the application.
Environment Variables
Environment variables are very important in deployment.
For the backend, some of the main variables include:
SPRING_DATASOURCE_URL=jdbc:postgresql://host:5432/database
SPRING_DATASOURCE_USERNAME=username
SPRING_DATASOURCE_PASSWORD=password
JWT_SECRET=your-secret-key
SERVER_PORT=8080
Using environment variables keeps the application more flexible and safer.
It also makes it easier to use different configurations for local development and production.
PostgreSQL Database
POS Lite uses PostgreSQL as the database.
The backend connects to PostgreSQL through the datasource configuration.
For a project like this, there are different options:
- Running PostgreSQL on the same EC2 instance
- Using a managed database service
- Using an external PostgreSQL provider
For this project, the important part was learning how the backend connects to a production database and how configuration changes between local and deployed environments.
Database configuration is one of the areas where deployment becomes more real because local assumptions often stop working once the app is online.
Cloudflare for Domain, DNS and SSL
Cloudflare was used to manage the domain, DNS and SSL.
This helped with:
- Domain management
- DNS records
- HTTPS configuration
- SSL/TLS encryption
- Better security and performance
A simplified DNS setup can include:
Type Name Target
A api EC2 Public IP
CNAME www Vercel domain
The api subdomain can point to the backend running on EC2.
The frontend can point to the Vercel deployment.
This makes the project feel more professional because users access the app through real domains instead of raw deployment URLs.
Connecting Frontend and Backend
Once the backend was deployed and the domain was configured, the frontend needed to communicate with the backend.
The frontend API URL was configured using an environment variable:
NEXT_PUBLIC_API_URL=https://api.example.com
Then the frontend could call backend routes like:
https://api.example.com/api/products
https://api.example.com/api/sales
https://api.example.com/api/reports
This step was important because it connected the two main parts of the project.
A full stack app is not truly deployed until the frontend and backend can communicate correctly in the live environment.
Common Issues I Faced
Deployment came with several challenges.
Some of the common issues were:
- Environment variables not configured correctly
- Backend running locally but not on the server
- Ports not open in the EC2 security group
- CORS errors between frontend and backend
- Wrong API URL in the frontend
- Docker container stopping after an error
- Database connection errors
- HTTP vs HTTPS configuration issues
These problems were frustrating at times, but they helped me understand how deployment works beyond tutorials.
What I Learned
Deploying POS Lite helped me improve in several areas:
- Working with AWS EC2
- Running applications with Docker
- Configuring environment variables
- Deploying a Next.js frontend with Vercel
- Managing DNS with Cloudflare
- Connecting frontend and backend in production
- Understanding CORS and HTTPS issues
- Debugging server-side deployment problems
The biggest lesson was that deployment is not just about uploading code.
Deployment involves infrastructure, configuration, networking, security and debugging.
Key Takeaways
Some of my main takeaways were:
- Docker makes backend deployment more consistent.
- Environment variables are essential for production.
- Vercel simplifies frontend deployment.
- AWS EC2 provides control but requires more configuration.
- Cloudflare helps with domain, DNS and SSL.
- Small configuration mistakes can break the entire deployment.
- A project feels much more real once it is available online.
This deployment setup helped me understand the full lifecycle of a full stack application, from local development to a production-like environment.
What’s Next
In the next and final post of this series, I will share the biggest lessons I learned while building POS Lite.
I will write about technical lessons, mistakes, improvements, project structure and how building a real full stack project helped me grow as a developer.
Thanks for reading.
For further actions, you may consider blocking this person and/or reporting abuse
