Skip to main content

Command Palette

Search for a command to run...

πŸš€ From Manual Deployment to Automated CI/CD: My Real DevOps Journey (React + AWS + GitLab)

Published
β€’7 min read
P
Welcome! I’m Prajwal P. I stand at the intersection of technology and efficiency, exploring the dynamic world of DevOps βš™οΈ. From mastering Cloud infrastructure to orchestrating containers, I am passionate about automating the complex to create the simple. Join me as I document my learning curve, share technical insights, and navigate the ever-evolving landscape of software deployment.

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 ci

  • Build 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:

  1. Build happens first

  2. 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

  1. Go to AWS S3 Console

  2. Select the frontend bucket

  3. Open Properties

  4. Scroll down to Static website hosting

  5. Click Edit

  6. Configure:

    • Index document β†’ index.html

    • Error document β†’ index.html

  7. 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 ci

  • Artifact 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. πŸš€

More from this blog

Terraform on AWS

29 posts

Stop clicking in the AWS console. Start coding your infrastructure.