title: dotnet core + Angular Universal + docker => Prerendering failed because of error: Error: Cannot find module
body: <p>We have a project developed using Dotnet core, Angular Universal. We use the prerender feature of Asp.net Core SPA services. In the local environment, the application works both on Windows, Linux. There some problems when using Visual Studio's own docker multi-stage build template. After we build it in microsoft/ aspnetcore-build image, we transfer the files to microsoft/aspnetcore image. Because of the dependency of Asp.net SPA's to nodejs, we also install nodejs to microsoft/aspnetcore image.</p>
  
  <p>But when we run the application,  we are getting the error "Prerendering failed because of error: Error: Can not find module 'angular2-universal-polyfills'" at the prerender stage of main.bundle.js (server-side). We are confident that the problem is not related to 'angular2-universal-polyfills' because we have also tried other polyfills and got similar errors.</p>
  
  <p>But when we install both .net core and nodejs into a single container instead of multi-stage build and do build and publish operations inside the same container, our application works. But then the container is 2 GB in size.</p>
  
  <p>Theoretically, there is no difference between the container we build and the container we run. Both have both .net core and nodejs installed. The only difference is we copy publish output to the container that we run.</p>
  
  <p>Publish output</p>
  
  <pre><code>root
      wwwroot (angular client-side dist)
          assets
          inline.bundle.js
          main.bundle.js
          polyfill.bundle.js
          vendor.bundle.js
      CodefictionApp/dist-server (angular server-side dist)
          assets
          main.bundle.js
      dlls
  </code></pre>
  
  <hr>
  
  <p><strong>Working Dockerfile</strong></p>
  
  <pre><code>FROM microsoft/aspnetcore:2.0 AS base
  WORKDIR /app
  EXPOSE 80
  
  FROM buildpack-deps:jessie-scm
  
  RUN apt-get update \
      &amp;&amp; apt-get install -y --no-install-recommends \
          libc6 \
          libcurl3 \
          libgcc1 \
          libgssapi-krb5-2 \
          libicu52 \
          liblttng-ust0 \
          libssl1.0.0 \
          libstdc++6 \
          libunwind8 \
          libuuid1 \
          zlib1g \
      &amp;&amp; rm -rf /var/lib/apt/lists/*
  
  ENV DOTNET_SDK_VERSION 2.1.200
  ENV DOTNET_SDK_DOWNLOAD_URL https://dotnetcli.blob.core.windows.net/dotnet/Sdk/$DOTNET_SDK_VERSION/dotnet-sdk-$DOTNET_SDK_VERSION-linux-x64.tar.gz
  ENV DOTNET_SDK_DOWNLOAD_SHA c1b07ce8849619ca505aafd2983bcdd7141536ccae243d4249b0c9665daf107e03a696ad5f1d95560142cd841a0888bbf5f1a8ff77d3bdc3696b5873481f0998
  
  RUN curl -SL $DOTNET_SDK_DOWNLOAD_URL --output dotnet.tar.gz \
      &amp;&amp; echo "$DOTNET_SDK_DOWNLOAD_SHA dotnet.tar.gz" | sha512sum -c - \
      &amp;&amp; mkdir -p /usr/share/dotnet \
      &amp;&amp; tar -zxf dotnet.tar.gz -C /usr/share/dotnet \
      &amp;&amp; rm dotnet.tar.gz \
      &amp;&amp; ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet
  
  ENV DOTNET_RUNNING_IN_CONTAINER=true \
      DOTNET_USE_POLLING_FILE_WATCHER=true \
      NUGET_XMLDOC_MODE=skip
  
  RUN mkdir warmup \
      &amp;&amp; cd warmup \
      &amp;&amp; dotnet new \
      &amp;&amp; cd .. \
      &amp;&amp; rm -rf warmup \
      &amp;&amp; rm -rf /tmp/NuGetScratch
  
  ENV RestoreUseSkipNonexistentTargets false
  
  ENV NODE_VERSION="8.11.0"
  
  RUN apt-get install wget -y
  
  RUN set -ex \
      &amp;&amp; for key in \
        94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \
        0034A06D9D9B0064CE8ADF6BF1747F4AD2306D93 \
        FD3A5288F042B6850C66B31F09FE44734EB7990E \
        71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \
        DD8F2338BAE7501E3DD5AC78C273792F7D83545D \
        B9AE9905FFD7803F25714661B63B535A4C206CA9 \
        C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \
        77984A986EBC2AA786BC0F66B01FBB92821C587A \
        56730D5401028683275BD23C23EFEFE93C4CFFFE \
      ; do \
        gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \
      done
  
  RUN set -ex \
      &amp;&amp; wget "https://nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.gz" -O node-v$NODE_VERSION-linux-x64.tar.gz \
      &amp;&amp; wget "https://nodejs.org/download/release/v$NODE_VERSION/SHASUMS256.txt.asc" -O SHASUMS256.txt.asc \
      &amp;&amp; gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
      &amp;&amp; grep " node-v$NODE_VERSION-linux-x64.tar.gz\$" SHASUMS256.txt | sha256sum -c - \
          &amp;&amp; tar -xzf "node-v$NODE_VERSION-linux-x64.tar.gz" -C /usr/local --strip-components=1 \
          &amp;&amp; rm "node-v$NODE_VERSION-linux-x64.tar.gz" SHASUMS256.txt.asc SHASUMS256.txt \
          &amp;&amp; ln -s /usr/local/bin/node /usr/local/bin/nodejs \
          &amp;&amp; rm -fr /var/lib/apt/lists/* /tmp/* /var/tmp/*
  
  RUN npm config set user 0
  RUN npm config set unsafe-perm true
  
  RUN npm install -g @angular/cli@1.7.4
  RUN node -v &amp;&amp; npm -v &amp;&amp; ng -v
  
  RUN apt-get update &amp;&amp; apt-get install bzip2 -y
  
  ENV ASPNETCORE_URLS http://+:80
  
  WORKDIR /app
  EXPOSE 80
  COPY . .
  RUN dotnet restore ./CodefictionApi
  RUN dotnet publish ./CodefictionApi -c Release
  
  WORKDIR /app/CodefictionApi/bin/Release/netcoreapp2.0/publish
  
  ENTRYPOINT ["dotnet", "CodefictionApi.dll"]
  </code></pre>
  
  <p><strong>Multi-stage docker file (whic gives prerender error when its run)</strong></p>
  
  <pre><code>FROM microsoft/aspnetcore:2.0 AS base
  WORKDIR /app
  EXPOSE 80
  
  FROM buildpack-deps:jessie-scm AS publisher
  
  RUN apt-get update \
      &amp;&amp; apt-get install -y --no-install-recommends \
          libc6 \
          libcurl3 \
          libgcc1 \
          libgssapi-krb5-2 \
          libicu52 \
          liblttng-ust0 \
          libssl1.0.0 \
          libstdc++6 \
          libunwind8 \
          libuuid1 \
          zlib1g \
      &amp;&amp; rm -rf /var/lib/apt/lists/*
  
  ENV DOTNET_SDK_VERSION 2.1.200
  ENV DOTNET_SDK_DOWNLOAD_URL https://dotnetcli.blob.core.windows.net/dotnet/Sdk/$DOTNET_SDK_VERSION/dotnet-sdk-$DOTNET_SDK_VERSION-linux-x64.tar.gz
  ENV DOTNET_SDK_DOWNLOAD_SHA c1b07ce8849619ca505aafd2983bcdd7141536ccae243d4249b0c9665daf107e03a696ad5f1d95560142cd841a0888bbf5f1a8ff77d3bdc3696b5873481f0998
  
  RUN curl -SL $DOTNET_SDK_DOWNLOAD_URL --output dotnet.tar.gz \
      &amp;&amp; echo "$DOTNET_SDK_DOWNLOAD_SHA dotnet.tar.gz" | sha512sum -c - \
      &amp;&amp; mkdir -p /usr/share/dotnet \
      &amp;&amp; tar -zxf dotnet.tar.gz -C /usr/share/dotnet \
      &amp;&amp; rm dotnet.tar.gz \
      &amp;&amp; ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet
  
  ENV DOTNET_RUNNING_IN_CONTAINER=true \
      DOTNET_USE_POLLING_FILE_WATCHER=true \
      NUGET_XMLDOC_MODE=skip
  
  RUN mkdir warmup \
      &amp;&amp; cd warmup \
      &amp;&amp; dotnet new \
      &amp;&amp; cd .. \
      &amp;&amp; rm -rf warmup \
      &amp;&amp; rm -rf /tmp/NuGetScratch
  
  ENV RestoreUseSkipNonexistentTargets false
  
  ENV NODE_VERSION="8.11.0"
  
  RUN apt-get install wget -y
  
  RUN set -ex \
      &amp;&amp; for key in \
        94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \
        0034A06D9D9B0064CE8ADF6BF1747F4AD2306D93 \
        FD3A5288F042B6850C66B31F09FE44734EB7990E \
        71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \
        DD8F2338BAE7501E3DD5AC78C273792F7D83545D \
        B9AE9905FFD7803F25714661B63B535A4C206CA9 \
        C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \
        77984A986EBC2AA786BC0F66B01FBB92821C587A \
        56730D5401028683275BD23C23EFEFE93C4CFFFE \
      ; do \
        gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \
      done
  
  RUN set -ex \
      &amp;&amp; wget "https://nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.gz" -O node-v$NODE_VERSION-linux-x64.tar.gz \
      &amp;&amp; wget "https://nodejs.org/download/release/v$NODE_VERSION/SHASUMS256.txt.asc" -O SHASUMS256.txt.asc \
      &amp;&amp; gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
      &amp;&amp; grep " node-v$NODE_VERSION-linux-x64.tar.gz\$" SHASUMS256.txt | sha256sum -c - \
          &amp;&amp; tar -xzf "node-v$NODE_VERSION-linux-x64.tar.gz" -C /usr/local --strip-components=1 \
          &amp;&amp; rm "node-v$NODE_VERSION-linux-x64.tar.gz" SHASUMS256.txt.asc SHASUMS256.txt \
          &amp;&amp; ln -s /usr/local/bin/node /usr/local/bin/nodejs \
          &amp;&amp; rm -fr /var/lib/apt/lists/* /tmp/* /var/tmp/*
  
  RUN npm config set user 0
  RUN npm config set unsafe-perm true
  
  RUN npm install -g @angular/cli@1.7.4
  RUN node -v &amp;&amp; npm -v &amp;&amp; ng -v
  
  RUN apt-get update &amp;&amp; apt-get install bzip2 -y
  
  ENV ASPNETCORE_URLS http://+:80
  
  WORKDIR /app
  COPY . .
  RUN dotnet restore ./CodefictionApi
  RUN dotnet publish ./CodefictionApi -c Release
  
  FROM base AS final
  ARG source
  EXPOSE 5102
  ENV ASPNETCORE_URLS http://*:80
  RUN apt-get -qq update &amp;&amp; apt-get -qqy --no-install-recommends install wget gnupg \
      git \
      unzip
  RUN curl -sL https://deb.nodesource.com/setup_8.x |  bash -
  RUN apt-get install -y nodejs
  RUN npm config set user 0
  RUN npm config set unsafe-perm true
  RUN npm install -g npm
  
  RUN npm install -g @angular/cli@1.7.4 --unsafe
  RUN npm install -g @angular/compiler-cli@5.2.1
  RUN node -v &amp;&amp; npm -v &amp;&amp; ng -v
  
  ENV ASPNETCORE_ENVIRONMENT=Development
  
  WORKDIR /app/CodefictionApi/bin/Release/netcoreapp2.0/publish
  COPY --from=publisher /app/CodefictionApi/bin/Release/netcoreapp2.0/publish .
  ENTRYPOINT ["dotnet", "CodefictionApi.dll"]
  </code></pre>
  
  <p><strong>package.json</strong></p>
  
  <pre><code>{
    "name": "codefiction-app",
    "version": "0.0.0",
    "license": "MIT",
    "scripts": {
      "ng": "ng",
      "start": "ng serve",
      "build": "ng build --prod",
      "test": "ng test",
      "lint": "ng lint",
      "e2e": "ng e2e"
    },
    "private": true,
    "dependencies": {
      "@angular/cli": "~1.7.4",
      "@angular/animations": "^5.0.0",
      "@angular/common": "^5.0.0",
      "@angular/compiler": "^5.0.0",
      "@angular/core": "^5.0.0",
      "@angular/forms": "^5.0.0",
      "@angular/http": "^5.0.0",
      "@angular/platform-browser": "^5.0.0",
      "@angular/platform-browser-dynamic": "^5.0.0",
      "@angular/platform-server": "^5.0.0",
      "@angular/router": "^5.2.1",
      "@nguniversal/aspnetcore-engine": "^5.0.0",
      "@nguniversal/common": "^5.0.0",
      "@types/node": "^7.0.12",
      "angular2-router-loader": "^0.3.5",
      "angular2-template-loader": "^0.6.2",
      "angular2-universal-polyfills": "2.0.11",
      "aspnet-prerendering": "3.0.1",
      "awesome-typescript-loader": "^3.0.0",
      "bootstrap": "^3.3.7",
      "bootstrap-sass": "^3.3.7",
      "core-js": "^2.5.1",
      "css": "^2.2.1",
      "css-loader": "^0.28.7",
      "event-source-polyfill": "^0.0.9",
      "file-loader": "^0.11.2",
      "html-loader": "^0.5.1",
      "isomorphic-fetch": "^2.2.1",
      "jquery": "^2.2.1",
      "json-loader": "^0.5.4",
      "moment": "2.18.1",
      "node-sass": "^4.5.2",
      "preboot": "6.0.0-beta.0",
      "raw-loader": "^0.5.1",
      "rimraf": "^2.6.2",
      "rxjs": "^5.5.6",
      "style-loader": "^0.18.2",
      "to-string-loader": "^1.1.5",
      "typescript": "~2.5.0",
      "url-loader": "^0.5.7",
      "zone.js": "^0.8.4"
    },
    "devDependencies": {
      "@angular/compiler-cli": "^5.2.1",
      "@types/chai": "^3.4.34",
      "@types/jasmine": "^2.5.37",
      "chalk": "^2.4.1",
      "chai": "^3.5.0",
      "codelyzer": "4.0.0",
      "jasmine-core": "^2.5.2",
      "karma": "^1.7.1",
      "karma-chai": "^0.1.0",
      "karma-chrome-launcher": "^2.2.0",
      "karma-coverage": "^1.1.1",
      "karma-jasmine": "^1.1.0",
      "karma-mocha-reporter": "^2.2.4",
      "karma-phantomjs-launcher": "^1.0.4",
      "karma-remap-coverage": "^0.1.4",
      "karma-sourcemap-loader": "^0.3.7",
      "protractor": "~5.1.2",
      "ts-node": "~4.1.0",
      "tslint": "~5.9.1"
    }
  }
  </code></pre>
  
  <p>csproj publish command</p>
  
  <pre><code>  &lt;Target Name="PublishProject" BeforeTargets="ComputeFilesToPublish"&gt;
      &lt;Exec Command="npm install" /&gt;
      &lt;Exec Command="./node_modules/.bin/ng -v" /&gt;
      &lt;Exec Command="./node_modules/.bin/ng build --env=dev --app 0" /&gt;
      &lt;Exec Command="./node_modules/.bin/ng build --env=dev --app 1" /&gt;
  
      &lt;!-- Include the newly-built files in the publish output --&gt;
      &lt;ItemGroup&gt;
        &lt;DistFiles Include="wwwroot/**; CodefictionApp/dist-server/**" /&gt;
        &lt;ResolvedFileToPublish Include="@(DistFiles-&gt;'%(FullPath)')" Exclude="@(ResolvedFileToPublish)"&gt;
          &lt;RelativePath&gt;%(DistFiles.Identity)&lt;/RelativePath&gt;
          &lt;CopyToPublishDirectory&gt;PreserveNewest&lt;/CopyToPublishDirectory&gt;
        &lt;/ResolvedFileToPublish&gt;
      &lt;/ItemGroup&gt;
    &lt;/Target&gt;
  </code></pre>
  
  <p>Full source can be inspected from here</p>
  
  <p><a href="https://github.com/Blind-Striker/codefiction-tech-new" rel="noreferrer">https://github.com/Blind-Striker/codefiction-tech-new</a></p>
  
  <p>Not working version can be inspected from this branch</p>
  
  <p><a href="https://github.com/Blind-Striker/codefiction-tech-new/tree/docker-not-working-version" rel="noreferrer">https://github.com/Blind-Striker/codefiction-tech-new/tree/docker-not-working-version</a></p>
  
  <p>Thanks.</p>