Back

Astro build wallpaper

/

How to Deploy an Astro static Site to S3 via Terraform

Astro is a popular static site generator that offers a unique approach to building fast and content-focused websites. It allows you to use your favorite UI framework, like React, Vue, or Svelte, while keeping the JavaScript footprint minimal for optimal performance. I started off with Hugo, then Jekyll, but settled on Astro, due to its customisability, while still retaining that static element.

I’m not a frontend web-development specialist, so having the heavy lifting taken care of Astro is a big relief, and lowers the barrier of entry when creating a website to deploy!

When it comes to deploying your Astro site, Terraform is a strong choice, as it allows provider-agnostic configuration. I currently work with AWS day-to-day, but am keen to have the ability to refactor this site’s Infrastructure-as-Code to another provider (Azure, GCP), if I start working with a new platform. This allows my personal website to be a good ‘hello world’ for new cloud providers in future.

Prerequisites
Before we begin, ensure you have the following set up:

  • Node.js and npm (or yarn): You’ll need these to build your Astro site.
  • Astro Project: If you don’t have an existing Astro project, you can use the Astro CLI to create one quickly.
  • Terraform: Make sure you have Terraform installed.
  • AWS Account: You’ll need an active AWS account for this tutorial.

Setting Up Your Astro Project
If you’re starting fresh, use the Astro CLI to bootstrap your project. For this tutorial, we’ll be focusing on deploying a static site, which is what Astro excels at. Ensure your astro.config.mjs file includes:

  • output: 'static': This is crucial for building a static website that can be hosted on S3.
  • publicDir: 'public': This setting tells Astro where your public assets live.

Initializing Terraform
Here’s how you can initialize Terraform for your project. Add the following to your main.tf file:

terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">= 1.2.0"
backend "s3" {
bucket = "my-terraform-state-cogsdev.com"
key = "pipeline/terraform.tfstate"
region = "eu-west-2"
dynamodb_table = "terraform-lock-table"
encrypt = true
}
}
variable "aws_region" {
default = "eu-west-2"
}
provider "aws" {
region = var.aws_region
}

Creating the S3 Bucket and CloudFront Distribution
Now, let’s provision the infrastructure for hosting your site.

  1. Provisioning an S3 bucket:
resource "aws_s3_bucket" "astroSite" {
bucket = var.bucketName
}
resource "aws_s3_bucket_public_access_block" "astroSite_public_access_block" {
bucket = aws_s3_bucket.astroSite.id
block_public_acls = false
block_public_policy = false
ignore_public_acls = false
restrict_public_buckets = false
}
  1. Configuring CloudFront and OAI:
resource "aws_cloudfront_origin_access_identity" "oai" {
comment = "OAI for astroSite"
}
resource "aws_cloudfront_distribution" "cdn" {
origin {
domain_name = aws_s3_bucket.astroSite.bucket_regional_domain_name
origin_id = "S3-astroSite"
s3_origin_config {
origin_access_identity = aws_cloudfront_origin_access_identity.oai.cloudfront_access_identity_path
}
}
enabled = true
default_root_object = "index.html"
default_cache_behavior {
target_origin_id = "S3-astroSite"
viewer_protocol_policy = "redirect-to-https"
}
viewer_certificate {
acm_certificate_arn = aws_acm_certificate.cert.arn
ssl_support_method = "sni-only"
minimum_protocol_version = "TLSv1.2_2019"
}
}

What is OAI? An Origin Access Identity (OAI) is a CloudFront feature that allows CloudFront to securely fetch content from an Amazon S3 bucket without exposing the bucket to public access. By creating an OAI and attaching it to the S3 bucket policy, you ensure that only CloudFront can access your bucket’s objects.

  1. Adding MIME Types:

When you host files on S3, the correct MIME type (also called “content type”) needs to be set for each file. This tells the browser how to handle and display the file. For example:

.html files should be served as text/html. .css files should be served as text/css. Images like .png should be served as image/png.

locals {
mime_types = {
"html" = "text/html"
"css" = "text/css"
"js" = "application/javascript"
"json" = "application/json"
"png" = "image/png"
# Add more as needed
}
}

I had some big issues being able to see my deployed live website because I didn’t set the MIME types for each incoming file, meaning S3 was not able to parse them correctly in the website.


Building and Deploying Your Astro Site
With infrastructure ready, deploy your site:

  1. Building the Astro project: Run npm run build.
  2. Uploading files:
resource "aws_s3_object" "provision_source_files" {
bucket = aws_s3_bucket.astroSite.bucket
for_each = fileset("dist/", "**/*.*")
key = each.value
source = "dist/${each.value}"
content_type = lookup(
local.mime_types,
lower(regex(".*\.([^.]+)$", each.value)[0]),
"application/octet-stream"
)
}

Deployment

From here, deploying the site can be done in two steps:

terraform plan

This enumerates the main.tf file, and displays the resources and their dependencies that will be created on first deploy. In future deploys and changes, it will display the difference, highlighting resources to be created, modified or destroyed.

terraform apply

Thtis is the deployment step, and will have a similar start to plan, but then require an explitly typed yes command to enact the deployment. I was pretty nervous on the first go round of this command, but I now appreciate there shouldn’t be any big surprises if I’ve paid attention to the plan command beforehand.

The next step is adding this to a CICD pipeline, to enable testing and automated deployments. I do have this in place already, and will release a supplementary post detailing my steps and configuration.

Conclusion

Deploying an Astro site with Terraform allows you to manage your infrastructure declaratively. The examples above show how to automate provisioning with Terraform for reliable, repeatable deployments. This combination of Astro’s performance focus and Terraform’s infrastructure automation capabilities creates a streamlined path to hosting modern web applications.