A complete workflow for deploying a static site (Jekyll, Hugo, or any build output) to AWS S3 with CloudFront CDN and HTTPS.
main or develop → automatic build and deployCreate .github/workflows/deploy.yml:
name: Build and Deploy to S3
on:
push:
branches: [main, develop]
workflow_dispatch:
permissions:
contents: read
id-token: write
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
- name: Build Jekyll site
run: bundle exec jekyll build
env:
JEKYLL_ENV: production
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: $
aws-region: us-east-1
- name: Sync to S3
run: aws s3 sync _site/ s3://YOUR-BUCKET-NAME --delete
- name: Invalidate CloudFront cache
run: |
aws cloudfront create-invalidation \
--distribution-id $ \
--paths "/*"
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1
Trust policy (trust-policy.json):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::YOUR-ACCOUNT-ID:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:YOUR-ORG/YOUR-REPO:*"
}
}
}
]
}
aws iam create-role \
--role-name github-deploy \
--assume-role-policy-document file://trust-policy.json
Deploy policy (deploy-policy.json):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "S3Deploy",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::YOUR-BUCKET-NAME",
"arn:aws:s3:::YOUR-BUCKET-NAME/*"
]
},
{
"Sid": "CloudFrontInvalidate",
"Effect": "Allow",
"Action": "cloudfront:CreateInvalidation",
"Resource": "arn:aws:cloudfront::YOUR-ACCOUNT-ID:distribution/YOUR-DISTRIBUTION-ID"
}
]
}
aws iam put-role-policy \
--role-name github-deploy \
--policy-name deploy-policy \
--policy-document file://deploy-policy.json
In your repo settings, add:
AWS_ROLE_ARN - the IAM role ARNCLOUDFRONT_DISTRIBUTION_ID - your distribution ID--delete flag on s3 sync removes files from S3 that aren’t in your build output