---
title: "Document Engine with Docker and EJS templates"
canonical_url: "https://www.nutrient.io/sdk/document-engine/getting-started/docker-deployment-ejs-templates/"
md_url: "https://www.nutrient.io/sdk/document-engine/getting-started/docker-deployment-ejs-templates.md"
last_updated: "2026-05-20T19:49:34.959Z"
description: "Build a document viewing application using Document Engine with EJS (Embedded JavaScript) templates. This guide shows you how to create a Node.js web application with server-side rendering for viewing and annotating documents."
---

# Document Engine with Docker and EJS templates

You can integrate Document Engine with [Nutrient Web SDK](https://www.nutrient.io/guides/web.md). This integration enables you to offload document processing, rendering, and management to the server, enabling features such as real-time collaboration, annotation synchronization, and improved performance for large or complex documents.

This guide shows you how to build a document viewing application using Embedded JavaScript (EJS) templates with Document Engine. You’ll create a Node.js web application with server-side rendering that integrates [Nutrient Web SDK](https://www.nutrient.io/guides/web.md) for viewing and annotating documents.

## Requirements

Document Engine is compatible with a range of platforms. Below is the list of supported operating systems.

- **macOS**:
  - Ventura
  - Monterey
  - Mojave
  - Catalina
  - Big Sur

- **Linux**:
  - Ubuntu, Fedora, Debian, and CentOS
  - Ubuntu and Debian derivatives (such as Kubuntu, Xubuntu) are also supported

**Processor requirements**:

- 64-bit Intel (x86_64) processors

- ARM (AArch64) processors

**Minimum system requirements**:

- At least 4GB of RAM, regardless of the operating system

## Installing Docker

Document Engine is distributed as a Docker container. To run it on your computer, you need to install a Docker runtime distribution for your operating system.

### macOS

Install and start Docker Desktop for Mac. For detailed instructions, refer to the [Docker website](https://docs.docker.com/docker-for-mac/install/).

### Windows

Install and start Docker Desktop for Windows. For detailed instructions, refer to the [Docker website](https://docs.docker.com/docker-for-windows/install/).

> Document Engine runs as a Linux container. If you’re using Docker Desktop for Windows, ensure it’s configured to work with Linux containers. For detailed steps, refer to the **How do I switch between Windows and Linux containers?** section in the [Docker documentation](https://docs.docker.com/desktop/setup/install/windows-install/). Users with Docker already set up might need to switch from Windows containers to Linux containers for compatibility.

### Linux

Install and start Docker Engine. For detailed instructions on how to install Docker Engine for your Linux distribution, refer to the [Docker website](https://docs.docker.com/engine/install/#server).

Once you finish installing Docker Engine, follow the [instructions](https://docs.docker.com/compose/install/#install-compose-on-linux-systems) to install Docker Compose.








## Setting up Document Engine

Copy the code snippet below and save it anywhere on your computer in a file called `docker-compose.yml`:

```yaml

version: "3.8"

services:
  document_engine:
    image: pspdfkit/document-engine:1.15.0
    environment:
      PGUSER: de-user
      PGPASSWORD: password
      PGDATABASE: document-engine
      PGHOST: db
      PGPORT: 5432
      API_AUTH_TOKEN: secret
      SECRET_KEY_BASE: secret-key-base
      JWT_PUBLIC_KEY: |
        -----BEGIN PUBLIC KEY-----
        MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2gzhmJ9TDanEzWdP1WG+
        0Ecwbe7f3bv6e5UUpvcT5q68IQJKP47AQdBAnSlFVi4X9SaurbWoXdS6jpmPpk24
        QvitzLNFphHdwjFBelTAOa6taZrSusoFvrtK9x5xsW4zzt/bkpUraNx82Z8MwLwr
        t6HlY7dgO9+xBAabj4t1d2t+0HS8O/ed3CB6T2lj6S8AbLDSEFc9ScO6Uc1XJlSo
        rgyJJSPCpNhSq3AubEZ1wMS1iEtgAzTPRDsQv50qWIbn634HLWxTP/UH6YNJBwzt
        3O6q29kTtjXlMGXCvin37PyX4Jy1IiPFwJm45aWJGKSfVGMDojTJbuUtM+8P9Rrn
        AwIDAQAB
        -----END PUBLIC KEY-----
      JWT_ALGORITHM: RS256
      DASHBOARD_USERNAME: dashboard
      DASHBOARD_PASSWORD: secret
    ports:
      - 5000:5000
    depends_on:
      - db
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: de-user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: document-engine
      POSTGRES_INITDB_ARGS: --data-checksums
      PGDATA: /var/lib/postgresql/data/pgdata
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

```

## Starting Document Engine

To start Document Engine, follow the steps below.

1. Open your terminal emulator.

   ### macOS

   Use the terminal emulator integrated with your code editor or IDE. Alternatively, you can use `Terminal.app` or [iTerm2](https://iterm2.com/).

   ### Windows

   Use your code editor’s integrated terminal or [PowerShell](https://learn.microsoft.com/en-us/powershell/scripting/windows-powershell/starting-windows-powershell?view=powershell-7.5).

   ### Linux

   Use the terminal emulator integrated with your code editor or IDE, or the one bundled with your desktop environment.

2. Go to the directory where you saved the `docker-compose.yml` file:

   ```sh

   cd <path-to-directory-with-docker-compose-yml>
   ```

3. Run the following:

   ```sh

   docker-compose up
   ```

This command might take a while to run, depending on your internet connection speed. Wait until you see the following message in the terminal:

```

document_engine_1  | Access the web dashboard at http://localhost:5000/dashboard

```

Document Engine is now up and running!

## Uploading a document to Document Engine

With Document Engine running, visit `http://localhost:5000/dashboard` and authenticate using “dashboard” for the username and “secret” for the password. Choose **Add Document** and upload the document you want to work with.

Once the document is uploaded, visit `http://localhost:5000/dashboard/documents` to see the list of available documents. Each document is identified by an ID. Take note of the ID of the document you just uploaded, as you’ll need it shortly.

The ID will look similar to `7KPS8X13JRB2G841X4V7EQ3T2J`.

## Installing Node.js

If you haven’t installed Node.js, head to [the official guides](https://nodejs.org/en/download/package-manager) and follow the instructions. By the end of the installation process, you should be able to run the following command:

```

node --version

```

The output will be something like v14. You can ignore any subsequent number.

## Generating the application

You’ll use [Express](https://expressjs.com/), one of the most common Node.js web frameworks. To create a new Express application, you can use the official generator.

Run:

```

npx express-generator pspdfkit_example --view=ejs

```

This command will generate a project structure and instruct you on the steps to follow to install dependencies and start the project.

Once you’ve followed all the steps, you should be able to visit `http://localhost:3000` to confirm the application is working as expected.

## Adding a page to view the document

You need to create a page that will show a document stored inside Document Engine.

You’ll want this page to be available at `http://localhost:3000/documents/:id`, where the document ID is the ID automatically assigned by Document Engine when uploading a document.

To achieve this, create a new route to display a document and mount it in the application.

1. Create the document’s route in the `./routes/documents.js` file:

   ```js

   var express = require("express");
   var router = express.Router();

   router.get("/:documentId", function (req, res, next) {
     res.render("documents/show", { documentId: req.params.documentId });
   });

   module.exports = router;
   ```

   Inside the route, retrieve the ID captured by the routing logic and assign it to a `documentId` variable you can refer to in the view.

2. Create a corresponding view with some minimal HTML that prints the document ID in the `./views/documents/show.ejs` file:

   ```html

   <h1>Show document <%= documentId %></h1>
   ```

3. Mount the new route in the application in the `./app.js` file:

   ```diff

    var indexRouter = require('./routes/index');
    var usersRouter = require('./routes/users');
   +var documentsRouter = require("./routes/documents");
    //...
    // rest of the file
    //...
    app.use('/', indexRouter);
    app.use('/users', usersRouter);
   +app.use("/documents", documentsRouter);
   ```

Stop and restart the Express server.

You can now visit `http://localhost:3000/documents/:id`, replacing `:id` with the ID of the document you uploaded to the Document Engine dashboard.

The page should contain the text **Show document**, followed by your document ID.

## Creating a JSON Web Token (JWT)

Nutrient requires the use of JWTs to authenticate and authorize a viewer session against Document Engine. For a comprehensive understanding of JWT-based authentication with Document Engine, refer to our [client authentication](https://www.nutrient.io/guides/document-engine/viewer/client-authentication.md) guide.

To create JWTs, install the `jsonwebtoken` dependency:

```bash

npm install --save jsonwebtoken

```

Stop and restart the Express server.

Working with JWTs requires a private and public key pair. The private key is used by the Express application, while the public key is used by Document Engine.

The public key has already been configured in the Document Engine `docker-compose.yml` file via the `JWT_PUBLIC_KEY` environment variable.

To configure the private key, create a `config/pspdfkit/jwt.pem` file with the following contents:

```

-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA2gzhmJ9TDanEzWdP1WG+0Ecwbe7f3bv6e5UUpvcT5q68IQJK
P47AQdBAnSlFVi4X9SaurbWoXdS6jpmPpk24QvitzLNFphHdwjFBelTAOa6taZrS
usoFvrtK9x5xsW4zzt/bkpUraNx82Z8MwLwrt6HlY7dgO9+xBAabj4t1d2t+0HS8
O/ed3CB6T2lj6S8AbLDSEFc9ScO6Uc1XJlSorgyJJSPCpNhSq3AubEZ1wMS1iEtg
AzTPRDsQv50qWIbn634HLWxTP/UH6YNJBwzt3O6q29kTtjXlMGXCvin37PyX4Jy1
IiPFwJm45aWJGKSfVGMDojTJbuUtM+8P9RrnAwIDAQABAoIBAQDSKxhGw0qKINhQ
IwQP5+bDWdqUG2orjsQf2dHOHNhRwJoUNuDZ4f3tcYzV7rGmH0d4Q5CaXj2qMyCd
0eVjpgW0h3z9kM3RA+d7BX7XKlkdQABliZUT9SUUcfIPvohXPKEzBRHed2kf6WVt
XKAuJTD+Dk3LjzRygWldOAE4mnLeZjU61kxPYriynyre+44Gpsgy37Tj25MAmVCY
Flotr/1WZx6bg3HIyFRGxnoJ1zU1MkGxwS4IsrQwOpWEHBiD5nvo54hF5I00NHj/
ccz+MwpgGdjyl02IGCy1fF+Q5SYyH86DG52Mgn8VI9dseGmanLGcgNvrdJFILoJR
SZW7gQoBAoGBAP+D6ZmRF7EqPNMypEHQ5qHHDMvil3mhNQJyIC5rhhl/nn063wnm
zhg96109hVh4zUAj3Rmjb9WqPiW7KBMJJdnEPjmZ/NOXKmgjs2BF+c8oiLQyTQml
xB7LnptvBDi8MnEd3uemfxNuZc+2siuSzgditshNru8xPG2Sn99JC271AoGBANp2
xj5EfdlqNLd11paLOtJ7dfREgc+8FxQCiKSxbaOlVXNk0DW1w4+zLnFohj2m/wRr
bBIzSL+eufoQ9y4BT/AA+ln4qxOpC0isOGK5SxwIjB6OHhCuP8L3anj1IFYM+NX0
Xr1/qdZHKulgbS49cq+TDpB74WyKLLnsvQFyINMXAoGABR5+cp4ujFUdTNnp4out
4zXasscCY+Rv7HGe5W8wC5i78yRXzZn7LQ8ohQCziDc7XXqadmYI2o4DmrvqLJ91
S6yb1omYQCD6L4XvlREx1Q2p13pegr/4cul/bvvFaOGUXSHNEnUKfLgsgAHYBfl1
+T3oDZFI3O/ulv9mBpIvEXUCgYEApeRnqcUM49o4ac/7wZm8czT5XyHeiUbFJ5a8
+IMbRJc6CkRVr1N1S1u/OrMqrQpwwIRqLm/vIEOB6hiT+sVYVGIJueSQ1H8baHYO
4zjdhk4fSNyWjAgltwF2Qp+xjGaRVrcYckHNUD/+n/VvMxvKSPUcrC7GAUvzpsPU
ypJFxsUCgYEA6GuP6M2zIhCYYeB2iLRD4ZHw92RfjikaYmB0++T0y2TVrStlzXHl
c8H6tJWNchtHH30nfLCj9WIMb/cODpm/DrzlSigHffo3+5XUpD/2nSrcFKESw4Xs
a4GXoAxqU44w4Mckg2E19b2MrcNkV9eWAyTACbEO4oFcZcSZOCKj8Fw=
-----END RSA PRIVATE KEY-----

```

Update `./routes/documents.js` to read the private key so that it can be used to sign JWTs and pass them to the view.

In the claims, pass the document ID, the set of permissions you want to have, and an expiry of one hour in the `./routes/documents.js` file:

```diff

 var express = require("express");
 var router = express.Router();
+var fs = require("fs");
+var path = require("path");
+var jwt = require("jsonwebtoken");
+var jwtKey = fs.readFileSync(

+  path.resolve(__dirname, "../config/pspdfkit/jwt.pem")
+);

 router.get("/:documentId", function (req, res, next) {

+  var jwt = prepareJwt(req.params.documentId);

-  res.render("documents/show", { documentId: req.params.documentId });

+  res.render("documents/show", { documentId: req.params.documentId, jwt: jwt });
 });

+ +var prepareJwt = function (documentId) {

+  var claims = {

+    document_id: documentId,

+    permissions: ["read-document", "write", "download"],

+  };

+ +  return jwt.sign(claims, jwtKey, {

+    algorithm: "RS256",

+    expiresIn: 60 * 60, // 1hr, this will set the `exp` claim for us.

+    allowInsecureKeySizes: true,

+  });
+};

 module.exports = router;

```

The encoded JWT is then assigned to the `jwt` variable, which can be referenced in the `./views/documents/show.ejs` file:

```diff

 <h1>Show document <%= documentId %></h1>
+<h1>JWT <%= jwt %></h1>

```

Stop and restart the Express server, and then refresh the page. You’ll now see a fairly long token printed on the page.

## Loading an existing document

Update the view to load the SDK, passing the document ID and the JWT in the `./views/documents/show.ejs` file:

```diff

+<script src="https://cdn.cloud.nutrient.io/pspdfkit-web@1.15.0/pspdfkit.js"></script>
 <h1>Show document <%= documentId %></h1>
 <h1>JWT <%= jwt %></h1>
+<!-- 2. Element where PSPDFKit will be mounted. -->

+<div id="pspdfkit" style="width: 100%; max-width: 800px; height: 480px;"></div>
+<!-- 3. Initialize PSPDFKit. -->

+<script>

+  PSPDFKit.load({

+    serverUrl: "http://localhost:5000/",

+    container: "#pspdfkit",

+    documentId: "<%= documentId %>",

+    authPayload: { jwt: "<%= jwt %>" },

+    instant: true

+  })

+.then(function(instance) {

+      console.log("PSPDFKit loaded", instance);

+    })

+.catch(function(error) {

+      console.error(error.message);

+    });
+</script>

```

Refresh the page, and you’ll see the Nutrient Web SDK viewer showing the document you just uploaded. If you annotate the document and refresh the page, all changes will be automatically persisted.
---

## Related pages

- [Process documents with Document Engine and curl](/sdk/document-engine/getting-started/curl.md)
- [Document Engine with Docker](/sdk/document-engine/getting-started/docker-deployment-react-frontend.md)
- [Process documents with Document Engine and Golang](/sdk/document-engine/getting-started/golang.md)
- [Getting started with Document Engine](/sdk/document-engine/getting-started.md)
- [Process documents with Document Engine and Python](/sdk/document-engine/getting-started/python.md)
- [Process documents with Document Engine and Rust](/sdk/document-engine/getting-started/rust.md)
- [Process documents with Document Engine and PHP](/sdk/document-engine/getting-started/php.md)

