Personal portfolio website showcasing experience, projects, and contact info.
- 🎨 Frontend: React + TypeScript + Vite single-page portfolio.
- ☁️ Infra: Terraform stack for S3 + CloudFront + ACM + Route 53.
- 🐳 Local Dev: Docker Compose or native Node.js.
portfolio/
├── docs/ # DNS + IAM setup guides
├── frontend/ # React + TS app (Vite)
├── infra/ # Terraform for AWS hosting
├── docker-compose.yml # Local dev via Docker
├── DOCKER.md # Docker workflow
├── LICENSE
└── README.md
cd frontend
npm install
npm run devdocker-compose up -dOpen: http://localhost:5173
Docs: DOCKER.md
Terraform provisions:
- ✅ S3 (private bucket)
- ✅ CloudFront (OAC)
- ✅ ACM (us-east-1)
- ✅ Route 53 DNS
Infra docs: infra/README.md
- Update
infra/terraform.tfvarswith your domain, hosted zone, and region. - Configure
infra/backend.hclfor remote state (S3 + DynamoDB). - Export AWS credentials / profile (see
infra/README.md).
# Provision infra
cd infra
terraform init -backend-config=backend.hcl
terraform workspace new prod || terraform workspace select prod
terraform apply
# Build frontend
cd ../frontend
npm install
npm run build
# Upload to S3 (replace with the output bucket name)
aws s3 sync dist s3://<site_bucket_name> --delete
# Invalidate CloudFront (replace with output distribution id)
aws cloudfront create-invalidation \
--distribution-id <cloudfront_distribution_id> \
--paths "/*"Outputs for site_bucket_name and cloudfront_distribution_id appear after terraform apply.
cd infra
terraform state show aws_s3_bucket.site
terraform state show aws_cloudfront_distribution.siteYou can also use outputs:
terraform output -raw site_bucket_name
terraform output -raw cloudfront_distribution_id# If you already have the distribution ID
aws cloudfront get-distribution --id <distribution_id> \
--query "Distribution.{Status:Status,Enabled:Enabled,DomainName:DomainName,LastModified:LastModifiedTime}" \
--output table
# Find distribution IDs
aws cloudfront list-distributions \
--query "DistributionList.Items[].{Id:Id,Domain:DomainName,Status:Status}" \
--output table
# Check invalidation progress
aws cloudfront list-invalidations --distribution-id <distribution_id> \
--query "InvalidationList.Items[].{Id:Id,Status:Status,CreateTime:CreateTime}" \
--output table- HTML is cached short-term; static assets (e.g.,
/assets/*) are cached long-term. - Prefer invalidating only
/index.htmlafter deploys (avoid/*unless necessary). - This reduces origin load and helps prevent S3
SlowDown(503) spikes.
- State is stored in a shared bucket, with a unique key per project.
- This repo uses
portfolio/terraform.tfstateto avoid collisions. - Reusing the same bucket is safe as long as keys/prefixes are unique.
docs/Route53_Domain_Setup.mddocs/terraform-iam-user-setup.mdinfra/README.mdCHANGELOG.md
- 2026-02-01 — Bootstrap grid responsiveness, donation widget click-toggle + mobile sizing, CloudFront cache policies, centralized state key.
- Frontend scaffolded and ready for content
- Infra ready for AWS provisioning
MIT — see LICENSE.