~/home/blogs/case-study-serverless-aws-migration.md

Case Study: Migrating a Website to a Serverless AWS Stack

Migrating a Ruby on Rails monolith to a serverless architecture with Next.js, AWS S3, and CloudFront, automated with CI/CD for performance and cost savings.

- Raul G.
2024-02-05

1. The Challenge: Modernizing a Monolithic Application

My personal website and blog were originally built as a standard Ruby on Rails application, hosted on a virtual machine with DigitalOcean. While functional, this monolithic architecture presented several key challenges:

  • High Maintenance Overhead: Required manual server maintenance, security patches, and scaling management.
  • Cost Inefficiency: A fixed monthly cost for the VM, regardless of traffic levels.
  • Slow Deployment Cycle: The deployment process was manual and time-consuming, hindering rapid iteration.
  • Performance Limitations: As a dynamic application, it couldn't fully leverage the speed benefits of modern static site generation.

The goal was to re-platform the website to a modern, serverless architecture that would be more performant, scalable, cost-effective, and easier to maintain.

2. The Solution: A Serverless Frontend with Automated CI/CD

I architected a new solution centered on Next.js for the frontend, AWS for infrastructure, and GitHub Actions for automation. This decoupled the frontend from a traditional backend server, embracing a static-first approach.

Technical Architecture

The core components of the new architecture are:

  • Next.js Framework: Chosen for its powerful Static Site Generation (SSG) capabilities, which pre-renders the entire website into highly optimized HTML, CSS, and JavaScript files at build time. This dramatically improves performance and SEO.
  • AWS S3: Used as the primary storage for all static assets. S3 provides durable, inexpensive, and highly available object storage, eliminating the need for a traditional web server.
  • AWS CloudFront: A global Content Delivery Network (CDN) that caches the site's assets at edge locations around the world. This ensures low-latency delivery to users everywhere and provides a security layer with a custom SSL certificate.
  • GitHub Actions for CI/CD: An automated workflow was established to handle the entire build and deployment process. On every new release pushed to the GitHub repository, a GitHub Actions runner automatically:
    1. Builds the Next.js application.
    2. Generates the static HTML files for all blog posts.
    3. Uploads the entire build output to the S3 bucket.
    4. Invalidates the CloudFront cache to ensure the latest version is served.
graph LR
    subgraph "Development Workflow"
        A[GitHub Repository] -->|Publish Release| B[GitHub Actions]
    end
    subgraph "Build & Deploy Pipeline"
        B -->|1. npm run build| C[Static Site Generation]
        C -->|2. Upload to S3| D[(AWS S3 Bucket)]
        D -->|3. Invalidate Cache| E[AWS CloudFront]
    end
    subgraph "User Access"
        F[User Browser] -->|www.rgonzalez.tech| E
    end

Content Management Transformation

The content workflow was also modernized. Instead of a database-backed CMS, all blog posts are now written in Markdown. This developer-centric approach, known as "Git as a CMS," has several advantages:

  • Simplified Workflow: I can write and edit content in any text editor and use version control (Git) to manage changes.
  • No Database Needed: Eliminates the cost and complexity of managing a database.
  • Improved Performance: Content is compiled directly into static HTML during the build process.

3. The Migration Process

Migrating from a Rails application with a SQLite database required a systematic approach.

  • Content Export: I leveraged a Python script within a Jupyter notebook (run via ChatGPT's data analysis feature) to query the .sqlite database. This script extracted all blog posts and their metadata into a structured format.
  • Content Transformation: The extracted data was then programmatically converted into individual Markdown files, complete with the necessary frontmatter (title, date, tags, etc.).
  • Asset Migration: A custom Ruby script was written to iterate through the Rails Active Storage attachments, download all image assets, and organize them into a new folder structure compatible with the Next.js project.
# Snippet from the Rails asset migration script
ActiveStorage::Attachment.all.each do |attachment|
  blob = attachment.blob
  filename = attachment.filename.to_s
  new_path = "#{Rails.root}/tmp/migrated_assets/#{filename}"

  # Write the file to a temporary location for upload
  File.open(new_path, "wb") do |file|
    file.write(blob.download)
  end
end

4. The Results: A Measurable Improvement

The migration to a serverless architecture yielded significant, quantifiable benefits:

  • Hosting Costs Reduced by >95%: Moved from a fixed monthly VM cost to paying only for pennies-per-month S3 storage and CDN bandwidth, resulting in near-zero operational expense.
  • Dramatically Improved Performance: Achieved a Google PageSpeed Insights score of 95+ for performance, thanks to SSG and CDN caching.
  • Zero-Maintenance Infrastructure: The serverless model completely eliminated the need for server management, patching, or manual scaling.
  • Accelerated Development Cycle: The automated CI/CD pipeline reduced deployment time from a manual 15-minute process to a fully automated 2-minute workflow, enabling faster iteration and content updates.

This project successfully transformed a legacy monolithic website into a modern, high-performance, and cost-effective digital asset, showcasing the tangible business advantages of adopting a serverless-first mindset.

Share this post