logo
Menu
How to Seamlessly Host Your Personal Portfolio Website on AWS with S3, CloudFront, Route 53, and GitHub Actions

How to Seamlessly Host Your Personal Portfolio Website on AWS with S3, CloudFront, Route 53, and GitHub Actions

Discover the step-by-step process of hosting your personal portfolio website on AWS using S3, CloudFront, Route 53, and GitHub Actions. This guide will help you make your portfolio internet-facing and automate updates and deployments with continuous integration and continuous delivery (CI/CD). Perfect for developers looking to streamline their website hosting with AWS services.

Published Jun 7, 2024

Introduction

In today's digital age, having a personal portfolio website is more important than ever. It serves as your digital business card, showcasing your skills, projects, and professional journey to potential employers, clients, and collaborators. A well-crafted portfolio can set you apart from the competition, demonstrating your technical proficiency, creativity, and commitment to your field.
For developers and tech professionals, a portfolio website is a dynamic way to present your work, allowing you to highlight projects with detailed descriptions, code snippets, and interactive demos. It offers a platform to share your expertise, blog about your experiences, and connect with a broader audience.
By hosting your portfolio on AWS, you leverage the robustness, scalability, and security of one of the leading cloud service providers. AWS services like S3, CloudFront, Route 53, and GitHub Actions enable you to create a fast, reliable, and continuously updated website with minimal effort. This guide will walk you through the process of setting up and deploying your personal portfolio website on AWS, ensuring it is always accessible and performant.

1. Hosted Zone Setup

  • A public hosted zone is a container that holds information about how you want to route traffic on the internet for a specific domain, such as example.com, and its subdomains (acme.example.com, zenith.example.com).
  • Navigate to the Route 53 service in the AWS Console. In the left-hand panel, locate and click on "Registered Domains," then select "Register Domain."
  • Consider creating a domain using your first and last name, such as "elliotalderson.com." These domains typically cost around $15 per year. After completing the registration process and receiving approval, you will see your new domain listed under the "Hosted Zones" section in the left-hand panel. This hosted zone is where you will manage the DNS settings for your domain.
  • We will come back to Route53 in a later step for routing traffic to Cloudfront.
  • AWS CDK Hosted Zone Code Snippet:
1
2
3
4
5
6
7
8
9
10
// Route53 Hosted Zone Lookup for Site Hosting and Record Adding
let hostedZone: route53.IHostedZone;
const domainName = "elliotalderson.com";
hostedZone = route53.HostedZone.fromLookup(
this,
"ElliotAldersonHostedZone",
{
domainName: domainName,
},
);

2. Portfolio Assets

  • There are various approaches to building your portfolio, commonly involving files such as HTML, CSS, JavaScript, and images. Personally, I use React and webpack, running npm run build to generate a build directory containing my website assets. However, for simplicity, this tutorial can work with just an index.html file that contains "Hello World".

3. S3 Bucket Setup

  • Create a general-purpose S3 bucket with ACLs disabled. Be sure to uncheck all public access settings as we want our S3 bucket to be public and internet-facing. You can enable bucket versioning, set the encryption type to Server-side encryption with Amazon S3 managed keys (SSE-S3), and disable the bucket key and object lock.
  • After creating the bucket, enable static website hosting at the very bottom of the properties tab in the bucket. Be sure to specify your index document, typically named index.html unless you named it something else or placed it inside a directory.
  • Once that's configured, you can upload your website assets to your S3 bucket using the uploader. Notice how my index.html is in the root directory.
  • Add the following Bucket Policy in the permissions tab to your bucket. Be sure to replace BUCKET_NAME
1
2
3
4
5
6
7
8
9
10
11
12
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPublicReadAccess",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::BUCKET_NAME/*"
}
]
}
  • You can validate that you completed this step correctly by navigating to your bucket website endpoint, which is displayed in the static website hosting section in the properties tab. Your website should be displayed there.
  • AWS CDK S3 Bucket Code Snippet:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// S3 Bucket for Website Hosting
const grantStarkmanWebsiteBucket = new s3.Bucket(
this,
'GrantStarkmanWebsiteBucket',
{
bucketName: 'grantstarkman',
websiteIndexDocument: "index.html",
removalPolicy: RemovalPolicy.RETAIN,
versioned: true,
blockPublicAccess: {
blockPublicAcls: false,
blockPublicPolicy: false,
ignorePublicAcls: false,
restrictPublicBuckets: false,
},
publicReadAccess: true,
},
);

// S3 Bucket deployment from local website/build directory
// (Not necessary for this tutorial)
new s3deploy.BucketDeployment(this, `DeployVozAmigo`, {
sources: [
s3deploy.Source.asset("../website/build"),
],
destinationBucket: grantStarkmanWebsiteBucket,
});

4. ACM (AWS Certificate Manager) Setup

1
2
3
4
5
6
7
8
9
const grantStarkmanCloudfrontSiteCertificate = new acm.Certificate(
this,
"GrantStarkmanCloudfrontSiteCertificate",
{
domainName: "elliotalderson.com",
certificateName: "elliotalderson.com",
validation: acm.CertificateValidation.fromDns(hostedZone),
},
);

