This guide explains how cdk-nextjs automatically manages and cleans up cache data and static assets across deployments to optimize costs and prevent storage bloat.
cdk-nextjs implements automatic pruning mechanisms to clean up old cache data and static assets after each deployment. This prevents unlimited storage growth while maintaining deployment isolation and safety during rollouts.
Every deployment gets a unique BUILD_ID that isolates cache data:
- S3 Cache Keys:
/{buildId}/{cacheKey} - DynamoDB Revalidation: Partition key is
buildId, sort key is{tag}#{cacheKey} - Static Assets: Metadata includes
next-build-id: {buildId}
After each successful deployment, old cache data is cleaned up:
// Remove objects with old BUILD_ID prefixes
// Pattern: /{oldBuildId}/* → DELETE
// Keep: /{currentBuildId}/* → RETAIN// 1. Read metadata entry (pk="METADATA", sk="CURRENT_BUILD") to get previous buildId
// 2. Query all entries for previous buildId (efficient partition query)
// 3. Batch delete old entries
// 4. Update metadata with current buildId
// Cost: Only reads previous build's partition, not full table scan// Remove objects older than msTtl (default: 30 days)
// with different next-build-id metadata
// Prevents version skew while allowing gradual cleanup- Cost Optimization: Removes unused cache data
- Deployment Safety: Isolates deployments during rollout
- Version Skew Protection: Keeps recent static assets for users with stale tabs
- Storage Management: Prevents unlimited cache growth
- Identify Current BUILD_ID: Extract from deployment metadata
- List S3 Objects: Scan cache bucket for objects with old BUILD_ID prefixes
- Batch Delete: Remove objects in batches of 1000 (S3 limit)
- Preserve Current: Keep all objects matching current BUILD_ID
- Log Results: Report pruned object count and storage freed
- Query by Metadata: Find objects with
next-build-idmetadata - Check Age: Compare object creation time against TTL (default: 30 days)
- Version Check: Only delete if BUILD_ID differs from current
- Gradual Cleanup: Allows time for users with cached pages to transition
- Preserve Recent: Keep recent assets even if BUILD_ID differs
- Read Metadata: Get previous BUILD_ID from metadata entry (pk="METADATA", sk="CURRENT_BUILD")
- Query Previous Build: Efficiently query all items where pk=previousBuildId (single partition query)
- Batch Delete: Remove items in batches of 25 (DynamoDB limit)
- Update Metadata: Store current BUILD_ID in metadata entry for next deployment
- Log Results: Report pruned item count
Cost Efficiency: Uses Query instead of Scan, only reading items from previous build's partition. Cost scales with previous deployment size, not total table size.
// Automatic pruning enabled by default
new NextjsGlobalFunctions(this, "NextjsApp", {
buildDirectory: "./my-nextjs-app",
// Pruning happens automatically via NextjsPostDeploy
});The TTL (Time To Live) for static assets is configurable via the msTtl property in custom resource properties:
new NextjsGlobalFunctions(this, "NextjsApp", {
buildDirectory: "./my-nextjs-app",
overrides: {
nextjsPostDeploy: {
customResourceProperties: {
msTtl: (7 * 24 * 60 * 60 * 1000).toString(), // 7 days (must be string)
},
},
},
});Note: The msTtl value must be a string due to CloudFormation Custom Resource limitations. The default is 30 days: (1000 * 60 * 60 * 24 * 30).toString().
- BUILD_ID Prefixing: Ensures current deployment data is never deleted
- Atomic Operations: Pruning only starts after successful deployment
- Rollback Protection: Previous deployment data preserved during rollout window
- Partial Failures: Continue pruning even if some objects fail to delete
- Retry Logic: Automatic retries for transient failures
- Logging: Detailed logs for troubleshooting pruning issues
- Graceful Degradation: Application continues working if pruning fails
Pruning operations are logged with detailed information when debug mode is enabled:
Found 1234 cache objects with old BUILD_ID prefixes to delete
Deleting cache objects: ["/old-build-123/index.html", "/old-build-123/about.rsc"] from my-cache-bucket
Deleted 1234 cache objects from my-cache-bucket
Cache bucket pruning complete. Deleted 1234 objects from my-cache-bucket
Checking old objects metadata to determine pruning: ["/static/js/main.js", "/static/css/app.css"]
Deleting objects: ["/static/js/main.js", "/static/css/app.css"] from my-static-assets-bucket
Deleted 56 objects from my-static-assets-bucket
Pruning complete. Deleted 56 objects from my-static-assets-bucket
Pruning revalidation entries for previous build: old-build-123
Found 789 revalidation entries to delete for build old-build-123
Deleting revalidation entries: user-profile#api-users-123, ... from my-revalidation-table
Deleted 789 revalidation entries from my-revalidation-table
Updated metadata with current build ID: new-build-456
Revalidation table pruning complete. Deleted 789 entries from my-revalidation-table
Note: Debug logging is enabled by default but can be disabled by setting debug: false in NextjsPostDeploy props.
- Cache Bucket: Prevents unlimited growth of cache data
- Static Assets: Removes old build artifacts after TTL
- DynamoDB: Keeps revalidation table size manageable
- Fewer Objects: Reduces S3 LIST operation costs
- Smaller Scans: DynamoDB scans process fewer items
- Batch Operations: Efficient bulk delete operations
For a typical application with daily deployments, pruning prevents unlimited storage growth:
- Without Pruning: Cache data accumulates indefinitely
- With Pruning: Cache data is cleaned up after each deployment
- Storage Savings: Varies based on application cache usage patterns
The actual cost savings depend on your application's caching patterns, deployment frequency, and data volume.
- Check NextjsPostDeploy Lambda function logs
- Verify Lambda has S3 and DynamoDB permissions
- Ensure deployment completed successfully
- Check for environment variable configuration
- Review CloudWatch logs for partial failures
- Check S3 bucket permissions for delete operations
- Verify DynamoDB table permissions
- Monitor for throttling or rate limiting
- Monitor pruning duration in CloudWatch
- Check for large numbers of objects to prune
- Consider adjusting batch sizes for large datasets
- Review Lambda timeout settings
- Verify BUILD_ID is changing between deployments
- Check that pruning is not disabled
- Monitor for failed pruning operations
- Review static assets TTL configuration
- Monitor First Deploy: Watch pruning logs on initial deployment
- Gradual Rollout: Test pruning with staging environments first
- Backup Strategy: Consider S3 versioning for critical data
- Regular Monitoring: Set up alarms for pruning failures
- Batch Size Tuning: Adjust for your object count patterns
- Concurrent Operations: Prune different resources in parallel
- Timing Optimization: Run pruning during low-traffic periods
- Resource Limits: Monitor Lambda memory and timeout settings
- TTL Tuning: Balance safety vs storage costs
- Monitoring Setup: Track storage usage trends
- Regular Reviews: Periodically audit pruning effectiveness
- Lifecycle Policies: Consider S3 lifecycle rules as backup
This comprehensive pruning system ensures your Next.js application maintains optimal performance and cost efficiency across all deployments while providing safety mechanisms to prevent data loss.