π From Manual Deployment to Automated CI/CD: My Real DevOps Journey (React + AWS + GitLab)
As part of my DevOps Engineer training, I got the opportunity to work on a real-world React.js frontend application in a professional environment. This was not a personal or demo project β it was an actual working application where deployments needed to be stable, secure, and consistent.
This experience taught me what DevOps truly means in the industry:
DevOps is not only about tools β itβs about automating processes, solving real deployment issues, and improving delivery speed continuously.
In this blog, Iβm sharing how I converted a manual frontend deployment process into an automated GitLab CI/CD pipeline, and how I solved real-world challenges while deploying the application using AWS S3 + CloudFront CDN.
π Before CI/CD: Manual Deployment Process
Initially, the frontend was deployed manually. The deployment workflow looked like this:
SSH into the server
Pull the latest code using Git
Install dependencies (
npm install)Build the React application (
npm run build)Serve the build output using Nginx
Reload/restart Nginx to reflect the changes
This process worked, but it had major drawbacks:
β Time-consuming deployments
β High chance of human mistakes
β Dependency and Node version mismatch issues
β Difficult to scale with frequent releases
β No consistent automated deployment workflow
Thatβs when automation became necessary.
π My Contribution: Automating Deployment Using GitLab CI/CD
To improve the workflow, I worked on implementing a GitLab CI/CD pipeline that automated the entire build and deployment process.
π― Goal
β Every push/merge to the main branch should automatically build and deploy the frontend.
This removed manual dependency on SSH deployments and improved release consistency.
ποΈ CI/CD Pipeline Workflow
The pipeline was designed with two major stages:
πΉ Stage 1: Build Stage
Install dependencies using
npm ciBuild the React application
Store build output (
build/) as an artifact
πΉ Stage 2: Deploy Stage
Deploy the build folder to AWS S3
Sync files automatically using AWS CLI
π§Ύ GitLab CI/CD Pipeline Configuration
Below is the GitLab pipeline I worked on:
stages:
- build
- deploy
variables:
NODE_VERSION: "22.14.0"
build_react_app:
stage: build
image: node:${NODE_VERSION}
script:
- echo "$ENV_FILE" > .env
- node -v
- npm ci
- CI=false npm run build
artifacts:
paths:
- build/
expire_in: 1 hour
only:
- main
tags:
- docker-runner
deploy_to_s3:
stage: deploy
image:
name: amazon/aws-cli:latest
entrypoint: [""]
script:
- aws --version
- aws s3 sync build/ s3://$AWS_S3_BUCKET/ --delete
only:
- main
tags:
- docker-runner
π CI/CD Pipeline Explanation (Simple Breakdown)
Letβs understand what this pipeline does:
β 1. Stages Definition
stages:
- build
- deploy
This ensures the pipeline runs in a sequence:
Build happens first
Deploy happens only if build succeeds
β 2. Node Version Fix (Stability Improvement)
variables:
NODE_VERSION: "22.14.0"
This ensures every pipeline run uses the exact same Node.js version.
This was important because Node mismatches often cause dependency failures.
β 3. Build Job (React Build Automation)
build_react_app:
stage: build
image: node:${NODE_VERSION}
This job runs inside a Docker container with Node version 22.14.0.
πΉ Creating .env Securely
- echo "$ENV_FILE" > .env
Instead of storing .env inside Git, the environment variables are stored securely in GitLab CI/CD variables and created during pipeline runtime.
πΉ Installing Dependencies
- npm ci
npm ci is preferred in CI/CD pipelines because it installs dependencies strictly based on package-lock.json, ensuring consistency.
πΉ React Strict Mode CI Issue Fix
- CI=false npm run build
React builds sometimes fail in CI because warnings are treated as errors. Setting CI=false ensures the build completes successfully.
πΉ Storing Build Output as Artifacts
artifacts:
paths:
- build/
Artifacts ensure the build/ folder is passed to the deploy stage.
β 4. Deploy Job (AWS S3 Deployment)
The deploy stage uses AWS CLI:
image:
name: amazon/aws-cli:latest
And syncs the build output:
aws s3 sync build/ s3://$AWS_S3_BUCKET/ --delete
Why --delete is important?
It ensures that if any file is removed in the latest build, it will also be removed from S3.
This keeps the deployment clean and updated.
π₯ Real Challenges I Faced (And How I Solved Them)
Working in a real environment means real issues come up daily. These are the major challenges I faced and solved.
β Challenge 1: GitLab Runner Conflict (Backend Runner vs Frontend Pipeline)
β Issue Faced
Initially, the available GitLab runner was dedicated for backend workloads and had a different Node.js version. When I tried running the frontend pipeline on that runner, builds started failing frequently due to Node version mismatch and dependency errors.
β Solution
To solve this, we registered a new GitLab Docker Runner dedicated for frontend CI/CD pipelines.
This runner was configured to support the required Node version and had its own tag.
After this fix:
β
frontend pipeline ran in the correct environment
β
no conflicts with backend runner
β
consistent and stable builds
β
Challenge 2: npm ci Failed Due to package-lock Drift
β Issue Faced
The pipeline failed during:
npm ci
Because package.json and package-lock.json were not aligned.
Since npm ci is strict, it requires both files to match exactly.
β Solution
The issue was solved by syncing dependency updates properly so that package-lock.json matched the latest package.json.
This made dependency installation consistent and stabilized the pipeline.
βοΈ Deploying the Frontend to AWS (S3 + CloudFront CDN)
After stabilizing CI/CD, the frontend was deployed using a scalable AWS architecture:
S3 Static Website Hosting
CloudFront CDN
S3 bucket maintained as private for security
This setup is commonly used for production frontend deployments.
β Challenge 3: React App Showing 404 Error After Refresh (S3 + CDN)
β Issue Faced
The application worked fine when navigating normally.
But after login, if I refreshed the page or opened a deep route like:
/dashboard/profile
It returned:
β 404 Not Found
π Root Cause
React is a Single Page Application (SPA).
When a user refreshes a route like /dashboard, the request goes to S3/CloudFront.
S3 expects a physical file called /dashboard, but it doesnβt exist.
So S3 returns 404.
β
Solution: Configure index.html as Error Document
To fix this, I updated the S3 static website hosting configuration:
Index document:
index.html
Error document:
index.html
This ensures S3 always returns index.html, allowing React Router to handle routing.
π§ Steps to Configure This in AWS S3
Go to AWS S3 Console
Select the frontend bucket
Open Properties
Scroll down to Static website hosting
Click Edit
Configure:
Index document β
index.htmlError document β
index.html
Save changes
π― Final Outcome
After implementing CI/CD automation and fixing deployment issues:
β
manual deployment was removed
β
build and deployment became fully automated
β
dedicated runner ensured correct Node environment
β
frontend hosted securely using S3 + CloudFront
β
refresh issue fixed by proper SPA routing configuration
β
deployments became stable and repeatable
π‘ Key Learnings From This Real DevOps Work
This experience gave me strong hands-on understanding of:
GitLab CI/CD pipeline creation and debugging
Runner registration and workload separation
Node version consistency in CI/CD
Dependency consistency using
npm ciArtifact sharing between pipeline stages
Hosting frontend applications on AWS S3
CloudFront CDN integration with private S3
Solving React SPA routing issues (404 on refresh)
π₯ Biggest Lesson I Learned
One major lesson I learned is:
DevOps is not about writing a pipeline once.
DevOps is about continuously improving automation, fixing failures, and ensuring stable deployments in real environments.
β Conclusion
This project was one of the best real-world learning experiences in my DevOps journey.
I started with a manual deployment process that required SSH login, git pull, npm install, and Nginx-based hosting. But by implementing GitLab CI/CD automation and deploying through AWS S3 + CloudFront, I helped make the deployment workflow faster, consistent, and scalable.
Working on these real challenges improved my troubleshooting skills and gave me confidence in real DevOps practices.
Iβm excited to keep learning and contribute more to automation and cloud deployments. π




