AWS Migration Guide - Nov 10, 2025
Time Required: 4-8 hours Cost: ~$1-5/month for AWS services Result: joshuamichaelhall.com hosted on AWS infrastructure
Prerequisites Checklist
Before starting, ensure you have:
- AWS account created and payment method added
- AWS CLI installed (
brew install awsclior download from AWS) - AWS CLI configured (
aws configurewith access keys) - Jekyll site builds successfully locally
- Domain currently pointing to GitHub Pages (we’ll migrate DNS)
Phase 1: Build Static Site (15 minutes)
Step 1.1: Build Jekyll Site Locally
cd /Users/jmh/repos/joshuamichaelhall.github.io
# Install dependencies (if needed)
bundle install
# Build the site
bundle exec jekyll build
# Verify _site/ directory was created
ls -la _site/
Checkpoint: You should see HTML files in _site/ directory
Step 1.2: Test Build Locally (Optional)
# Serve locally to verify everything works
bundle exec jekyll serve
# Open http://localhost:4000 in browser
# Ctrl+C to stop when verified
Phase 2: S3 Setup (30-45 minutes)
Step 2.1: Create S3 Bucket
- Go to AWS Console → S3
- Create bucket:
- Bucket name:
joshuamichaelhall.com(must match your domain) - Region:
us-east-1(recommended for CloudFront) - Uncheck “Block all public access” (we need public read)
- Acknowledge the warning
- Create bucket
- Bucket name:
Step 2.2: Enable Static Website Hosting
- Click on bucket
joshuamichaelhall.com - Go to Properties tab
- Scroll to Static website hosting
- Click Edit
- Enable static website hosting
- Index document:
index.html - Error document:
404.html - Save changes
- Copy the bucket website endpoint URL (you’ll need this later)
Step 2.3: Upload Site Files
Option A: AWS Console (Easy)
- Go to Objects tab in your S3 bucket
- Click Upload
- Click Add folder
- Navigate to
/Users/jmh/repos/joshuamichaelhall.github.io/_site/ - Select all files and folders inside
_site/ - Click Upload
- Wait for upload to complete
Option B: AWS CLI (Faster)
cd /Users/jmh/repos/joshuamichaelhall.github.io
# Sync _site/ to S3 bucket
aws s3 sync _site/ s3://joshuamichaelhall.com --delete
# Set proper content types
aws s3 sync _site/ s3://joshuamichaelhall.com \
--content-type "text/html" \
--exclude "*" \
--include "*.html" \
--delete
aws s3 sync _site/ s3://joshuamichaelhall.com \
--content-type "text/css" \
--exclude "*" \
--include "*.css" \
--delete
aws s3 sync _site/ s3://joshuamichaelhall.com \
--content-type "application/javascript" \
--exclude "*" \
--include "*.js" \
--delete
Step 2.4: Configure Bucket Policy for Public Read
- Go to Permissions tab
- Scroll to Bucket policy
- Click Edit
- Paste this policy (replace bucket name if different):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::joshuamichaelhall.com/*"
}
]
}
- Save changes
Step 2.5: Test S3 Static Website
- Go back to Properties tab
- Scroll to Static website hosting
- Click the Bucket website endpoint URL
- Your site should load (without HTTPS or custom domain yet)
Checkpoint: Site loads at S3 endpoint (e.g., http://joshuamichaelhall.com.s3-website-us-east-1.amazonaws.com)
Phase 3: CloudFront Distribution (30-45 minutes)
Step 3.1: Request SSL Certificate in ACM
IMPORTANT: Must be done in us-east-1 region for CloudFront
- AWS Console → Certificate Manager (ACM)
- Switch region to us-east-1 (top right, even if your S3 is elsewhere)
- Click Request certificate
- Choose Request a public certificate
- Domain names:
- Add
joshuamichaelhall.com - Add
www.joshuamichaelhall.com(click “Add another name”)
- Add
- Validation method: DNS validation (recommended)
- Click Request
Step 3.2: Validate Certificate via DNS
- Click on the certificate you just created
- Under Domains, you’ll see CNAME records to add
- Click Create records in Route 53 (if your domain is in Route 53)
- OR manually add CNAME records to your DNS provider
- Wait for validation (usually 5-30 minutes)
- Certificate status should change to Issued
Checkpoint: Certificate status = “Issued” in ACM
Step 3.3: Create CloudFront Distribution
- AWS Console → CloudFront
- Click Create distribution
- Origin domain:
- DO NOT select S3 bucket from dropdown
- Instead, paste your S3 website endpoint (from Step 2.2)
- Example:
joshuamichaelhall.com.s3-website-us-east-1.amazonaws.com
- Protocol: HTTP only (S3 static website endpoints don’t support HTTPS)
- Name:
joshuamichaelhall-s3-origin
Default cache behavior:
- Viewer protocol policy: Redirect HTTP to HTTPS
- Allowed HTTP methods: GET, HEAD
- Cache policy: CachingOptimized
- Leave other defaults
Settings:
- Alternate domain names (CNAMEs):
- Add
joshuamichaelhall.com - Add
www.joshuamichaelhall.com
- Add
- Custom SSL certificate: Select the certificate you created in ACM
- Default root object:
index.html -
Standard logging: Off (or enable if you want logs)
- Click Create distribution
Wait for deployment (15-30 minutes) - Status will change from “Deploying” to “Enabled”
Checkpoint: CloudFront distribution status = “Enabled”
Step 3.4: Test CloudFront Distribution
- Copy the Distribution domain name (e.g.,
d123456abcdef.cloudfront.net) - Open it in browser
- Site should load with HTTPS
Checkpoint: Site loads at CloudFront URL with HTTPS
Phase 4: Route 53 DNS Configuration (30 minutes + propagation time)
Step 4.1: Check Current DNS Setup
Your domain joshuamichaelhall.com is currently pointing to GitHub Pages. We need to:
- Move DNS to Route 53 (if not already there)
- Update DNS records to point to CloudFront
Step 4.2: Create Route 53 Hosted Zone (if needed)
If domain is already in Route 53, skip to Step 4.3
- AWS Console → Route 53
- Click Hosted zones
- Click Create hosted zone
- Domain name:
joshuamichaelhall.com - Type: Public hosted zone
- Click Create hosted zone
- Note the 4 nameservers (you’ll need these)
Step 4.3: Update Domain Nameservers (if needed)
If your domain registrar is NOT AWS Route 53:
- Go to your domain registrar (GoDaddy, Namecheap, etc.)
- Find DNS/Nameserver settings
- Change nameservers to the 4 Route 53 nameservers from Step 4.2
- Save changes
- Wait 1-24 hours for propagation (usually faster)
If your domain is already registered in Route 53, skip this step
Step 4.4: Create DNS Records
- Go to Route 53 → Hosted zones →
joshuamichaelhall.com - Click Create record
Record 1: Root domain (joshuamichaelhall.com)
- Record name: (leave blank)
- Record type: A
- Alias: Yes (toggle on)
- Route traffic to: Alias to CloudFront distribution
- Choose your CloudFront distribution from dropdown
- Click Create records
Record 2: www subdomain (www.joshuamichaelhall.com)
- Click Create record again
- Record name:
www - Record type: A
- Alias: Yes (toggle on)
- Route traffic to: Alias to CloudFront distribution
- Choose your CloudFront distribution from dropdown
- Click Create records
Step 4.5: Remove GitHub Pages DNS (Optional - do this last)
Once everything works on AWS:
- Go to your GitHub repo settings
- Remove custom domain from GitHub Pages
- (Optional) Delete CNAME file from repo
Checkpoint: Wait 5-60 minutes for DNS propagation
Phase 5: Verification & Testing (30 minutes)
Step 5.1: Test All URLs
Open these URLs in browser (private/incognito mode to avoid cache):
http://joshuamichaelhall.com→ Should redirect to HTTPShttps://joshuamichaelhall.com→ Should load sitehttp://www.joshuamichaelhall.com→ Should redirect to HTTPShttps://www.joshuamichaelhall.com→ Should load site
Step 5.2: Test SSL Certificate
- Click padlock icon in browser
- View certificate
- Issued by: Amazon
- Valid for: joshuamichaelhall.com, www.joshuamichaelhall.com
Step 5.3: Test All Pages
Navigate through your site:
- Homepage
- Portfolio page
- Blog posts
- About page
- Contact page
- 404 page (type invalid URL)
Step 5.4: Test QR Code
- Open QR code scanner on phone
- Scan the QR code from your business card
- Should open
https://joshuamichaelhall.com - Site should load on mobile
Step 5.5: Performance Check
- Run Google PageSpeed Insights: https://pagespeed.web.dev/
- Enter
https://joshuamichaelhall.com - Should see good performance scores (CloudFront is fast)
Phase 6: Update GitHub Workflow (Optional - 30 minutes)
Option A: Keep GitHub Pages, Manual AWS Sync
When you update site:
# 1. Build locally
bundle exec jekyll build
# 2. Sync to S3
aws s3 sync _site/ s3://joshuamichaelhall.com --delete
# 3. Invalidate CloudFront cache (so changes appear immediately)
aws cloudfront create-invalidation \
--distribution-id YOUR_DISTRIBUTION_ID \
--paths "/*"
Option B: Automated Deployment via GitHub Actions (Better - do this later)
Create .github/workflows/deploy-to-aws.yml:
name: Deploy to AWS
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.1
bundler-cache: true
- name: Build Jekyll site
run: bundle exec jekyll build
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: $
aws-secret-access-key: $
aws-region: us-east-1
- name: Sync to S3
run: aws s3 sync _site/ s3://joshuamichaelhall.com --delete
- name: Invalidate CloudFront
run: |
aws cloudfront create-invalidation \
--distribution-id $ \
--paths "/*"
Setup GitHub Secrets:
- GitHub repo → Settings → Secrets and variables → Actions
- Add secrets:
AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYCLOUDFRONT_DISTRIBUTION_ID
This is a Cloud Resume Challenge component - add this after Re:Invent
Cost Breakdown
Monthly AWS costs (estimated):
- S3 storage: $0.02-0.10 (for ~100MB of files)
- CloudFront: $0.10-1.00 (first 1TB transfer is cheap)
- Route 53 hosted zone: $0.50/month
- Total: ~$1-5/month
First year includes:
- CloudFront: 1 TB data transfer out free (12 months)
- Route 53: First hosted zone free (12 months)
Troubleshooting
Problem: Certificate validation stuck
Solution:
- Check Route 53 CNAME records were created correctly
- Wait up to 30 minutes
- If using external DNS provider, manually add CNAME records
Problem: Site loads but no HTTPS
Solution:
- Verify CloudFront distribution has correct certificate attached
- Check certificate is in us-east-1 region
- Wait for CloudFront deployment to complete
Problem: 403 Forbidden error
Solution:
- Check S3 bucket policy allows public read
- Verify files were uploaded to bucket
- Check CloudFront origin points to S3 website endpoint, not bucket directly
Problem: DNS not resolving
Solution:
- Wait for DNS propagation (up to 24 hours)
- Check nameservers are correctly set
- Use
dig joshuamichaelhall.comto check DNS records - Clear browser cache / use incognito mode
Problem: Old GitHub Pages site still showing
Solution:
- Clear browser cache
- Wait for DNS propagation
- Remove custom domain from GitHub Pages settings
- Delete CNAME file from repo
After Migration Success
Update your resume/LinkedIn:
- “Built and deployed portfolio website using Jekyll, AWS S3, CloudFront, Route 53, and ACM”
- “Implemented secure HTTPS delivery using AWS Certificate Manager”
- “Configured CloudFront CDN for global content delivery”
Update your Re:Invent pitch:
“I deployed my portfolio site on AWS infrastructure - S3 for static hosting, CloudFront for CDN, Route 53 for DNS, and ACM for SSL certificates. After Re:Invent, I’m planning to enhance it with serverless backend - Lambda, DynamoDB, and API Gateway.”
Post-Re:Invent enhancements (Dec 7-21):
- Add visitor counter (DynamoDB + Lambda + API Gateway)
- Set up CI/CD with GitHub Actions (automated deployment)
- Add monitoring with CloudWatch
- Write blog post documenting architecture
- Add infrastructure-as-code (Terraform or CloudFormation)
Success Criteria - You’re Done When:
- Site loads at https://joshuamichaelhall.com
- SSL certificate shows valid (padlock icon)
- All pages work (portfolio, blog, about, contact)
- QR code on business cards opens your site
- Site loads on mobile
- No GitHub Pages references (fully migrated to AWS)
- You can explain the architecture to someone at Re:Invent
Architecture Diagram (for your understanding)
User Browser
↓
Route 53 DNS (joshuamichaelhall.com)
↓
CloudFront CDN (HTTPS, global edge locations)
↓
S3 Bucket (static website hosting)
↓
Your Jekyll site files (HTML, CSS, JS)
ACM Certificate (SSL/TLS) ← CloudFront uses this
Key AWS Services Used (Know this for Re:Invent)
- Amazon S3: Object storage for static website files
- Amazon CloudFront: CDN for fast global content delivery
- Amazon Route 53: DNS service for domain management
- AWS Certificate Manager (ACM): Free SSL/TLS certificates
- (Future) AWS Lambda: Serverless compute for backend logic
- (Future) Amazon DynamoDB: NoSQL database for visitor counter
- (Future) Amazon API Gateway: RESTful API for frontend/backend communication
Timeline for Nov 10
- 8:00 AM - 9:00 AM: Phase 1 - Build Jekyll site
- 9:00 AM - 10:00 AM: Phase 2 - S3 setup and upload
- 10:00 AM - 11:30 AM: Phase 3 - ACM certificate + CloudFront (includes wait time)
- 11:30 AM - 12:00 PM: Lunch break (while CloudFront deploys)
- 12:00 PM - 1:00 PM: Phase 4 - Route 53 DNS configuration
- 1:00 PM - 2:00 PM: Phase 5 - Testing and verification
- 2:00 PM - 3:00 PM: Buffer time for troubleshooting
- 3:00 PM - 4:00 PM: Document what you did, prepare Re:Invent talking points
Total: 6-8 hours including wait times
Your Business Cards Are Now Valid
✅ AWS Solutions Architect Associate - Passing Nov 9 ✅ Security+ - Passing Nov 29 ✅ Cloud Resume Demo - Live at joshuamichaelhall.com (AWS-hosted)
All claims are legitimate and defensible.
Good luck with the migration! Focus on AWS SAA studying until Nov 9, then execute this on Nov 10.