Deploying Hugo to AWS S3 with Terraform: A Complete Guide
Introduction
In this post, I’ll walk you through deploying a Hugo static site to AWS S3 with a custom domain and SSL certificate using Terraform. We’ll create a complete infrastructure-as-code solution that’s reusable and production-ready.
What We’re Building
- Hugo static site hosted on AWS S3
- Custom domain with SSL certificate
- CloudFront CDN for global distribution
- Route53 for DNS management
- Terraform module for reusable infrastructure
Prerequisites
- AWS CLI configured
- Terraform installed
- Hugo installed
- A domain name you own
Step 1: Setting Up Hugo
First, create a new Hugo site:
hugo new site quickstart
cd quickstart
git init
git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke themes/ananke
echo "theme = 'ananke'" >> hugo.toml
Step 2: Creating the Terraform Infrastructure
Using the Terraform Module
Create a terraform/main.tf
file to use the module:
provider "aws" {
region = "eu-central-1"
}
provider "aws" {
alias = "us_east_1"
region = "us-east-1"
}
module "hugo_website" {
source = "github.com/username/terraform-hugo-s3-module"
domain_name = "example.com"
aws_region = "eu-central-1"
providers = {
aws.us_east_1 = aws.us_east_1
}
}
Key Components
- S3 Bucket: Configured for static website hosting
- Route53: DNS management and certificate validation
- ACM Certificate: SSL/TLS encryption
- CloudFront: Global CDN for fast content delivery
Step 3: Making It Modular
To make the infrastructure reusable, I created a Terraform module:
terraform-hugo-s3-module/
├── main.tf # Core resources
├── variables.tf # Input variables
├── outputs.tf # Output values
├── examples/basic/ # Usage example
└── tests/ # Module tests
Module Usage
module "hugo_website" {
source = "github.com/username/terraform-hugo-s3-module"
domain_name = "example.com"
aws_region = "eu-central-1"
providers = {
aws.us_east_1 = aws.us_east_1
}
}
Step 4: Deployment Process
1. Deploy Infrastructure
cd terraform
terraform init
terraform plan
terraform apply
2. Update DNS
Since the domain wasn’t previously in Route53, Terraform creates a new hosted zone. Check the nameservers in the AWS Console (Route53 > Hosted Zones) or via Terraform output:
terraform output nameservers
Update your domain registrar with these Route53 nameservers and wait for DNS propagation (24-48 hours).
3. Configure Hugo Deploy
Add deployment configuration to hugo.toml
:
[[deployment.targets]]
name = "aws"
URL = "s3://example.com?region=eu-central-1"
4. Deploy Hugo Site
hugo deploy
Step 5: Challenges and Solutions
Certificate Validation
The ACM certificate initially showed “pending validation.” This was resolved by:
- Ensuring the domain uses Route53 nameservers
- Waiting for DNS propagation (24-48 hours)
- Terraform automatically creates validation DNS records
Regional Considerations
- S3 bucket: Can be in any region (eu-central-1)
- ACM certificate: Must be in us-east-1 for CloudFront
- CloudFront: Global service
Benefits of This Approach
- Infrastructure as Code: Version-controlled, repeatable deployments
- Scalable: CloudFront provides global CDN
- Secure: Automatic SSL certificate management
- Cost-effective: S3 static hosting is very affordable
- Reusable: Terraform module can deploy multiple sites
Module Features
The final Terraform module includes:
- ✅ S3 static website hosting
- ✅ Route53 DNS management
- ✅ ACM SSL certificate with auto-validation
- ✅ CloudFront CDN distribution
- ✅ Optional www redirect
- ✅ Configurable tags and regions
- ✅ Comprehensive tests
Conclusion
This setup provides a robust, scalable solution for hosting Hugo sites on AWS. The Terraform module makes it easy to deploy multiple sites with consistent infrastructure.
The combination of Hugo’s fast static site generation and AWS’s reliable infrastructure creates an excellent foundation for modern websites.
Next Steps
- Set up automated deployments with GitHub Actions
- Add CloudFront cache invalidation
- Implement blue-green deployments
- Add monitoring and alerting
The complete Terraform module and example code are available in the project repository.