Static Deploys
Static deploys upload your built site to S3 and serve it globally through CloudFront with Lambda@Edge handling subdomain-based routing.
Deploy Lifecycle
Each deploy moves through these states:
uploading → extracting → syncing → invalidating → live- Uploading — Client sends a tar.gz payload (max 100MB) to
POST /api/apps/:id/deploy. Returns 202 with a deploy ID. - Extracting — Server extracts the archive to
/tmp/{deploy-id}/. - Syncing — Files are uploaded to
s3://bucket/{app-id}/with multipart upload for files >5MB. Previous versions are preserved at_versions/{version}/. - Invalidating — A CloudFront cache invalidation is triggered. Deploy is marked
liveafter invalidation starts (it completes asynchronously).
Lambda@Edge SPA Routing
A Lambda@Edge function handles origin-request events on the CloudFront distribution:
- Extracts the subdomain from the
Hostheader (e.g.,my-appfrommy-app.indigo-hq.com) - Looks up the app in DynamoDB to determine if it’s static or SSR
- For static apps: rewrites the request URI to the correct S3 prefix (
/{app-id}/path) - SPA fallback: If the requested path doesn’t have a file extension, it rewrites to
/{app-id}/index.html - Unknown subdomains return a 404 response
The Lambda@Edge function must stay under the 1MB code limit — this is a hard AWS constraint.
S3 Bucket Structure
s3://hq-deploy-assets/ {app-id-1}/ index.html assets/ main.js style.css _versions/ v1/ v2/ {app-id-2}/ ...Each app gets its own prefix. Versioning is handled at the application level (not S3 versioning) so previous deploys can be rolled back by copying from _versions/.
Deploy History
GET /api/apps/:id/deploysReturns a list of all deploys for an app, ordered by creation date. Each record includes:
- Deploy ID, version number, status
- File count, total size
- Created timestamp, completed timestamp
- Who triggered the deploy (user ID)