Skip to main content

Hosting on S3

We're just about ready to deploy the website to AWS S3 and see it running on a cloud server with a working URL.

If you open aws-infra/lib/aws-infra-stack.ts, it's a template file that won't create any AWS resources beyond an empty CDK stack:

aws-infra/lib/aws-infra-stack.ts
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
// import * as sqs from 'aws-cdk-lib/aws-sqs';

export class AwsInfraStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

// The code that defines your stack goes here

// example resource
// const queue = new sqs.Queue(this, 'AwsInfraQueue', {
// visibilityTimeout: cdk.Duration.seconds(300)
// });
}
}

Let's go ahead and create an S3 bucket to store and serve the static website files. It's easy to define a deployment that uploads locally built website files to S3, so well do that now too. Change aws-infra-stack.ts to be:

aws-infra/lib/aws-infra-stack.ts
import * as cdk from "aws-cdk-lib";
import type { Construct } from "constructs";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as s3deploy from "aws-cdk-lib/aws-s3-deployment";

export class AwsInfraStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

const websiteDir = `${__dirname}/../..`;

const websiteBucket = new s3.Bucket(this, "Bucket", {
websiteIndexDocument: "index.html",
websiteErrorDocument: "404.html",
publicReadAccess: true,
blockPublicAccess: {
blockPublicPolicy: false,
blockPublicAcls: false,
ignorePublicAcls: false,
restrictPublicBuckets: false,
},
});

new s3deploy.BucketDeployment(this, "Deploy", {
sources: [s3deploy.Source.asset(`${websiteDir}/build`)],
destinationBucket: websiteBucket,
});

// Display the S3 bucket URL at the end of `cdk deploy`
// so we can try loading the site:
new cdk.CfnOutput(this, "S3BucketUrl", {
value: websiteBucket.bucketWebsiteUrl,
});
}
}
tip

If you destroy this CDK stack and start over, or change the S3 bucket ID (causing it to be removed from the stack and replaced with a new bucket), the original S3 bucket with not be deleted automatically. You need to delete it manually in the AWS console web UI or with the AWS CLI.

This is intentional default behavior in case any files are uploaded without CDK that you don't want auto-deleted (for example, maybe you manually upload some large binary files, or use a video transcoding service to put processed video files in this bucket).

If you are sure it's ok to auto-delete everything if this CDK stack is torn down, then you can add these options when constructing the s3.Bucket:

removalPolicy: cdk.RemovalPolicy.DESTROY,
autoDeleteObjects: true,

If you're building a semi-permanent website, I'd leave these options disabled. If you are spinning up transient cloud stacks for testing purposes, it's a good to enable these options to avoid accumulating tons of junk on S3.

At this point, running cdk deploy (in the aws-infra folder) probably gives an error:

Error: Cannot find asset at .../YOUR_SITE_NAME/build

That happens if the site hasn't explicitly been built for deployment yet. That's easily solved by running a command (npm run build in the top-level folder), but let's automate a solution by adding this near the top of the AwsInfraStack constructor:

aws-infra/lib/aws-infra-stack.ts
// ... other imports ...
import { execSync } from "node:child_process";

export class AwsInfraStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

const websiteDir = `${__dirname}/../..`;

console.log("Building the website for deployment...");
execSync("npm run build", { cwd: websiteDir, stdio: "inherit" });

// ... the rest of the code is the same as before

Now cdk deploy should work. For me it typically takes 1-2 minutes, and might be a little slower the first time. You should see something like this at the end:

 ✅  SiteOnAws

✨ Deployment time: 109.03s

Outputs:
SiteOnAws.S3BucketUrl = http://siteonaws-bucket83908e77-uft5sxtw7dfw.s3-website-us-west-1.amazonaws.com

You can open that URL in the browser to see the website running on AWS. 🎉