Skip to main content

Static Websites Guide

Comprehensive guide to deploying static websites (React, Vite, Next.js export, Docusaurus) using carlin deploy static-app.

Overview

carlin simplifies static site deployment by automating S3 bucket creation, optional CloudFront distribution, DNS records, caching strategy, and clean URLs.

Supported Frameworks

FrameworkBuild CommandDefault Output Folder
Vite / Reactpnpm builddist/
Next.js (export)pnpm next build && pnpm next exportout/
Docusauruspnpm buildbuild/
Vue / Nuxt (static)pnpm builddist/

Quick Start (Vite)

pnpm build
carlin deploy static-app --cloudfront --aliases app.example.com --acm <CERT_ARN>

Directory Detection

If --build-folder is not provided, carlin searches for (in order):

build/
dist/
out/
.next/
public/

Specify explicitly:

carlin deploy static-app --build-folder dist

Clean URLs vs SPA Mode

ModeFlagBehavior
Clean URLs--append-index-html/docs/guide serves /docs/guide/index.html
SPA--spaAll 404 => index.html (client-side routing)

You can combine both when needed:

carlin deploy static-app --cloudfront --append-index-html --spa

Custom Domain + SSL

  1. Request certificate in us-east-1 (CloudFront required region).
  2. Validate certificate (DNS or email).
  3. Deploy with aliases:
carlin deploy static-app \
--cloudfront \
--aliases app.example.com www.app.example.com \
--acm arn:aws:acm:us-east-1:123456789012:certificate/abc123 \
--hosted-zone-name example.com

carlin:

  • Associates certificate with CloudFront
  • Creates Route 53 DNS A/AAAA records for each alias

Cache Strategy

Asset TypeSuggested TTLReason
HTML0–300sFrequent content updates
JS/CSS (hashed)1 yearImmutable with content hash
Images1 yearRarely change; use hash/versioned names
Fonts1 yearImmutable assets

Add hashed filenames (Vite example):

// vite.config.ts
export default {
build: {
rollupOptions: {
output: {
entryFileNames: 'assets/[name].[hash].js',
chunkFileNames: 'assets/[name].[hash].js',
assetFileNames: 'assets/[name].[hash].[ext]',
},
},
},
};

Advanced Deployment Patterns

Multi-Environment Sites

# Staging docs
carlin deploy static-app --environment staging --cloudfront --aliases staging.docs.example.com --acm <ARN>

# Production docs
carlin deploy static-app --environment production --cloudfront --aliases docs.example.com --acm <ARN>

Monorepo Multiple Apps

apps/
marketing/ (build => dist/)
dashboard/ (build => build/)
docs/ (build => build/)

Deploy each:

cd apps/marketing
pnpm build
carlin deploy static-app --cloudfront --aliases marketing.example.com --acm <ARN>

cd ../dashboard
pnpm build
carlin deploy static-app --cloudfront --aliases app.example.com --acm <ARN>

Incremental Updates (No File Upload)

Update CloudFront config only:

carlin deploy static-app --skip-upload --cloudfront --aliases app.example.com

Forced Re-Upload (Cache Bust)

Change build output hash or invalidate distribution manually:

aws cloudfront create-invalidation --distribution-id <ID> --paths '/*'

Security Best Practices

  • Enable --cloudfront for HTTPS everywhere
  • Use long, random S3 bucket names (automatic via carlin)
  • Restrict S3 public access except via CloudFront
  • Use OAC (Origin Access Control) for private S3 origin (future enhancement)
  • Avoid exposing /index.html directly when using clean URLs

Troubleshooting

ProblemCauseSolution
404 on nested docs pagesMissing --append-index-htmlRedeploy with --append-index-html
SPA routes broken (refresh)No SPA fallbackAdd --spa flag
Certificate validation failedWrong region or DNS not propagatedEnsure certificate in us-east-1, wait DNS propagation
Aliases ignoredMissing --acm with aliasesProvide both --aliases and --acm
High TTFB globallyNo CDNAdd --cloudfront
Stale assets after deployBrowser caching hashed assetsEnsure hashed filenames; invalidate only if necessary

Cost Overview

ComponentApprox Monthly (small site)
S3 Storage0.100.10–1
S3 Requests<$0.50
CloudFront22–10
DNS (Route 53)$0.50 per hosted zone + query costs
SSL (ACM)Free

Deployment Flow

Best Practices Summary

  • Prefer hashed asset filenames for long cache lifetimes
  • Use --append-index-html for static site generators with nested routes
  • Use --spa for single-page apps with client-side routing
  • Separate staging and production domains for safer iteration
  • Keep build output small (tree shaking, code splitting)