5. CloudFront Setup

  • Create a CloudFront distribution with the origin domain of the S3 bucket you created in the previous step. After selecting your S3 bucket, be sure to choose the option to "Use Website Endpoint." You can leave the default settings as they are for the origin section, but for the Default Cache Behavior, be sure to select "Redirect HTTP to HTTPS" and set the cache policy to Caching Optimized. The remaining origin settings and function associations can also be left as is.
  • For the Web Application Firewall, set it to "Do not enable security protections." In the final settings section, set the Alternate Domain Name (CNAME) to the domain you created in the previous step, such as "elliotalderson.com." Also, select the custom SSL certificate you created in the previous step. It may take sometime for the certificate to be issued.
  • Modify your S3 Bucket Policy again in the permissions tab to your bucket. Be sure to replace BUCKET_NAME, ACCOUNT_NUMBER, and DISTRIBUTION_ID.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPublicReadAccess",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::BUCKET_NAME/*"
},
{
"Sid": "AllowCloudFrontServicePrincipalReadOnly",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::BUCKET_NAME/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::ACCOUNT_NUMBER:distribution/DISTRIBUTION_ID"
}
}
}
]
}
  • You can validate that you completed this step correctly by navigating to your Cloudfront distribution domain name, which is displayed in details section in the selected Cloudfront distribution. Your website should be displayed there.
  • AWS CDK Cloudfront Code Snippet:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const grantStarkmanDistribution = new cloudfront.Distribution(
this,
"GrantStarkmanDistribution",
{
comment: `CloudFront distribution for ${grantStarkmanWebsiteBucket.bucketName} bucket.`,
defaultBehavior: {
origin: new origins.S3Origin(grantStarkmanWebsiteBucket, {
originAccessIdentity: grantStarkmanCloudfrontOAI
}),

viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
},
errorResponses: [
{
httpStatus: 403,
responseHttpStatus: 200,
responsePagePath: "/index.html",
},
{
httpStatus: 404,
responseHttpStatus: 200,
responsePagePath: "/index.html",
},
],
defaultRootObject: "index.html",
domainNames: ["grantstarkman.com"],
certificate: grantStarkmanCloudfrontSiteCertificate,
},
);

6. Route 53 Setup

1
2
3
4
5
6
7
8
// Route 53 Record for Cloudfront Distribution Frontend
new route53.ARecord(this, `GrantStarkmanCloudFrontARecord`, {
zone: hostedZone,
recordName: "elliotalderson.com",
target: route53.RecordTarget.fromAlias(
new route53Targets.CloudFrontTarget(grantStarkmanDistribution),
),
});

7. GitHub Actions CI/CD Setup

  • It's assumed that you already have a GitHub repository set up and that you have pushed your newest code changes. In your GitHub project, click the Actions tab and select “set up a workflow yourself.” This will create a main.yml file inside of .github/workflows/.
  • Be sure to configure your AWS credentials in your GitHub repository. You can do this by navigating to the repository settings, then to Actions, and selecting Secrets and Variables. Create new repository secrets for AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, WEBSITE_BUCKET_NAME, and DISTRIBUTION_ID.
  • You can copy my workflow file directly. Just keep in mind that it utilizes NodeJS with some dependency and build steps that may not be necessary for all applications.
  • The crucial step here is using the aws s3 sync command. Make sure to replace "website/build" with the path to your site assets. It's also essential to invalidate the CloudFront cache because the CDN caches your S3 bucket's site contents on servers worldwide.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
name: Build React App and AWS S3 Sync

on:
push:
branches:
- main

jobs:
BuildAndDeployToAWS:
name: Build and Deploy to AWS
runs-on: ubuntu-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v2

- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: '16'

- name: Install Dependencies For React App
run: npm install
working-directory: ./website

- name: Build React App
run: npm run build
working-directory: ./website

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: 'us-east-1'

- name: Sync Website Changes with S3
run: aws s3 sync website/build s3://${{ secrets.WEBSITE_BUCKET_NAME }} --delete

- name: Invalidate CloudFront Cache
run: aws cloudfront create-invalidation --distribution-id ${{ secrets.DISTRIBUTION_ID }} --paths "/*"
  • You can copy my workflow file directly.
  • Now, every time you push a change, GitHub Actions will automatically update your S3 bucket contents and invalidate the CloudFront cache.

Conclusion

  • In this blog, we covered the essential steps to host your personal portfolio website on AWS. We started by setting up a hosted zone in Route 53, created a domain, and managed the DNS settings. We then built and prepared our portfolio assets, and set up an S3 bucket to host these assets as a static website. Next, we requested a certificate from AWS Certificate Manager (ACM) to secure our site with HTTPS. We then configured a CloudFront distribution to serve our site globally with optimized caching and security. Finally, we integrated GitHub Actions for continuous deployment, ensuring that any changes pushed to our repository automatically update the S3 bucket contents and invalidate the CloudFront cache.
  • Hosting your portfolio on AWS not only ensures high availability and performance but also leverages the powerful features and scalability of AWS services. I encourage you to try hosting your portfolio on AWS. The process might seem complex at first, but with this step-by-step guide, you can achieve a professional, reliable, and secure online presence that effectively showcases your skills and projects. Give it a try, and take your personal branding to the next level!
     

Comments