Let’s Encrypt SSL with Node.js

Let’s Encrypt SSL Add-On with Node.js

For Node.js nodes, the Let’s Encrypt add-on issues and stores the certificates but does not bind them automatically to the application. The Node.js HTTPS server must read the generated certificate files directly from the application code.
Recommended alternative for larger projects: Place a load balancer in front of the Node.js layer and install Let’s Encrypt on the balancer. This provides automated certificate binding and is better suited to horizontally scaled applications.

Create the Node.js Environment

1Create an environment

Open the topology wizard, select a Node.js application server, configure the required resources and environment name, and create the environment.

Create a Node.js environment
Create the Node.js application environment.

Install Let’s Encrypt

2Install the Let’s Encrypt add-on

Install Let’s Encrypt Free SSL, enter the connected external domain, select the Node.js environment, and choose the Application Servers layer.

Install Let’s Encrypt for Node.js
Generate the certificate for the Node.js application.

The generated files are stored in:

Private Key/var/lib/jelastic/keys/privkey.pem
Certificate Chain/var/lib/jelastic/keys/fullchain.pem
CA Certificate/var/lib/jelastic/keys/ca.cer
Node.js-specific behaviour: The add-on creates the certificate files but the application code must load them and create the HTTPS server manually.

Configure a New HTTPS Application

For a new application, replace the default /home/jelastic/ROOT/server.js content with an HTTPS server configuration:

const https = require('node:https');
const fs = require('node:fs');

const options = {
  key: fs.readFileSync('/var/lib/jelastic/keys/privkey.pem'),
  cert: fs.readFileSync('/var/lib/jelastic/keys/fullchain.pem')
};

https.createServer(options, (req, res) => {
  res.writeHead(200);
  res.end('hello world\n');
}).listen(443);

console.log("The HTTPS server has started at: https://localhost:443/");

Update an Existing Application

For an existing project, integrate the HTTPS module and certificate paths into the current server.js file. The following example adapts the default Hello World application:

#!/usr/bin/env node

var https = require("https"),
    url = require("url"),
    ejs = require("ejs"),
    fs = require("fs"),
    os = require("os"),
    staticResource = require("static-resource"),
    port = 443,
    serverUrl,
    handler,
    favicon;

const options = {
  key: fs.readFileSync('/var/lib/jelastic/keys/privkey.pem'),
  cert: fs.readFileSync('/var/lib/jelastic/keys/fullchain.pem')
};

serverUrl = "https://localhost:" + port + "/";
handler = staticResource.createHandler(fs.realpathSync("./public"));
favicon = fs.realpathSync('./public/favicon.png');

https.createServer(options, function (req, res) {
    var path = url.parse(req.url).pathname;

    if (path === "/") {
        res.writeHead(200, {"Content-Type": "text/html"});
        res.write(ejs.render(fs.readFileSync("./index.ejs", "utf8"), {
            hostname: os.hostname()
        }));
        res.end();
    } else if (req.method === 'GET' && path === '/favicon.png') {
        res.setHeader('Content-Type', 'image/png');
        fs.createReadStream(favicon).pipe(res);
    } else {
        if (!handler.handle(path, req, res)) {
            res.writeHead(404);
            res.write("404");
            res.end();
        }
    }
}).listen(port);

console.log("The HTTPS server has started at: " + serverUrl);

Run the HTTPS Server

Open Web SSH and start the application. The documentation example uses the Forever process manager. Elevated privileges are required to listen on port 443.

cd /home/jelastic/ROOT
sudo forever start server.js

Without a process manager, start the application directly:

sudo node server.js

Open the application through https:// and verify the certificate and secure connection.

Node.js application over HTTPS
Verify that the Node.js application is accessible through a secure HTTPS connection.

Certificate Renewal

Let’s Encrypt certificates remain valid for 90 days. The add-on renews them automatically about 30 days before expiration. After renewal, the Node.js server must be restarted or preferably reloaded so it reads the new certificate files.

Create the following file, creating the directory when necessary:

/var/lib/jelastic/keys/letsencrypt/settings-custom

For Forever, add this deployment hook:

deployHook=sudo forever restart /home/jelastic/ROOT/server.js
Let’s Encrypt renewal webhook
Restart the Node.js process automatically after certificate renewal.

A custom executable shell script can also be used:

deployHook: /path/to/your/file.sh

Make the script executable:

chmod +x {fileName}

Example script:

#!/bin/bash
echo "This is example of deployHook script" >> /tmp/testFile

The certificate update can also be triggered manually from the Node.js server’s Add-Ons panel.

Manual Let’s Encrypt update
Use Update Now to trigger certificate renewal manually.

Additional Recommendations

  • Create environment variables for certificate paths instead of hardcoding them in the application.
  • Use /var/lib/jelastic/keys/fullchain.pem for the certificate path.
  • Use /var/lib/jelastic/keys/privkey.pem for the private-key path.
  • Use the deployHook parameter when installing or managing the add-on through API.
  • Prefer a load balancer in front of the Node.js application for larger or horizontally scaled projects.
Let’s Encrypt certificate environment variables
Store certificate paths as environment variables for easier maintenance.

What’s next?