PROJECT / WEB + CLOUD

Deploying My Portfolio on AWS

An Astro portfolio deployed to a private Amazon S3 bucket behind CloudFront, with infrastructure managed through Terraform.

Live

Goal

Build a portfolio to document networking, cloud, and security projects: a place to share the work, explain decisions, and record lessons learned.

Stack

Astro generates the static site. Terraform manages the hosting infrastructure, and the AWS CLI uploads the build and invalidates the CloudFront cache.

Architecture

Visitors connect to CloudFront over HTTPS. HTTP requests redirect to HTTPS, and CloudFront uses Origin Access Control to sign requests and retrieve files from the private Amazon S3 bucket through its REST endpoint.

Custom domain — completed

The portfolio is live at jacobferguson.io. Cloudflare manages DNS, with the apex and www records pointing to d2q1cw2uv4nyuz.cloudfront.net. Cloudflare also holds the DNS-only ACM validation CNAME records, which remain in place for certificate renewal.

An issued AWS Certificate Manager certificate in us-east-1 covers jacobferguson.io and www.jacobferguson.io. Both names are configured as CloudFront aliases. CloudFront serves the certificate using SNI and the TLSv1.2_2021 security policy, requiring TLS 1.2 or later.

Security

All four S3 Block Public Access settings are enabled: BlockPublicAcls, IgnorePublicAcls, BlockPublicPolicy, and RestrictPublicBuckets. The bucket uses AES256 server-side encryption.

The bucket policy grants the CloudFront service permission to read objects only when the request's source ARN matches this distribution. The S3 bucket is not publicly accessible.

Routing

A CloudFront Function rewrites directory and extensionless URLs to the generated index.html files. For example, /about/ becomes /about/index.html, and /blog becomes /blog/index.html. Asset URLs with file extensions retain their filenames.

Deployment

  1. Run npm run build to generate the static site in dist/. Astro's configured site URL is https://jacobferguson.io, used for canonical URLs, social sharing metadata, RSS, and the sitemap.
  2. For infrastructure changes, run Terraform plan, review the proposed changes, and apply the saved plan. Content-only updates require only a build, S3 upload, and CloudFront invalidation.
  3. Use aws s3 sync with the portfolio profile to upload dist/ to the private S3 bucket.
  4. Invalidate the CloudFront cache so visitors receive the updated files.

Troubleshooting

Next steps — planned

Back to all projects