Skip to content
Latchkey

How to Deploy a Static Site to AWS S3 and CloudFront From CI

Sync the build to S3 with --delete, then invalidate CloudFront so the edge serves the new files.

Run aws s3 sync <dir> s3://<bucket> --delete to upload and prune, then aws cloudfront create-invalidation --paths "/*" to clear the edge cache. Prefer OIDC role assumption over long-lived keys.

Steps

  • Configure AWS credentials, ideally via aws-actions/configure-aws-credentials with OIDC.
  • Sync the build directory to the bucket with --delete.
  • Create a CloudFront invalidation for the changed paths.
  • Wait for the invalidation only if a later step depends on it.

Workflow

.github/workflows/ci.yml
jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/deploy
          aws-region: us-east-1
      - run: aws s3 sync dist s3://my-site-bucket --delete
      - run: >-
          aws cloudfront create-invalidation
          --distribution-id E123ABC --paths "/*"

Gotchas

  • Invalidating "/*" is billed after the first 1000 paths per month; scope paths when you can.
  • The IAM role needs both s3:PutObject/DeleteObject and cloudfront:CreateInvalidation.
  • Upload immutable fingerprinted assets first, then HTML, to avoid serving stale references.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →