Vercel is amazing and with a single command can deploy your entire site. However due to changes there pretty severe limits on the amount of serverless functions you can deploy. That limit on the free is 12. That seems fine until you realize that pages with getInitialProps
will be counted.
Serverless is not just the name of a movement towards a serverless architecture but Serverless is also a company https://www.serverless.com. They provide a way to deploy to any of the cloud providers without having to know all the ins-and-outs of each the cloud provider.
They created a Next.JS Serverless component which you can check out here https://github.com/serverless-nextjs/serverless-next.js . This will deploy your Next.js application to AWS Lambda@Edge.
Meaning with Lambda@Edge your site, content, and APIs will all be served as close to the requesting user as possible. All powered using CloudFront and Lambdas.
They do have full support yet but they support 95% of Next.js Feature set.
Before we jump into config lets focus on what should be capable. Typically when deploying you may need two or more environments. Staging, production, or maybe one off feature branches. In order to accomplish this we will need to do a little setup.
First in your Next.js App install yarn add @sls-next/serverless-component@1.17.0
or whatever the latest version is. Then in the root create a serverless.yml
and pop in the config below.
name: MyApplication nextApp: component: "@sls-next/serverless-component@1.17.0" inputs: domain: - ${env.SUB_DOMAIN} - ${env.DOMAIN} memory: 1024
Make sure you update the component
to match the version installed. The component takes various inputs, however if you just want to deploy and don't want to worry too much about config all we need to do is specify our SUB_DOMAIN
and DOMAIN
as environment variables when we deploy.
You will get a CloudFront URL back if you do not specify a domain but we're focusing on production usage. Also I upped the memory as the cost to time execution is worth it.
Ensure that any domain you use is configured in Route 53, otherwise AWS won't be able to generate certificates and set things up correctly.
If all you are doing is deploying a single domain then you could check in the .serverless
folder and everything would be great.
The caveat is that the you do need to do a little bit of manual work to support multiple domains. Serverless generates a .serverless
folder that stores all the generated resources. Like the CloudFront instance it needs to update, etc.
So if the .serverless
exists it would deploy and update the resources, however production and staging would be 2 separate resources.
To solve this issue the Serverless team recommends not checking in .serverless
directory to git and syncing the .serverless
folder to S3 based upon the environment.
So for example on first deploy you would run something like
aws s3 sync s3://my-bucket/my-repo-name/env-name/.serverless .serverless --delete
This reaches out and grabs the serverless
folder from a specific bucket and puts it where ever you ran the command from. Generally this should be at the root of the repo next your .serverless.yml
file.
After you deploy the .serverless
folder needs to be reuploaded to S3. To accomplish this we run the reverse command. We put the .serverless
folder first, and the S3 bucket destination second.
aws s3 sync .serverless s3://my-bucket/my-repo-name/env-name/.serverless --delete
This will sync back and replace the .serverless
folder on the remote S3 bucket.
The solution is outlined on their README . They link to a github actions workflow you can utilize. You will need to add your AWS_ACCESS_KEY_ID
and AWS_SECRET_ACCESS_KEY
into the Settings => Secret
section of your repository.
Additionally you will need to setup a deploy
command in your package.json
. The deploy command would just be "deploy": "serverless"
and you would need to ensure that serverless
is installed as a devDependency
. yarn add serverless --dev
will accomplish that if you didn't already have it installed.
# [https://gist.github.com/hadynz/b4e190e0ce10e5811cb462920a9c678f](https://gist.github.com/hadynz/b4e190e0ce10e5811cb462920a9c678f) name: Continuous deploy on: push: branches: [master] jobs: serverless-deploy: runs-on: ubuntu-latest env: AWS_ACCESS_KEY_ID: ${{secrets.AWS_ACCESS_KEY_ID}} AWS_SECRET_ACCESS_KEY: ${{secrets.AWS_SECRET_ACCESS_KEY}} DOMAIN: ${{secrets.PROD_DOMAIN}} SUB_DOMAIN: ${{secrets.PROD_SUB_DOMAIN}} steps: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 with: node-version: 12 - name: Install dependencies run: npm ci - name: Run tests run: npm test - name: Download `.serverless` state from S3 run: aws s3 sync s3://my-bucket/my-repo-name/env-name/.serverless .serverless --delete - name: Deploy to AWS run: npm run deploy - name: Upload `.serverless` state to S3 run: aws s3 sync .serverless s3://my-bucket/my-repo-name/env-name/.serverless --delete
This is a slightly tweaked You will need to replace my-bucket/my-repo-name/env-name/
with the path to the folder.
This however will need to be different for both staging/prod because it needs to know to pull and replace the correct .serverless
folder on S3.
There are two options
master
to your staging branch name and have 2 separate configs. The configs would have hardcoded buckets, as well as hard coded domains.S3_SLS_PROD_BUCKET
and S3_SLS_STAGING_BUCKET
and still have 2 configs but they aren't hard coded. Same with the domains you'd need to swap out PROD_DOMAIN
for a separate STAGING_DOMAIN
.One other option for the buckets is to use the BRANCH_NAME
as the storage path.
Github CI isn't as feature packaged as some other CIs but the concept still holds. You need to pull the .serverless
bucket stored in S3, deploy, then update the .serverless
folder after deploy for each specific environment.