Building and deploying

This project is no longer under development.
See the project README for more details.

This page will take you through the steps you need to do to build and deploy your application to production.

Table of Contents

prpl-server is a node server that uses differential serving to deliver the optimal response for each browser. Our provided configuration (polymer.json) supports the following responses:

Building for prpl-server

To run the build:

npm run build

This will populate the server/build/ directory:

├── build/
|   └── es5-bundled/
|   └── es6-bundled/
|   └── esm-bundled/
|   └── polymer.json
├── app.yaml
├── package-lock.json
└── package.json

Previewing prpl-server

To preview the build using prpl-server locally:

npm run serve

Deploying prpl-server

After building, the contents of server/ contains all the files and configuration necessary to run the app in production. The provided server/package.json specifies server dependencies and the start command which can be used on almost any hosting service that supports Node.js.

App Engine

Standard Environment

The contents of server/app.yaml is pre-configured to be deployed to Google App Engine Node.js Standard Environment. Use the gcloud tool to deploy the contents of server/ (e.g. gcloud app deploy server/app.yaml).

Flexible Environment

To deploy to Google App Engine Node.js Flexible Environment, replace the entire contents of server/app.yaml with:

runtime: nodejs
env: flex
  min_num_instances: 1

Use the gcloud tool to deploy the contents of server/ (e.g. gcloud app deploy server/app.yaml).

Firebase Hosting + Firebase Functions

Firebase Hosting alone is not sufficient for hosting the prpl-server build since it requires some server-side processing of the user agent string. Instead, you will have to use Firebase Functions for server-side processing. This gist contains detailed instructions on how to accomplish this.

prpl-server as an Express middleware

prpl-server also works as an express middleware, so that it adds the appropriate routes to the server application. To use prpl-server as Express middleware, create an app.js file in the server directory:

// server/app.js
const express = require('express')
prpl = require('prpl-server');
const app = express();

app.get('/api/launch', (req, res, next) => res.send('Launched));
let polyConfigFile = require("./build/polymer.json");
app.get('/*', prpl.makeHandler('server/build',polyConfigFile));
app.listen(3000, () => console.log('Express + prpl-server app listening on port 3000!'));

Since prpl.makeHandler() receives polymer.json as configuration (also used by Polymer to build), it will serve the right build depending on the browser’s capabilities.

Add a script to your main package.json file:

"scripts": {
    "serve:express-prpl": "node ./server/app.js",

Finally, change the start scritpt in the server/package.json file:

"scripts": {
    "start": "node ./app.js",

Static hosting

If you don’t need differential serving and want to serve the same build to all browsers, you can just deploy to a static server.

Building for static hosting

To build the production site, run:

npm run build:static

This will create three different build outputs:

├── es5-bundled/
├── es6-bundled/
├── esm-bundled/
└── ...

Previewing static hosting

To preview it locally, run:

npm run serve:static

Our provided configuration will serve the es5-bundled build. If you don’t need to support legacy browsers, you can use a more modern build by modifying the serve:static script in package.json to use es6-bundled or esm-bundled instead. Be sure that all page navigation requests are served the contents of index.html.

Deploying static hosting

By default, static hosting servers aren’t set up to work with single page apps (SPAs) – in particular, the problem is that an SPA uses routes that do not correspond to full file path names. For example, in pwa-starter-kit the second view’s URL is http://localhost:8081/view2, but that doesn’t correspond to a file that the browser can use. Each static hosting server has a different approach to working around this:

App Engine

Download the Google App Engine SDK and follow the instructions for your platform to install it. Here we are using Python SDK.

Sign up for an App Engine account and go to project dashboard page to create a new project. Make note of the project ID associated with your project.

Create an App Engine config file (app.yaml) with the following:

runtime: python27
api_version: 1
threadsafe: yes


- url: /images
  static_dir: build/es5-bundled/images
  secure: always

- url: /node_modules
  static_dir: build/es5-bundled/node_modules
  secure: always

- url: /src
  static_dir: build/es5-bundled/src
  secure: always

- url: /manifest.json
  static_files: build/es5-bundled/manifest.json
  upload: build/es5-bundled/manifest.json
  secure: always

- url: /service-worker.js
  static_files: build/es5-bundled/service-worker.js
  upload: build/es5-bundled/service-worker.js
  secure: always

- url: /.*
  static_files: build/es5-bundled/index.html
  upload: build/es5-bundled/index.html
  secure: always

- build/es6-bundled/
- build/esm-bundled/
- images/
- node_modules/
- server/
- src/
- test/

To deploy your project:

gcloud app deploy --project <project_ID>

Firebase Hosting

Firebase provides easy http2-enabled static hosting, a real-time database, server functions, and edge-caching all over the globe.

Install the Firebase CLI:

npm install -g firebase-tools

Sign up for a Firebase account if you don’t have one. Then go to Firebase Console to create a new project. Make note of the project ID associated with your project.

Login to the Firebase and set the previously created project as the active Firebase project for your working directory:

firebase login
firebase use <project_ID>

Create a Firebase config file (firebase.json) with the following:

  "hosting": {
    "public": "build/es5-bundled/",
    "rewrites": [
        "source": "**/!(*.*)",
        "destination": "/index.html"
     "headers": [
        "headers": [
            "key": "Cache-Control",
            "value": "no-cache"

To deploy your project:

firebase deploy


Netlify has built-in Continuous Deployment which automatically runs your build commands and deploys the result whenever you push to your Git repository.

Create a _redirects file with the following rewrite rules:

/*    /index.html   200

Go to netlify project page and setup the Git hosting for the new project. In Basic build settings, put npm run build:static as the build command and build/es5-bundled as the publish directory.

Click Deploy site.

Service Worker

A Service Worker is loaded and registered in the index.html file. However, during development (to make debugging easier), the Service Worker does not actually exist, and only a stub file is used.

The production time Service Worker is automatically created during build time, i.e. by running npm run build or npm run build:static. This file is generated based on the polymer.json and sw-precache-config.js configuration files, and you can find it under each of the build directories:

├── es5-bundled/
|   └── service-worker.js
├── es6-bundled/
|   └── service-worker.js
├── esm-bundled/
|   └── service-worker.js
└── ...

By default, all of the source files (inside the /src directory) will be pre-cached, as specified in the sw-precache-config.js configuration file. If you want to change this behaviour, check out the sw-precache-config docs.