In this article, I’ll describe Next.js specifics when it comes to deploying and hosting, introduce the four most common steps in publishing a Jamstack website, and use them to compare the four most popular hosting providers.
Next.js specifics in deployment
If you want to leverage this benefit, it complicates the deployment a bit. There are server-side rendered pages, and for those, we need a true server, not just a static file serving CDN.
Here are three ways to host Next.js sites:
- True pre-generated static files on a CDN without server-side capabilities
Examples: Azure Static Web Apps, AWS Amplify, GitHub Pages
- Node.js hosting that uses Next.js’s production server
Examples: Heroku, MS Azure, Digital Ocean
- Partly pre-generated static files and partly server-side capabilities that are hosted as serverless functions
Examples: Vercel, Netlify, Layer0
I personally prefer the last one as it allows the static files to be provided via CDN, requires less configuration, and allows each code block to be scaled separately.
It’s important to note here that not every host supports all Next.js modes. The majority of them support only true pre-generated sites and don’t offer a solution for server-side code, and thus don’t support the greatest Next.js features—incremental static regeneration, instant previews, and anything else that can’t work on the client.
I picked four providers that are capable of fully running Next.js—Vercel, Netlify, Heroku, and Layer0. To compare them, I used my own site built with Next.js that contains pages using all three page modes:
- Client-side code that composes pages using JS bundle in the browser (Blog page and filter)
- A pre-generated page that is composed during the site build (home page)
- A page that requires data from server-running logic (Twitter page)
And I checked these six aspects:
- Deploying the site from GitHub
- Automatically rebuilding the site on code push
- Automatically rebuilding the site on content publish
- Verifying performance
- Extra features
- Pricing and free tier limitations
#1 Deploy the site from GitHub
The first use case is to set up the site and deploy the code for the first time.
Vercel is the easiest way to deploy Next.js as it’s the people behind the platform who created Next.js in the first place.
The wizard asks us to select a GitHub repository and offers the standard Next.js build commands that we can override. It also lets us define all environment keys on this screen before deploying the site for the first time.
Netlify is probably the most popular host among devs, and I believe the simple, intuitive, and fast UI plays a role in that. In the new site wizard, you can expand “advanced settings” and configure the build commands or add environment keys.
Note that Netlify recognized that we’re creating a Next.js site and automatically installs the Essential Next.js Build Plugin, which deploys server-side code as Netlify Functions. The default settings work well.
On Heroku, we first need to create a site. Only after that can we access the dashboard and connect the site to the GitHub repo.
It lets us configure automatic deployments, but the UI is not 100% clear. To configure the environment variables, we need to navigate to the Settings tab and Config Vars section.
By default, Heroku will build the app and run the
next start command to start the Next.js production server. It does not convert anything to serverless functions—Heroku is a Node.js hosting service. The problem is that Heroku uses a random port, and Next.js is configured to use 3000. So we need to adjust the port on which Next.js server runs by adding a special file called Procfile (the filename is case-sensitive):
web: next start -p $PORT
All the previously mentioned hosting providers also feature build tools, which means that they take the code from a GitHub repo, build it, deploy it, and host it. Layer0 does not offer a way to build your code after receiving a webhook notification from GitHub, so you either need to build & deploy manually or leverage a CI tool like GitHub Actions.
The deployment is done in these two steps, and Layer0 will automatically create an app for you. Then you can adjust its settings and add environmental variables.
Note: Layer0 installs a bunch of packages (as dev dependencies) into your site that are related to some extra features mentioned later in this article
The UI is not ideal, and I needed to check logs and google the Procfile solution after getting a 503 error.
You need to configure a CI process to get the site up and running. The UI, despite being very detailed, is nice and intuitive.
#2 Automatic build on push
We make a code change, commit, push to the GitHub repo. GitHub creates a webhook notification to the build server, which builds the site and deploys it to production.
We need a GitHub Action or another CI tool for this task. However, Layer0 offers a template in their docs.
#3 Automatic build on content publish
Content changes trigger a webhook notification to the build server, so the scenario is very similar to automatic build on push. We need a URL, a target of our content change webhook notifications.
We can create a deploy hook under Settings and Git.
Find the “Build hooks” section under Site settings, Build&Deploy, and Continuous Deployment.
Generating a unique URL for these notifications is not supported. You can work around this limitation by mocking a GitHub push notification via a special API as described by Matteus Deloge:
You will also need to fetch the application ID either by CLI:
heroku apps:info --json
Or by another API call:
Both requests need to have an Authorization header with an API Key you can find under Account settings:
The other option is to move your build process to another tool like GitHub Actions with better capabilities for triggering builds.
The platform does not offer to host the build process. You need to move it to a tool like GitHub Actions and handle the webhook notifications there.
Triggering builds via external webhook is not natively supported.
Layer0 does not offer build server capabilities but offers a template for GitHub Actions in their docs.
Note: You can also keep your site up to date using incremental static regeneration as described here. That way, you won’t need to configure and use webhooks anymore.
#4 Verify performance
In this category, I evaluated both the build process and the front-end performance. I used web.dev test on all four deployed sites, and apart from learning that I should make some adjustments in the implementation, for each host, I recorded two metrics that are related to the actual hosting server:
- First contentful paint
That is, how long it takes to see the first content appear on the blank browser canvas.
- Largest contentful paint
The render time of the largest image or text block visible within the viewport.
I conducted three tests focused on each metric (except the first build time) and calculated their average values.
|First build+deploy time||49.3s||1m 57s||1m 20s||3m 37s*|
|Subsequent build+deploy time||42.6s||1m 37s||1m 15s||1m 51s*|
|Pre-generated page FCP||2.4s||2.3s||3.0s||2.7s|
|Pre-generated page LCP||4.4s||4.3s||6.0s||4.1s|
|SSR page FCP||2.4s||2.3s||2.6s||2.3s|
|SSR page LCP||4.4s||4.6s||5.9s||4.5s|
* Layer0 deployment process builds the site on your local machine and only then deploys it, so these figures were affected by my computer’s power and network connection. They can be different for deployments from CI tools.
Note: Pre-generated page and SSR page have different content, so only results in the same row are comparable.
* Update 2021-06-07: The pre-generated page used for testing contained assets linked to our CDN. I repeated the speed tests on the website homepage and updated the table.
#5 Extra features
Each of these platforms provides some additional features to help us deal with optimizing the site or advanced scenarios. In this section, I only outline the features that stand out for developers.
The site is running Next v10, so we can fully leverage Vercel’s Analytics. Instead of measuring web vitals using a tool such as web.dev, Vercel gives us insight into the actual visitor’s experience. It collects data from the deployed site and builds a summary that we can filter by device and time. Analytics are available for every page of the site, so it’s possible to quickly identify a bad performing page.
Netlify also provides Analytics, but not by collecting data from visitors. They collect data server-side. To enable the feature, you need to upgrade to a paid plan.
Netlify offers multiple extra features that are designed to simplify Jamstack (and beyond) sites such as Identity management, A/B testing, and so on. While you may not need these enhancements on all your sites, you almost always need to manage form submissions. The Forms management on Netlify allows a quick integration:
<form name="contact" method="POST" data-netlify="true">
And it also collects all submissions in the same UI of Netlify administration.
It supports email/webhook/Slack notifications, reCaptcha, and file uploads.
The deployed site on Heroku runs as a Node.js app, so the platform offers tools for monitoring the server metrics such as memory usage, throughput, response time, and so on. You can also configure autoscaling to handle traffic peaks.
When hosting the Next.js site on Heroku, you’re one level higher in terms of abstraction. Heroku doesn’t support Next.js directly—it gives you the tools (Node.js server) to host it. Heroku offers a gallery of server-level add-ons for many use cases and platforms, but none of them are specific to Next.js or Jamstack. However, for some projects, this freedom may be a benefit.
In the deployment section, I complained that Layer0 adds some dev NPM packages to the site. Here I learned why. The deployed site displays dev tools that show network stats for every visited page. You see how long it took to load every asset the page needs, where the file was served from. In addition to that, no requests happen once the bundle is loaded and moving between pages is handled locally. Very cool.
Apart from that, Layer0 can track the core web vitals of your site as long as you put a tracking script in the header of all pages. It also supports multiple site environments, which simplifies the setup if you need a preview version of your site.
#6 Check pricing and free tier limitations
All platforms provide a free plan. This table shows the features provided by each host in its scope and tells you when you’re going to need to start paying.
|Custom domain||yes||yes||yes, but only without SSL||yes|
|Serverless functions||12/deployment||125k executions/month||not applicable||100 hours/month|
|Serverless function timeout||10s||10s||30s timeout for any request||20s|
|300 min/month||unlimited||not applicable|
|Always on||yes||yes||sleeps after 30min||yes|
The free plans are comparable, and it depends on your project which limit might become a bottleneck.
So, which one is the best? As usual, there’s no silver bullet, and the choice depends on the project and the developer. The test cases were focused on the simplicity of deployment and basic tasks a developer needs to do to get a Next.js site live. If a host received fewer points in this test, it doesn’t mean they’re a bad service and may excel in another aspect that was not measured. The different possibilities of each platform and the requirements of a specific project may shuffle the results and point you in another direction.
I am a fan of hosting the site on CDN with the help of serverless functions. It requires very little maintenance and is easily and granularly scalable. I also like it when the hosting platform takes care of the build automatically without configuration and is capable of triggering it in case of code and content changes. That helps me stay in the code rather than become a DevOps expert 😊.
Tell me where you host your sites and why on our Discord 😎.