Sitecore Rendering Host on Azure Linux web app loading with error 500
Sitecore JSS Next.js app deployment on Azure Linux Web App
Introduction
I recently worked on a project that used Sitecore 10.3 XM
(Sitecore Experience Edge [XE
] for content delivery
), the Sitecore JavaScript Rendering SDK (JSS) for Next.js
front-end build, and the Sitecore Headless SXA
(Sitecore Experience Accelerator) for setting up the content tree. As you are aware, Next.js applications can be hosted using various services, depending on the prerendering form and your preference.
We decided to set up Sitecore Rendering Host on Azure Linux Web App Service instead of Vercel as the hosting choice for the SSG Next.JS project due to security concerns.
Deploying a modern Sitecore JSS Next.js app on a Azure Linux web app sounds like a recipe for success, but we greeted by a daunting 500 Internal Server error at start-up 😁. Fear not, fellow developers, this blog post chronicles my journey of troubleshooting and resolving start-up issues that led to Error 500 Internal Server on a setup of Sitecore Rendering Host which deployed (Next.js deployment) on Azure Linux Web App. 🔝
The Challenge
The problem started when our Next.js application on an Azure Linux Web App began to load with an error message indicating an internal 500 server. This error is a generic HTTP status code that indicates an unexpected condition was encountered.
When attempting to visit the Sitecore Rendering Host (front-end application built with the Sitecore JSS Next.js SDK), we were seeing the Application Error screen in the browser: 🔝
🕵️The Investigation
I looked through the server logs first in an attempt to identify the source of the issue. Server logs are a treasure trove of data and frequently reveal what's wrong with the system.
Sitecore Rendering Host (Front-end app)
In our instance, the problem was occurring during the web application's start-up, according to the Sitecore Rendering Host logs, which were confirmed by the following logs: 🔝
Require stack: - /home/site/wwwroot/node_modules/.bin/next at Module._resolveFilename (node:internal/modules/cjs/loader:939:15) at Module._load (node:internal/modules/cjs/loader:780:27) at Module.require (node:internal/modules/cjs/loader:1005:19) at require (node:internal/modules/cjs/helpers:102:18) at Object.<anonymous> (/home/site/wwwroot/node_modules/.bin/next:3:35) at Module._compile (node:internal/modules/cjs/loader:1105:14) at Module._extensions..js (node:internal/modules/cjs/loader:1159:10) at Module.load (node:internal/modules/cjs/loader:981:32) at Module._load (node:internal/modules/cjs/loader:827:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12) { code: 'MODULE_NOT_FOUND', requireStack: [ '/home/site/wwwroot/node_modules/.bin/next' ] }
We (with other team members) examined the Sitecore Rendering Host Next.js Code base package's (
package.json
) start-up command and details are: 🔝"scripts": { "bootstrap": "ts-node --project tsconfig.scripts.json scripts/bootstrap.ts && graphql-let", "build": "npm-run-all --serial bootstrap next:build", "graphql:update": "ts-node --project tsconfig.scripts.json ./scripts/fetch-graphql-introspection-data.ts", "install-pre-push-hook": "ts-node --project tsconfig.scripts.json ./scripts/install-pre-push-hook.ts", "jss": "jss", "lint": "eslint ./src/**/*.tsx ./src/**/*.ts ./scripts/**/*.ts", "next:build": "next build", "next:dev": "cross-env NODE_OPTIONS='--inspect' next dev", "next:start": "next start", "scaffold": "ts-node --project tsconfig.scripts.json scripts/scaffold-component/index.ts", "start": " next start –p 80", "start:connected": "npm-run-all --serial bootstrap --parallel next:dev start:watch-components", "start:disconnected-proxy": "ts-node --project tsconfig.scripts.json ./scripts/disconnected-mode-proxy.ts", "start:production": "npm-run-all --serial bootstrap next:build next:start", "start:watch-components": "ts-node --project tsconfig.scripts.json scripts/generate-component-builder/index.ts --watch" }
We (with other team members) checked the webroot (
wwwroot
) directory of the Azure Linux web app with Kudu (Development Tools -> Advanced Tools) to identify whether the node_modules/next/dist/bin/next and /node_modules/.bin/ folder exists or not.We checked the .env file (
\src\rendering\.env
) of the Sitecore Rendering Host project to identify theSITECORE_API_HOST
and other required configurations: 🔝# Your Sitecore API key is needed to build the app. Typically, the API key is # defined in `scjssconfig.json` (as `sitecore.apiKey`). This file may not exist # when building locally (if you've never run `jss setup`), or when building in a # higher environment (since `scjssconfig.json` is ignored from source control). # In this case, use this environment variable to provide the value at build time. SITECORE_API_KEY= # Your Sitecore API hostname is needed to build the app. Typically, the API host is # defined in `scjssconfig.json` (as `sitecore.layoutServiceHost`). This file may # not exist when building locally (if you've never run `jss setup`), or when building # in a higher environment (since `scjssconfig.json` is ignored from source control). # In this case, use this environment variable to provide the value at build time. SITECORE_API_HOST= # Your GraphQL Edge endpoint. This is required for Sitecore Experience Edge. # For Sitecore XM, this is typically optional. By default, the endpoint is calculated using # the resolved Sitecore API hostname + the `graphQLEndpointPath` defined in your `package.json`. GRAPH_QL_ENDPOINT=
We tried changing the start-up command of the Azure Linux web app to
/home/site/wwwroot/node_modules/next/dist/bin/next start -p 8080
Then it’s giving the below error: 🔝
ERROR - Container myapp_434amit for site myapp has exited, failing site start ERROR - Container myapp_434amit didn't respond to HTTP pings on port: 8080, failing site start. See container logs for debugging. INFO - Stopping site myapp because it failed during startup
💡The default port used by the Azure Linux web app service (container) is 8080, which it transfers to an external http/https (80/443) port.Using the following modifications made to the Sitecore Rendering Host code base's .env file (
\src\rendering\.env
), we attempted to build the application on an Agent PC (Win machine in build pipeline): 🔝# The public URL to use for absolute URLs, which are required when # the Next.js app is run within Sitecore editors. # This should match the `serverSideRenderingEngineApplicationUrl` # in your Sitecore configuration (see \sitecore\config\contosoproject.config). # Be sure to update these values accordingly as your public endpoint changes. # See https://jss.sitecore.com/docs/fundamentals/services/view-engine PUBLIC_URL=http://localhost:3000 # Your Sitecore API key is needed to build the app. Typically, the API key is # defined in `scjssconfig.json` (as `sitecore.apiKey`). This file may not exist # when building locally (if you've never run `jss setup`), or when building in a # higher environment (since `scjssconfig.json` is ignored from source control). # In this case, use this environment variable to provide the value at build time. SITECORE_API_KEY= # Your Sitecore API hostname is needed to build the app. Typically, the API host is # defined in `scjssconfig.json` (as `sitecore.layoutServiceHost`). This file may # not exist when building locally (if you've never run `jss setup`), or when building # in a higher environment (since `scjssconfig.json` is ignored from source control). # In this case, use this environment variable to provide the value at build time. SITECORE_API_HOST= # Your GraphQL Edge endpoint. This is required for Sitecore Experience Edge. # For Sitecore XM, this is typically optional. By default, the endpoint is calculated using # the resolved Sitecore API hostname + the `graphQLEndpointPath` defined in your `package.json`. GRAPH_QL_ENDPOINT= # The way in which layout and dictionary data is fetched from Sitecore FETCH_WITH=GraphQL # Indicates whether SSG `getStaticPaths` pre-render any pages # Set the environment variable DISABLE_SSG_FETCH=true # to enable full ISR (Incremental Static Regeneration) flow DISABLE_SSG_FETCH=0
The
GRAPH_QL_ENDPOINT
is set as the Sitecore Experience Edge (XE
) end point, andSITECORE_API_HOST
is empty. After that, execute the following commands on the Sitecore Rendering Host Code base root folder:npm run build npm run next:dev
The Sitecore Rendering Host app is accessible using
http://localhost:3000
and a static resource URL pointing tolocalhost:3000
. 🔝we attempted to build the application on an Agent PC (Win machine in build pipeline) with all PROD
environment variable configurations
▶️ PROD Build Locally (on Agent PC) ▶️ Deployed Build manually after deletion of.next
folder inwwwroot
of Azure Linux web app but still Sitecore Rendering Host not working and giving Application ErrorI also ran the
npm cache clean –force
command within the wwwroot directory of Sitecore Rendering Host on the Azure Linux web app, and then it worked again. 🔝
Sitecore CM Server (Back-end app)
- We checked the log file of the Sitecore CM role and saw the logs whenever
SITECORE_API_HOST
was set as theSitecore CMS Endpoint
.
⚡The Solution
Following a comprehensive analysis, I was able to determine that the root of the problem was a symlink. Specifically, the build was indeed present at /home/site/wwwroot/node_modules/next/dist/bin/next
, but the symlink was attempting to execute commands from the folder /home/site/wwwroot/node_modules/.bin/next
, for which there was no mapping. 🔝
What is a Symlink?
A source of trouble occasionally arises when you attempting to deploy a Next.js code base using the command mentioned in the package.json start script (e.g.., next start
) and in certain deployment it may actually fail with MODULE_NOT_FOUND error. 🔝
It appears that Azure is experiencing issues with the next start
command since symlinks are kept during the build process of the Sitecore JSS Next.js SDK-based application. node_modules/.bin
, which is symlinked to node_modules/next/dist/bin/next
, is used by Next.js (and other frameworks of a similar nature). The .bin
folder aids in enabling NPM to use various commands, such as next start
.
To resolve this issue, we went with the Microsoft recommendation at Configure Node.js apps - Azure App Service | Microsoft Learn to run the application using PM2 instead of using npm start
(or) node server.js
(or) next start
. 🔝
To implement PM2, we followed the steps mentioned on the javascript - unable to deploy next js to azure - Stack Overflow:
Add the
ecosystem.config.js
file to yourNext.JS
app with the following code: 🔝module.exports = { apps: [ { name: "myapp", script: "./node_modules/next/dist/bin/next", args: "start -p " + (process.env.PORT || 3000), watch: false, autorestart: true, }, ], };
Change the start-up command for your Azure app to be this:
pm2 --no-daemon start /home/site/wwwroot/ecosystem.config.js
The deployed code will immediately launch PM2 as soon as it locates the ecosystem.config.js file during deployment, in accordance with the Microsoft instructions previously specified. 🔝
The container automatically starts your app with PM2 when one of the common Node.js files is found in your project:
bin/www
server.js
app.js
index.js
hostingstart.js
(or) One of the following PM2 files: process.json or ecosystem.config.js
Once the configurations were corrected, I restarted the Sitecore Rendering host app. To my relief, the Sitecore Rendering host app loaded successfully without any error 500
. It was a moment of triumph, but more importantly, it was a learning experience that reinforced the importance of meticulous troubleshooting and the value of understanding the technologies we work with.
I hope that sharing my experience will help others who might be facing similar challenges. Remember, every error is a step closer to the solution. Happy debugging! 🔝