-
Notifications
You must be signed in to change notification settings - Fork 150
Cloudformation template #32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
What isn't working? |
Base version of Serverless/CloudFormation, without the Middleware Edge function: # Service name
service: next-app
# Ensure configuration validation issues fail the command (safest option)
configValidationMode: error
# Package individually as multiple lambdas created
package:
individually: true
# Define plugins
plugins:
- serverless-scriptable-plugin
- serverless-s3-sync
provider:
name: aws
region: us-west-2
# Use direct deployments (faster). This is going to become the default in v4.
# See https://www.serverless.com/framework/docs/providers/aws/guide/deploying#deployment-method
deploymentMethod: direct
# Ensure Lambdas can access Assets S3 Bucket
iam:
role:
statements:
- Effect: Allow
Action:
- "s3:GetObject"
Resource:
- "arn:aws:s3:::${self:service}-assets/*"
functions:
imageOptimization:
name: ${self:service}-image-optimization
description: Image Optimization Lambda for Next.js App
handler: index.handler
runtime: nodejs18.x
architecture: arm64
memorySize: 1024
# We need a Function URL to use for the CloudFront Origin URL
url: true
package:
artifact: .open-next/zips/image-optimization-function.zip
# Set S3 BUCKET_NAME for Image Optimization Lambda to use
environment:
BUCKET_NAME: ${self:service}-assets
server:
name: ${self:service}-server
description: Server Lambda for Next.js App
handler: index.handler
runtime: nodejs18.x
architecture: arm64
memorySize: 512
# We need a Function URL to use for the CloudFront Origin URL
url: true
package:
artifact: .open-next/zips/server-function.zip
custom:
scriptable:
hooks:
before:package:createDeploymentArtifacts:
- npx nx run build
- mkdir -p ./.open-next/zips
- cd .open-next/server-function && zip -r ../zips/server-function.zip .
- cd .open-next/image-optimization-function && zip -r ../zips/image-optimization-function.zip .
s3Sync:
- bucketName: ${self:service}-assets
localDir: .open-next/assets
params: # Cache control
# Un-hashed files, should be cached at the CDN level, but not at the browser level
- "**/*":
CacheControl: "public,max-age=0,s-maxage=31536000,must-revalidate"
# Hashed files, should be cached both at the CDN level and at the browser level
- "_next/**/*":
CacheControl: "public,max-age=31536000,immutable"
resources:
Description: Next App Infrastructure
Resources:
# S3 Bucket for assets
AssetsBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: ${self:service}-assets
# S3 Bucket Policy to allow access from CloudFront Origin Access Control (OAC)
AssetsBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref AssetsBucket
PolicyDocument:
Statement:
- Action: s3:GetObject
Effect: Allow
Resource: !Sub ${AssetsBucket.Arn}/*
Principal:
Service: cloudfront.amazonaws.com
Condition:
StringEquals:
AWS:SourceArn: !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Enabled: true
PriceClass: PriceClass_All
# List of origins. S3 Bucket, Server function, and Image Optimization function
Origins:
- Id: StaticAssetOrigin
DomainName: !GetAtt AssetsBucket.DomainName
S3OriginConfig:
OriginAccessIdentity: ""
OriginAccessControlId: !GetAtt CloudFrontAccessToS3Bucket.Id
- Id: ImageOptimizationFunctionOrigin
# Remove https:// from URL
DomainName: !Select [2, !Split ["/", !GetAtt ImageOptimizationLambdaFunctionUrl.FunctionUrl]]
CustomOriginConfig:
HTTPSPort: 443
OriginProtocolPolicy: https-only
- Id: ServerFunctionOrigin
# Remove https:// from URL
DomainName: !Select [2, !Split ["/", !GetAtt ServerLambdaFunctionUrl.FunctionUrl]]
CustomOriginConfig:
HTTPSPort: 443
OriginProtocolPolicy: https-only
# We need a "failover" Origin Group to try the "Server function" origin first, then fallback to the S3 bucket origin if the server function fails
OriginGroups:
Quantity: 1
Items:
- Id: ServerAndStaticAssetOriginGroup
FailoverCriteria:
StatusCodes:
Quantity: 2
# TODO: Not sure if these are the correct error codes to use...
Items:
- 500
- 502
Members:
Quantity: 2
Items:
- OriginId: ServerFunctionOrigin
- OriginId: StaticAssetOrigin
# Default is used by /* resources
DefaultCacheBehavior:
MinTTL: 0
DefaultTTL: 0
MaxTTL: 31536000
TargetOriginId: ServerAndStaticAssetOriginGroup
ViewerProtocolPolicy: redirect-to-https
AllowedMethods: ["GET", "HEAD", "OPTIONS"]
CachedMethods: ["HEAD", "GET"]
Compress: true
ForwardedValues:
QueryString: true
Headers:
- x-op-middleware-request-headers
- x-op-middleware-response-headers
- x-nextjs-data
- x-middleware-prefetch
Cookies:
Forward: all
CacheBehaviors:
- TargetOriginId: StaticAssetOrigin
ViewerProtocolPolicy: https-only
PathPattern: /_next/static/*
Compress: true
AllowedMethods: ["GET", "HEAD", "OPTIONS"]
CachedMethods: ["HEAD", "GET"]
ForwardedValues:
QueryString: false
- TargetOriginId: ServerFunctionOrigin
ViewerProtocolPolicy: https-only
PathPattern: /api/*
AllowedMethods:
["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"]
ForwardedValues:
QueryString: true
Cookies:
Forward: all
Headers: ["Authorization", "Host", "Accept-Language"]
- TargetOriginId: ImageOptimizationFunctionOrigin
ViewerProtocolPolicy: https-only
PathPattern: /_next/image
AllowedMethods:
["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"]
ForwardedValues:
QueryString: true
Headers: ["Accept"]
- TargetOriginId: ServerFunctionOrigin
ViewerProtocolPolicy: https-only
PathPattern: /_next/data/*
AllowedMethods: ["GET", "HEAD"]
ForwardedValues:
QueryString: true
Cookies:
Forward: all
Headers:
- x-op-middleware-request-headers
- x-op-middleware-response-headers
- x-nextjs-data
- x-middleware-prefetch
CloudFrontAccessToS3Bucket:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Name: CloudFrontAccessToS3BucketOriginAccess
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4 |
I'm in the process of moving this to Terraform, does anyone have a CloudFormation template that includes middleware? Still trying to understand how that works. The docs mention API Gateway, but I cannot find any information on that. Thanks for the template @teriu, super helpful! So, to add middleware, that function needs to be added as a viewer request lambda function association on the default and |
I'm getting Access Denied from cloudfront url after using this exact config. Is it working for everybody else? |
@mrunbanked have you tried going to a public asset such as I have it working on Terraform, and your template is almost identical, with the slight difference that
|
Ok seems like something was wrong with my package versions so server wasn't working properly. After updating all the packages it works! thanks 🙏 |
I am now using:
and it is deploying fine as far as I can see now. watch serverless/serverless#11424 you should not use the serverless dashboard. The app/org in the serverless.yml destroys the lambda... - |
This issue is referenced in the README as being open-next being compatible with Serverless Framework, but I'm a bit confused. Is @emulienfou's post a question or is it the answer? It's the only config file I see that references I understand the open-sourced-ness of this, so if the answer is I need to figure it out, that's fine. |
Hi @mbaquerizo I post a started template for Serverless Framework. |
I could make it work propperly on serverless framework with this configuration. Not sure about cache-related stuff, though service: next-app
useDotenv: true
configValidationMode: error
plugins:
- serverless-scriptable-plugin
- serverless-s3-sync
- serverless-prune-plugin
package:
individually: true
provider:
name: aws
runtime: nodejs20.x
stage: ${opt:stage, 'dev'}
region: 'sa-east-1'
profile: default
deploymentMethod: direct
environment:
STAGE: ${self:provider.stage}
# OPEN-NEXT
SHARP_VERSION: "0.32.6"
REVALIDATION_QUEUE_URL: !GetAtt CacheRevalidationQueue.QueueUrl
REVALIDATION_QUEUE_REGION: ${self:provider.region}
CACHE_DYNAMO_TABLE: !Ref CacheTable
CACHE_BUCKET_NAME: ${self:custom.cacheBucketName}
CACHE_BUCKET: ${self:custom.cacheBucketName}
# CACHE_BUCKET_KEY_PREFIX: cache
CACHE_BUCKET_REGION: ${self:provider.region}
BUCKET_NAME: ${self:custom.assetsBucketName}
# BUCKET_KEY_PREFIX: assets
# DYNAMO_BATCH_WRITE_COMMAND_CONCURRENCY: 5
# MAX_REVALIDATE_CONCURRENCY: 10
iam:
role:
statements:
- Effect: "Allow"
Action:
- s3:GetObject
- s3:PutObject
- s3:ListObjects
- dynamodb:PutItem
- dynamodb:Query
- sqs:SendMessage
- lambda:InvokeFunction
Resource: "*"
deploymentBucket:
name: ${self:provider.stage}-serverless
blockPublicAccess: true
endpointType: REGIONAL
apiGateway:
shouldStartNameWithService: true
binaryMediaTypes:
- "*/*"
custom:
schema: ${self:service}
aliasHostedZoneId: Z00000000000000000
assetsBucketName: ${self:service}-${self:provider.stage}-assets
cacheBucketName: ${self:service}-${self:provider.stage}-cache
prune:
automatic: true
number: 10
scriptable:
hooks:
after:deploy:finalize:
- result=$(aws lambda invoke --function-name ${self:service}-${self:provider.stage}-dynamodb-provider /dev/null) && echo "$result"
before:package:createDeploymentArtifacts:
# - SHARP_VERSION="0.32.6" OPEN_NEXT_DEBUG=true yarn open-next build --dangerously-disable-dynamodb-cache --dangerously-disable-incremental-cache
- SHARP_VERSION="0.32.6" yarn open-next build --minify
- mkdir -p ./.open-next/zips
- cd .open-next/server-function && zip -r ../zips/server-function.zip .
- cd .open-next/image-optimization-function && zip -r ../zips/image-optimization-function.zip .
- cd .open-next/revalidation-function && zip -r ../zips/revalidation-function.zip .
- cd .open-next/warmer-function && zip -r ../zips/warmer-function.zip .
s3Sync:
- bucketName: ${self:custom.assetsBucketName}
localDir: .open-next/assets
params:
- "**/*":
CacheControl: "public,max-age=0,s-maxage=31536000,must-revalidate"
- "_next/**/*":
CacheControl: "public,max-age=31536000,immutable"
functions:
server:
description: Default Lambda for Next CloudFront distribution
name: ${self:service}-${self:provider.stage}-server
handler: index.handler
runtime: nodejs20.x
architecture: arm64
memorySize: 512
timeout: 10
url: true
package:
artifact: .open-next/zips/server-function.zip
imageOptimization:
description: Image Lambda for Next CloudFront distribution
name: ${self:service}-${self:provider.stage}-image-optimization
handler: index.handler
runtime: nodejs20.x
architecture: arm64
memorySize: 512
timeout: 10
url: true
package:
artifact: .open-next/zips/image-optimization-function.zip
cacheRevalidation:
description: Cache Revalidation Lambda for Next CloudFront distribution
name: ${self:service}-${self:provider.stage}-cache-revalidation
handler: index.handler
runtime: nodejs20.x
architecture: arm64
memorySize: 512
timeout: 10
url: true
package:
artifact: .open-next/zips/revalidation-function.zip
events:
- sqs:
arn: !GetAtt CacheRevalidationQueue.Arn
warmer:
description: Lambdas warmer (cheap provisioned concurrency)
name: ${self:service}-${self:provider.stage}-warmer
handler: index.handler
runtime: nodejs20.x
architecture: arm64
memorySize: 512
timeout: 10
url: true
package:
artifact: .open-next/zips/warmer-function.zip
environment:
FUNCTION_NAME: ${self:service}-${self:provider.stage}-server
CONCURRENCY: 1
events:
- schedule:
rate: rate(5 minutes)
resources:
Resources:
CacheTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: ${self:service}-${self:provider.stage}-cache
AttributeDefinitions:
- AttributeName: tag
AttributeType: S
- AttributeName: path
AttributeType: S
- AttributeName: revalidatedAt
AttributeType: N
KeySchema:
- AttributeName: tag
KeyType: HASH
- AttributeName: path
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
GlobalSecondaryIndexes:
- IndexName: revalidate
KeySchema:
- AttributeName: path
KeyType: HASH
- AttributeName: revalidatedAt
KeyType: RANGE
Projection:
ProjectionType: ALL
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
CacheRevalidationQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: ${self:service}-${self:provider.stage}-cache-revalidation
CacheBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: ${self:custom.cacheBucketName}
AssetsBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: ${self:custom.assetsBucketName}
AssetsBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref AssetsBucket
PolicyDocument:
Statement:
- Action: s3:GetObject
Effect: Allow
Resource: !Sub ${AssetsBucket.Arn}/*
Principal:
Service: cloudfront.amazonaws.com
# Condition:
# StringEquals:
# AWS:SourceArn: !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/${DefaultDistribution}
DefaultDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Enabled: true
PriceClass: PriceClass_All
CustomErrorResponses:
- ErrorCode: "404"
ResponsePagePath: "/index.html"
ResponseCode: "200"
ErrorCachingMinTTL: "30"
ViewerCertificate:
AcmCertificateArn: arn:aws:acm:us-east-1:0000000000
MinimumProtocolVersion: TLSv1.2_2018
SslSupportMethod: sni-only
Aliases: ["next-app.com"]
Origins:
- Id: ServerFunctionOrigin
DomainName: !Select [2, !Split ["/", !GetAtt ServerLambdaFunctionUrl.FunctionUrl]]
CustomOriginConfig:
HTTPSPort: 443
OriginProtocolPolicy: https-only
- Id: StaticAssetOrigin
DomainName: !GetAtt AssetsBucket.RegionalDomainName
S3OriginConfig:
OriginAccessIdentity: ""
OriginAccessControlId: !GetAtt CloudFrontAccessToS3Bucket.Id
- Id: ImageOptimizationFunctionOrigin
DomainName: !Select [2, !Split ["/", !GetAtt ImageOptimizationLambdaFunctionUrl.FunctionUrl]]
CustomOriginConfig:
HTTPSPort: 443
OriginProtocolPolicy: https-only
OriginGroups:
Quantity: 1
Items:
- Id: ServerAndStaticAssetOriginGroup
FailoverCriteria:
StatusCodes:
Quantity: 2
Items:
- 500
- 502
Members:
Quantity: 2
Items:
- OriginId: ServerFunctionOrigin
- OriginId: StaticAssetOrigin
DefaultCacheBehavior:
MinTTL: 0
DefaultTTL: 0
MaxTTL: 31536000
TargetOriginId: ServerAndStaticAssetOriginGroup
ViewerProtocolPolicy: redirect-to-https
AllowedMethods: ["GET", "HEAD", "OPTIONS"]
CachedMethods: ["HEAD", "GET"]
Compress: true
ForwardedValues:
QueryString: true
Headers:
- x-op-middleware-request-headers
- x-op-middleware-response-headers
- x-nextjs-data
- x-middleware-prefetch
Cookies:
Forward: all
CacheBehaviors:
# public directory (bucket root)
- TargetOriginId: StaticAssetOrigin
ViewerProtocolPolicy: https-only
# Tip: Create only a few directories, or even just one inside "public" and just use /directory/* instead of what I did
PathPattern: "/*.*" # or /*.{jpg}
Compress: true
AllowedMethods: ["GET", "HEAD", "OPTIONS"]
CachedMethods: ["HEAD", "GET"]
ForwardedValues:
QueryString: false
- TargetOriginId: StaticAssetOrigin
ViewerProtocolPolicy: https-only
PathPattern: "/**/*.*" # or /**/*.{jpg}
Compress: true
AllowedMethods: ["GET", "HEAD", "OPTIONS"]
CachedMethods: ["HEAD", "GET"]
ForwardedValues:
QueryString: false
#
- TargetOriginId: StaticAssetOrigin
ViewerProtocolPolicy: https-only
PathPattern: /_next/static/*
Compress: true
AllowedMethods: ["GET", "HEAD", "OPTIONS"]
CachedMethods: ["HEAD", "GET"]
ForwardedValues:
QueryString: false
- TargetOriginId: ServerFunctionOrigin
ViewerProtocolPolicy: https-only
PathPattern: /api/*
AllowedMethods:
["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"]
ForwardedValues:
QueryString: true
Cookies:
Forward: all
Headers: ["Authorization", "Host", "Accept-Language"]
- TargetOriginId: ImageOptimizationFunctionOrigin
ViewerProtocolPolicy: https-only
PathPattern: /_next/image
AllowedMethods:
["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"]
ForwardedValues:
QueryString: true
Headers: ["Accept"]
- TargetOriginId: ServerFunctionOrigin
ViewerProtocolPolicy: https-only
PathPattern: /_next/data/*
AllowedMethods: ["GET", "HEAD"]
ForwardedValues:
QueryString: true
Cookies:
Forward: all
Headers:
- x-op-middleware-request-headers
- x-op-middleware-response-headers
- x-nextjs-data
- x-middleware-prefetch
CloudFrontAccessToS3Bucket:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Name: CloudFrontAccessToS3BucketOriginAccess
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
FrontPageDNSName:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneId: Z00000000000000000
Comment: ${self:provider.stage} Next APP DNS
Name:
- "next-app.com"
Type: CNAME
TTL: 360
ResourceRecords:
- !GetAtt DefaultDistribution.DomainName I've added the two missing lambdas (warmer and revalidation) to the mix and the missing variables, as per-doc. |
At the docs, there is a "Dynamo provider". In the .open-next directory there is a "dynamodb-provider" (it does not finishes with "-function"), but I thought it was not a lambda. It seems I was wrong, despite it isn't in this docs drawing:
|
@celso-alexandre Sorry for the delay, i totally missed this. |
Trying to deploy Nextjs 13 using open-next with Serverless/Cloudformation.
The text was updated successfully, but these errors were encountered: