Deploy Astro on AWS with GitHub Actions

πŸš€ Astro ☁️ Amazon Web Services βš™οΈ GitHub Actions

Configuration Files

4 files
Production-ready configuration files with detailed comments and best practices. Each file works together as a complete deployment solution.
Astro configuration for static site generation
javascript
import { defineConfig } from 'astro/config';

// https://astro.build/config
export default defineConfig({
  output: 'static',
  build: {
    inlineStylesheets: 'auto',
  },
  // Optional: Add site URL for sitemap generation
  site: 'https://d1234567890.cloudfront.net',
  // Optional: Add base path if deploying to subdirectory
  // base: '/my-app',
});

Pro Tips

  • ⚑ output: "static" generates pure HTML/CSS/JS
  • 🎯 inlineStylesheets: "auto" optimizes CSS delivery
  • πŸ—ΊοΈ site: URL enables sitemap generation
  • πŸ’‘ Astro is perfect for content-heavy sites
  • πŸ“¦ Zero JavaScript by default (opt-in hydration)
  • πŸš€ Fastest static site generator (benchmarks)
AWS infrastructure for Astro static site
terraform
# Terraform for Astro on AWS S3 + CloudFront
terraform {
  required_version = ">= 1.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}

variable "aws_region" {
  default = "us-east-1"
}

variable "app_name" {
  default = "my-astro-site"
}

# S3 Bucket
resource "aws_s3_bucket" "website" {
  bucket = "${var.app_name}-website"
}

resource "aws_s3_bucket_website_configuration" "website" {
  bucket = aws_s3_bucket.website.id

  index_document {
    suffix = "index.html"
  }

  error_document {
    key = "404.html"
  }
}

resource "aws_s3_bucket_public_access_block" "website" {
  bucket = aws_s3_bucket.website.id

  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}

resource "aws_s3_bucket_policy" "website" {
  bucket = aws_s3_bucket.website.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "PublicReadGetObject"
        Effect    = "Allow"
        Principal = "*"
        Action    = "s3:GetObject"
        Resource  = "${aws_s3_bucket.website.arn}/*"
      }
    ]
  })

  depends_on = [aws_s3_bucket_public_access_block.website]
}

# CloudFront Distribution
resource "aws_cloudfront_distribution" "website" {
  enabled             = true
  is_ipv6_enabled     = true
  default_root_object = "index.html"
  price_class         = "PriceClass_100"

  origin {
    domain_name = aws_s3_bucket.website.bucket_regional_domain_name
    origin_id   = "S3-${var.app_name}"
  }

  default_cache_behavior {
    allowed_methods  = ["GET", "HEAD", "OPTIONS"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "S3-${var.app_name}"

    forwarded_values {
      query_string = false
      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
    compress               = true
  }

  custom_error_response {
    error_code         = 404
    response_code      = 404
    response_page_path = "/404.html"
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# Outputs
output "s3_bucket_name" {
  value = aws_s3_bucket.website.id
}

output "cloudfront_distribution_id" {
  value = aws_cloudfront_distribution.website.id
}

output "website_url" {
  value = "https://${aws_cloudfront_distribution.website.domain_name}"
}

Pro Tips

  • πŸ’° Costs ~$0.50-3/month for typical blog/docs site
  • 🌍 CloudFront: 450+ edge locations worldwide
  • ⚑ 99% cache hit rate = minimal S3 costs
  • πŸ”’ HTTPS included with CloudFront certificate
  • πŸ’‘ Add custom domain with Route 53 + ACM
  • πŸ“Š S3: $0.023/GB storage, CloudFront: $0.085/GB transfer
  • 🎯 Perfect for blogs, documentation, marketing sites
  • ⚠️ Run "terraform destroy" when done testing
Astro project dependencies and scripts
json
{
  "name": "my-astro-site",
  "type": "module",
  "version": "0.0.1",
  "scripts": {
    "dev": "astro dev",
    "start": "astro dev",
    "build": "astro check && astro build",
    "preview": "astro preview",
    "astro": "astro",
    "deploy": "npm run build && aws s3 sync dist/ s3://my-astro-site-website --delete"
  },
  "dependencies": {
    "astro": "^4.0.0"
  },
  "devDependencies": {
    "@astrojs/check": "^0.3.0",
    "typescript": "^5.3.0"
  }
}

Pro Tips

  • ⚑ "astro check" validates TypeScript before build
  • πŸš€ "deploy" script for manual deployments
  • πŸ’‘ Astro 4.0+ includes View Transitions API
  • πŸ“¦ Minimal dependencies = fast installs
  • 🎯 Update S3 bucket name in deploy script
  • πŸ”§ Use GitHub Actions for automated deployments
Environment variables for AWS deployment
bash
# AWS Configuration
AWS_REGION=us-east-1
S3_BUCKET=my-astro-site-website
CLOUDFRONT_DISTRIBUTION_ID=E1234567890ABC

# Public variables (available in Astro)
PUBLIC_API_URL=https://api.example.com
PUBLIC_SITE_URL=https://d1234567890.cloudfront.net
PUBLIC_GA_ID=G-XXXXXXXXXX

Pro Tips

  • πŸ”’ PUBLIC_ prefix makes variables available in Astro
  • ⚠️ All PUBLIC_ variables are exposed in client bundle
  • πŸ’‘ Access with import.meta.env.PUBLIC_API_URL
  • 🚫 Never commit .env to git (add to .gitignore)
  • πŸ“ Copy to .env for local development
  • 🎯 GitHub Actions uses repository secrets

Prerequisites

  • AWS account with billing enabled
  • AWS CLI installed and configured
  • Terraform installed (optional)
  • GitHub repository with admin access
  • Node.js 18+ installed

Deployment Steps

  • Create Astro project: npm create astro@latest
  • Run terraform apply to create S3 + CloudFront
  • Get S3 bucket name and CloudFront ID from outputs
  • Add AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY to GitHub Secrets
  • Add S3_BUCKET and CLOUDFRONT_DISTRIBUTION_ID to GitHub Secrets
  • Create .github/workflows/deploy.yml
  • Push to main branch to deploy
  • Access site at CloudFront URL

Additional Notes

  • πŸ’° Costs ~$0.50-3/month for typical sites
  • 🌍 Global CDN with 450+ edge locations
  • πŸ”’ Automatic HTTPS with CloudFront
  • ⚑ Astro = fastest static site generator
  • πŸ“¦ Zero JavaScript by default
  • πŸ’‘ Perfect for blogs, docs, marketing sites
  • 🎯 99% cache hit rate = minimal costs
  • πŸš€ Sub-second page loads globally