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:
- Builds the Next.js application.
- Generates the static HTML files for all blog posts.
- Uploads the entire build output to the S3 bucket.
- 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.