How to deploy NextJS / NodeJS webserver on Azure Webapps Free plan
Are you trying to deploy a NextJS apps on Azure Webapps, everything is deploy on the server, but nothing is working ? You are probably missing some configuration files very specific to IIS.
You will need a custom server, so that IISnode can run it, yarn start will not work. So here a very basic custom server for NextJS.
<root>/server.js
1const { createServer } = require('http');2const { parse } = require('url');34const next = require('next');56const dev = process.env.NODE_ENV !== 'production';7const hostname = process.env.NEXT_HOST_NAME || 'localhost';8const port = process.env.PORT || 3000;9// when using middleware 'hostname' and 'port' must be provided below10const app = next({ dev, hostname, port });11const handle = app.getRequestHandler();1213app.prepare().then(() => {14createServer(async (req, res) => {15try {16// Be sure to pass 'true' as the second argument to 'url.parse'.17// This tells it to parse the query portion of the URL.18const parsedUrl = parse(req.url, true);19await handle(req, res, parsedUrl);20} catch (err) {21console.error('Error occurred handling', req.url, err);22res.statusCode = 500;23res.end('internal server error');24}25}).listen(port, (err) => {26if (err) throw err;27console.log(`> Ready on http://${hostname}:${port}`);28});29});
Since we are on IIS we need a web.config file. This file is very basic and can be generate by azure if you don't have it. It basically give IIS the information of which file should be start with node. You guess it our server.js
<root>/web.config
1<?xml version="1.0" encoding="utf-8"?>2<!--3This configuration file is required if iisnode is used to run node processes behind4IIS or IIS Express. For more information, visit:56https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config7-->89<configuration>10<system.webServer>11<!-- Visit http://blogs.msdn.com/b/windowsazure/archive/2013/11/14/introduction-to-websockets-on-windows-azure-web-sites.aspx for more information on WebSocket support -->12<webSocket enabled="false" />13<handlers>14<!-- Indicates that the server.js file is a node.js site to be handled by the iisnode module -->15<add name="iisnode" path="server.js" verb="*" modules="iisnode"/>16</handlers>17<rewrite>18<rules>19<!-- Do not interfere with requests for node-inspector debugging -->20<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">21<match url="^server.js\/debug[\/]?" />22</rule>2324<!-- First we consider whether the incoming URL matches a physical file in the /public folder -->25<rule name="StaticContent">26<action type="Rewrite" url="public{REQUEST_URI}"/>27</rule>2829<!-- All other URLs are mapped to the node.js site entry point -->30<rule name="DynamicContent">31<conditions>32<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>33</conditions>34<action type="Rewrite" url="server.js"/>35</rule>36</rules>37</rewrite>3839<!-- 'bin' directory has no special meaning in node.js and apps can be placed in it -->40<security>41<requestFiltering>42<hiddenSegments>43<remove segment="bin"/>44</hiddenSegments>45</requestFiltering>46</security>4748<!-- Make sure error responses are left untouched -->49<httpErrors existingResponse="PassThrough" />5051<!--52You can control how Node is hosted within IIS using the following options:53* watchedFiles: semi-colon separated list of files that will be watched for changes to restart the server54* node_env: will be propagated to node as NODE_ENV environment variable55* debuggingEnabled - controls whether the built-in debugger is enabled5657See https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config for a full list of options58-->59<!--<iisnode watchedFiles="web.config;*.js"/>-->60</system.webServer>61</configuration>
Finally we need to tell the server to install our npm package, (sending them along the deploy is bad pratice, since some package are OS specific)
The first file tell IIS that it should execute a custom script
<root>/.deployment
1[config]2command = deploy.cmd
The second file will check if we have node and npm and install our deps
<root>/deploy.cmd
1@if "%SCM_TRACE_LEVEL%" NEQ "4" @echo off23:: ----------------------4:: KUDU Deployment Script5:: Version: 0.1.116:: ----------------------78:: Prerequisites9:: -------------1011:: Verify node.js installed12where node 2>nul >nul13IF %ERRORLEVEL% NEQ 0 (14echo Missing node.js executable, please install node.js, if already installed make sure it can be reached from current environment.15goto error16)1718:: Setup19:: -----2021setlocal enabledelayedexpansion2223SET ARTIFACTS=%~dp0%..\\artifacts2425IF NOT DEFINED DEPLOYMENT_SOURCE (26SET DEPLOYMENT_SOURCE=%~dp0%.27)2829IF NOT DEFINED DEPLOYMENT_TARGET (30SET DEPLOYMENT_TARGET=%ARTIFACTS%\\wwwroot31)3233IF NOT DEFINED NEXT_MANIFEST_PATH (34SET NEXT_MANIFEST_PATH=%ARTIFACTS%\\manifest3536IF NOT DEFINED PREVIOUS_MANIFEST_PATH (37SET PREVIOUS_MANIFEST_PATH=%ARTIFACTS%\\manifest38)39)4041IF NOT DEFINED KUDU_SYNC_CMD (42:: Install kudu sync43echo Installing Kudu Sync44call npm install kudusync -g --silent45IF !ERRORLEVEL! NEQ 0 goto error4647:: Locally just running "kuduSync" would also work48SET KUDU_SYNC_CMD=%appdata%\\npm\\kuduSync.cmd49)50goto Deployment5152:: Utility Functions53:: -----------------5455:SelectNodeVersion5657IF DEFINED KUDU_SELECT_NODE_VERSION_CMD (58:: The following are done only on Windows Azure Websites environment59call %KUDU_SELECT_NODE_VERSION_CMD% "%DEPLOYMENT_SOURCE%" "%DEPLOYMENT_TARGET%" "%DEPLOYMENT_TEMP%"60IF !ERRORLEVEL! NEQ 0 goto error6162IF EXIST "%DEPLOYMENT_TEMP%\\__nodeVersion.tmp" (63SET /p NODE_EXE=<"%DEPLOYMENT_TEMP%\\__nodeVersion.tmp"64IF !ERRORLEVEL! NEQ 0 goto error65)6667IF EXIST "%DEPLOYMENT_TEMP%\\__npmVersion.tmp" (68SET /p NPM_JS_PATH=<"%DEPLOYMENT_TEMP%\\__npmVersion.tmp"69IF !ERRORLEVEL! NEQ 0 goto error70)7172IF NOT DEFINED NODE_EXE (73SET NODE_EXE=node74)7576SET NPM_CMD="!NODE_EXE!" "!NPM_JS_PATH!"77) ELSE (78SET NPM_CMD=npm79SET NODE_EXE=node80)8182goto :EOF8384::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::85:: Deployment86:: ----------8788:Deployment89echo Handling node.js deployment.9091:: 1. KuduSync92IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" (93call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_SOURCE%" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd"94IF !ERRORLEVEL! NEQ 0 goto error95)9697:: 2. Select node version98call :SelectNodeVersion99100:: 3. Install npm packages101IF EXIST "%DEPLOYMENT_TARGET%\\package.json" (102pushd "%DEPLOYMENT_TARGET%"103call :ExecuteCmd !NPM_CMD! install --production104IF !ERRORLEVEL! NEQ 0 goto error105popd106)107108::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::109110:: Post deployment stub111IF DEFINED POST_DEPLOYMENT_ACTION call "%POST_DEPLOYMENT_ACTION%"112IF !ERRORLEVEL! NEQ 0 goto error113114goto end115116:: Execute command routine that will echo out when error117:ExecuteCmd118setlocal119set _CMD_=%*120call %_CMD_%121if "%ERRORLEVEL%" NEQ "0" echo Failed exitCode=%ERRORLEVEL%, command=%_CMD_%122exit /b %ERRORLEVEL%123124:error125endlocal126echo An error has occurred during web site deployment.127call :exitSetErrorLevel128call :exitFromFunction 2>nul129130:exitSetErrorLevel131exit /b 1132133:exitFromFunction134()135136:end137endlocal138echo Finished successfully.
With these file add to your project you should now be able to have your NextJS website working on Azure Webapps Free plan