How I Built a Static Website on AWS S3 with Terraform and GitHub Actions

Mubarak KhanMubarak Khan
3 min read

πŸ›  Why I Built It

I wanted to host a simple static website entirely on AWS, using infrastructure as code and automating deployment with GitHub Actions. This approach lets me manage the infrastructure reliably and deploy updates seamlessly. In this article, I’ll show you how I used Terraform to create an S3-backed static website, configured the bucket, and automated deployments via GitHub Actions.

πŸš€ What I Used

  • AWS S3: to host HTML/CSS files

  • Terraform: to automate deployment

  • PowerShell/CLI: for uploading files to S3

🧱 Step-by-Step Setup

  1. Update terraform.tfvars with your desired bucket name:

     bucket_name = "your.unique.bucket.name"
    
  2. Initialize Terraform:

     terraform init
    
  3. Review the execution plan:

     terraform plan
    
  4. Apply the changes:

     terraform apply
    
  5. After successful apply, Terraform outputs the website URL:

     website_url = http://your.unique.bucket.name.s3-website-<region>.amazonaws.com
    
  6. Open the URL in your browser to see your static website.

  7. (Optional) Set up GitHub Secrets in your repository for AWS credentials and region:

    • AWS_ACCESS_KEY_ID

    • AWS_SECRET_ACCESS_KEY

    • AWS_REGION

  8. Push your changes to the main branch to trigger GitHub Actions and deploy your site automatically.

GitHub Actions Workflow

The .github/workflows/deploy.yml file uses GitHub Actions to sync your site-content/ directory to the S3 bucket on every push to the main branch. It uses AWS credentials stored in GitHub Secrets and does not apply ACLs since the bucket policy handles permissions.

name: Deploy Static Website to S3

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Sync website content to S3
        run: aws s3 sync site-content/ s3://your.unique.bucket.name --delete

      - name: Verify deployment
        run: aws sts get-caller-identity

Project Structure

terraform-s3-static-site/
β”œβ”€β”€ main.tf               # S3 bucket and website config resources
β”œβ”€β”€ variables.tf          # Variable definitions
β”œβ”€β”€ outputs.tf            # Outputs the website URL
β”œβ”€β”€ terraform.tfvars      # Bucket name and other variable values
β”œβ”€β”€ site-content/         # Static website files (index.html, error.html, etc.)
β”œβ”€β”€ .github/
β”‚   └── workflows/
β”‚       └── deploy.yml    # GitHub Actions workflow for deployment

Notes

  • Ensure the bucket name is globally unique.

  • The URL generated will correspond directly to the specified bucket name.

  • The project sets force_destroy = true on the bucket, so the URL, bucket, and its contents will be deleted if you run terraform destroy.

  • Do not use --acl public-read with aws s3 sync if your bucket has ACLs disabled (best practice).

  • Use GitHub Secrets to securely store AWS credentials for CI/CD.

🌐 (Optional) Add Custom Domain + HTTPS

If using Route 53:

  • Create a CloudFront distribution

  • Use AWS Certificate Manager (ACM) for SSL

  • Point your domain's DNS to CloudFront

(I'll cover this in a separate post.)

🧩 Lessons Learned

  • S3 makes static site hosting dead simple

  • Don't forget the public read policy

  • HTTPS requires CloudFront and ACM

πŸ”— What’s Next

  • Add HTTPS and WAF via CloudFront

  • Integrate a contact form using Lambda + API Gateway

  • Fully automate with Terraform

Source Code

You can find the full Terraform project and GitHub Actions workflow on my GitHub repository.

πŸ’¬ Feedback?

If you're doing something similar or have questions, feel free to reach out or drop a comment.

License

MIT License

0
Subscribe to my newsletter

Read articles from Mubarak Khan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mubarak Khan
Mubarak Khan