Executive Summary
This is a live case study, not a tutorial — it documents exactly how ranjithr.com, my real personal portfolio, is hosted on AWS today. To be specific about what this is: it is a production static website, not an AWS application or server deployment — there is no compute instance, container, or backend runtime anywhere in this stack. Every push to the main branch on GitHub goes through a fully automated pipeline and is live within minutes, with no server to patch, no manual deploy step, and no dedicated DevOps time spent keeping it online.
The stack is deliberately different from the other two AWS projects on this site. AWS Static Website Hosting and AWS Dynamic Website Hosting both wire S3/EC2, CloudFront, ACM, and Route 53 together by hand to show how the primitives fit. This deployment instead leans on AWS Amplify as a managed layer over the same primitives — it provisions and operates the CloudFront distribution and the TLS certificate itself (and stores the static build output in S3 behind the scenes), triggered directly from GitHub. The trade-off is less manual control in exchange for a genuinely hands-off, git-driven release process, which is the right trade for a portfolio site that changes often and has no ops budget.
- Source of truth: a single GitHub repository, deployed from the main branch.
- Build & hosting: AWS Amplify — managed CI/CD, static build output, and CDN distribution.
- DNS: Route 53 hosted zone for ranjithr.com, aliased to the Amplify/CloudFront domain.
- TLS: AWS Certificate Manager, issued and renewed automatically for the custom domain.
- Analytics: Google Analytics, loaded client-side to track visitors and engagement — kept separate from the AWS hosting stack itself.
Architecture Diagram
A commit becomes a live page through one straight-line path: GitHub triggers Amplify, Amplify serves the build through CloudFront, CloudFront is secured by an ACM certificate, and Route 53 resolves ranjithr.com to that distribution. Google Analytics is kept deliberately separate from this hosting chain — it runs client-side in the visitor's browser after the page has already loaded, and has no part in serving the site itself.
GitHub → AWS Amplify (Hosting + CI/CD) → CloudFront (CDN) → ACM (HTTPS/TLS) → Route 53 (DNS) → ranjithr.com, with Google Analytics tracking visits separately
AWS Services & Cost Estimation
Every service below is one this deployment actually uses today — nothing here is invented or hypothetical. Rather than guess a dollar figure that would go stale, each row lists the cost model AWS actually bills it under. Most of it is usage-based, which is why a low-traffic personal portfolio like this one tends to run for very little — but that is not the same as free, and the real number always depends on your own traffic and usage.
Cost model, not a quote
The table below shows how each service is billed, not a fixed price. Most of these are usage-based AWS services, so the actual charge depends on traffic, storage, requests, and build frequency — and can change as AWS updates its pricing. For a number based on your own usage, use the AWS Pricing Calculator and check the current AWS Free Tier terms.- Purpose
- Static website hosting + CI/CD
- Cost model
- Usage-based
- Purpose
- CDN and content delivery
- Cost model
- Usage-based
- Purpose
- DNS management
- Cost model
- Hosted zone + DNS queries
- Purpose
- SSL/TLS certificate
- Cost model
- Free for public certificates
- Purpose
- Static/build asset storage
- Cost model
- Usage-based
- Purpose
- Source code + deployment trigger
- Cost model
- Free tier / plan dependent
- Purpose
- Website analytics
- Cost model
- Free
Deployment Workflow
There is exactly one way a change reaches production: push to main. Amplify owns everything after that.
- Push (or merge) to the main branch on GitHub.
- GitHub's webhook notifies Amplify that a new commit landed.
- Amplify spins up a managed build image and installs dependencies.
- Amplify runs the build command, producing the production output.
- Amplify deploys the new build behind its CloudFront distribution and invalidates stale cache.
- The new version is live at ranjithr.com, typically within a few minutes of the push.
Amplify reads its build steps from a build spec checked into the repo:
version: 1
frontend:
phases:
preBuild:
commands:
- npm ci
build:
commands:
- npm run build
artifacts:
baseDirectory: .next
files:
- '**/*'
cache:
paths:
- node_modules/**/*
- .next/cache/**/*No manual deploy step, on purpose
There is noaws s3 sync or CLI deploy command anywhere in this workflow — the entire point of choosing Amplify here was to remove that step. A broken build simply fails the Amplify job and never reaches production; the previous deployment keeps serving traffic.Security
- HTTPS everywhere — CloudFront redirects all HTTP traffic to HTTPS, and the ACM certificate is validated and renewed automatically with no manual step.
- Secrets stay out of git — analytics IDs and any environment-specific values are set as Amplify environment variables, scoped to the branch, never committed to the repository.
- Least-privilege deploys — Amplify's GitHub integration only needs read access to the repository and its own service role to publish; it never needs direct IAM credentials checked into CI config.
- Immutable releases — every deploy is a new, versioned Amplify build; rolling back means re-promoting a previous build, not hand-editing anything live.
Branch previews are still public URLs
Amplify can auto-deploy a preview for every branch/PR. Preview URLs are reachable by anyone who has the link, so treat them the same as production for anything sensitive — don't rely on obscurity.Performance
CloudFront serves every static asset from an edge location near the visitor, so repeat views and navigations feel effectively instant. Next.js's own build output (pre-rendered pages, code-split JavaScript, optimized images) means the origin rarely has to do real work on a cache hit.
- Fingerprinted, hashed build assets get long-lived, immutable cache headers.
- HTML is revalidated frequently so a fresh deploy becomes visible without waiting on a manual cache invalidation.
- Images are served through Next.js's image pipeline rather than as unoptimized originals.
Monitoring & Analytics
Monitoring here is intentionally lightweight, matching the size of the project:
- Amplify build notifications — every build's pass/fail status is visible in the Amplify console and can email or notify on failure.
- Google Analytics — pageviews, traffic sources, and custom events (like the interactions tracked elsewhere on this site) for understanding real visitor behavior. It runs entirely client-side and is kept separate from the AWS hosting stack itself.
- Uptime — CloudFront and Amplify's hosting are managed AWS services with their own published availability SLAs, so there is no separate uptime monitor to run for the hosting layer itself.
Project Highlights
- Zero-touch deploys — a git push is the entire release process, end to end.
- Custom domain with fully managed, auto-renewing TLS — no certificate to track manually.
- Global CDN distribution included by default, with no separate CloudFront setup step.
- Effectively no infrastructure maintenance — nothing to patch, back up, or scale.
- Usage-based AWS cost model, which stays very low for a low-traffic personal site like this one.
Challenges
Rewrites & redirects on a managed platform
Next.js's own routing config and Amplify's rewrite/redirect rules can overlap. Getting trailing-slash behavior and the custom 404 to match what worked locally took a few iterations of Amplify's console-level rewrite rules.Branch-scoped environment variables
Environment variables in Amplify are per-branch by default. It's easy to set a value onmain and forget a preview branch needs it too, which surfaces as a feature working in production but silently missing on a PR preview.Cache visibility after a deploy
Because CloudFront invalidation happens automatically as part of an Amplify deploy, it's not always obvious from the console alone whether a specific change has finished propagating — a hard refresh and checking the deployed commit hash is the reliable way to confirm.Lessons Learned
- A managed platform like Amplify trades some low-level control (you don't touch the CloudFront distribution or bucket directly) for a much shorter path from commit to production — worth it for a fast-moving personal site, less so for a system that needs fine-grained infrastructure control.
- Keeping the build spec (
amplify.yml) in the repository, rather than only in the console, means the entire deploy process is reviewable in a pull request like any other code change. - Treating preview URLs as public by default, from day one, avoided ever having to retroactively lock one down.
Documentation & References
Official documentation for every service used in this deployment:
Final Result
The outcome is a real, production static website that deploys itself: a push to main becomes a live, HTTPS-secured, globally-cached update at ranjithr.com with no manual step in between, running on a usage-based AWS cost model that stays very low for a site at this traffic level — though, since every AWS service here bills by usage rather than a flat fee, the actual cost always depends on traffic and resource usage, not a fixed number. For a personal project, that ratio of automation to operating cost is the entire point of building it this way.