Breadcrumbs

Environment-Specific Configuration for Custom Flows

Environment-Specific Configuration for Custom Flows

Overview

When customers add custom Node-RED flows that call external APIs with custom TLS certificates, they often hardcode API endpoints and cert paths across multiple nodes. This makes it painful to maintain separate QA, Staging, and Production environments since every value must be manually edited per environment.

This guide explains how to externalize these values so that flows.json remains identical across all environments, with configuration injected through Helm.


Externalizing API Endpoints and Custom Variables

How siteEnvVars Works

The Helm chart has two sections for environment variables:

  • extraEnvVars — Default variables shipped with the chart (e.g., CONVERSATION_MANAGER_INTERNAL_URL, TENANT_ID). Maintained by Expertflow.

  • siteEnvVars — Customer-specific variables. Empty by default. Customers add their own entries here.

Both render into the container's environment. Customers can add completely new variables that don't exist in the original chart — they are not limited to overriding existing ones.

Defining Variables

Add custom variables in the Helm values override file:

YAML
# customer-values-qa.yaml
conversation-studio:
  siteEnvVars:
    - name: EXT_CUSTOM_API_URL
      value: "https://custom-endpoint.com/api"

For production, maintain a separate file with production values:

YAML
# customer-values-prod.yaml
conversation-studio:
  siteEnvVars:
    - name: EXT_CUSTOM_API_URL
      value: "https://custom-endpoint.com/api"

Deploy with the appropriate file:

Bash

Using Variables in Node-RED

In Function Nodes — use env.get():

JavaScript
// Instead of hardcoding:
// const crmUrl = "https://crm.customer.com/api/v2";

const crmUrl = env.get("EXT_CUSTOM_API_URL");
const apiKey = env.get("EXT_CUSTOM_API_KEY");

const response = await fetch(crmUrl + "/contacts/" + msg.payload.customerId, {
    headers: { "Authorization": "Bearer " + apiKey }
});

In HTTP Request Node URL fields — use ${VAR_NAME} syntax directly:

${EXT_CUSTOM_API_URL}/contacts

Upgrade Compatibility

Since siteEnvVars live in the customer's override file (not in the chart), they persist across chart upgrades automatically. The customer upgrades to a new chart version and their custom variables carry forward — no changes needed. This follows the same pattern used by other components (e.g., bot-framework uses siteEnvVars the same way).

Naming Convention

Prefix all custom variables with EXT_ to distinguish them from built-in Conversation Studio variables (e.g., EXT_CUSTOM_API_URL, EXT_CRM_API_KEY, EXT_CLIENT_CERT_PATH).


Externalizing TLS Certificates

Certificates are files, not strings, so they require a different mechanism: Kubernetes secrets mounted as volumes.

Step 1: Create a Kubernetes Secret

Each environment has its own secret with different cert content but the same file names:

Bash
# QA
kubectl create secret generic customer-ext-certs \
  --from-file=client.pem=./qa-client.pem \
  --from-file=client-key.pem=./qa-client-key.pem \
  --from-file=ca.pem=./qa-ca.pem \
  -n <namespace>

# Prod (same secret name, same file names, different content)
kubectl create secret generic customer-ext-certs \
  --from-file=client.pem=./prod-client.pem \
  --from-file=client-key.pem=./prod-client-key.pem \
  --from-file=ca.pem=./prod-ca.pem \
  -n <namespace>

Step 2: Mount the Secret

There are two ways to mount custom volumes, depending on chart support.

Option A: Using siteVolumes / siteVolumeMounts (Recommended)

Like siteEnvVars, the chart can provide siteVolumes and siteVolumeMounts that render separately from extraVolumes and extraVolumeMounts. The customer only specifies their custom volumes — no need to repeat the chart defaults:

YAML
conversation-studio:
  siteVolumes:
    - name: customer-ext-certs
      secret:
        secretName: customer-ext-certs
  siteVolumeMounts:
    - name: customer-ext-certs
      mountPath: /certs/custom

The chart template appends these alongside the defaults (redis-crt, mongo-mongodb-ca, etc.), so nothing is lost. This is upgrade-safe and consistent with the siteEnvVars pattern.

Note: If the chart does not yet support siteVolumes/siteVolumeMounts, this requires a chart enhancement. See Option B for the current workaround.

Option B: Using extraVolumes / extraVolumeMounts (Workaround)

Helm performs a full replacement on arrays. If the customer overrides extraVolumes, it replaces the entire list. The customer must include the original defaults alongside their custom entries:

YAML
conversation-studio:
  extraVolumes:
    # Defaults (must be repeated)
    - name: redis-crt
      secret:
        secretName: redis-crt
    - name: mongo-mongodb-ca
      secret:
        secretName: mongo-mongodb-ca
    # Custom
    - name: customer-ext-certs
      secret:
        secretName: customer-ext-certs
  extraVolumeMounts:
    # Defaults (must be repeated)
    - name: redis-crt
      mountPath: /redis
    - name: mongo-mongodb-ca
      mountPath: /mongo
    - name: conversation-studio-flow-vol
      mountPath: /flows
    # Custom
    - name: customer-ext-certs
      mountPath: /certs/custom

Caution: If a new chart version changes the default extraVolumes, the customer must update their override file to match. The upgrade guide should call this out.

Step 3: Reference Certificates in Node-RED

The mount path (/certs/custom/) and file names are fixed, so the paths are the same across all environments. Only the file content differs per namespace.

Option A: TLS Config in HTTP Request Node (Recommended)
  1. Drag an HTTP Request node onto the canvas and double-click to open properties.

  2. Check "Enable secure (SSL/TLS) connection".

  3. Click the pencil icon next to TLS Configuration to create a new TLS config node.

  4. Fill in the paths:

    • Certificate: /certs/custom/client.pem

    • Private Key: /certs/custom/client-key.pem

    • CA Certificate: /certs/custom/ca.pem

  5. Name it (e.g., "Customer External APIs TLS") and click Add.

  6. Set the URL field to ${EXT_CRM_API_URL}/contacts.

Other HTTP Request nodes can select the same TLS config from the dropdown — configure once, reuse everywhere. This also works with the Custom HTTP Request node that ships with Conversation Studio.

Option B: Loading Certificates in Function Nodes

For scenarios needing full control over the HTTP client:

JavaScript
const fs = require("fs");
const https = require("https");

const agent = new https.Agent({
    cert: fs.readFileSync("/certs/custom/client.pem"),
    key: fs.readFileSync("/certs/custom/client-key.pem"),
    ca: fs.readFileSync("/certs/custom/ca.pem"),
});

const url = env.get("EXT_PAYMENT_API_URL");
const res = await fetch(url + "/charge", {
    method: "POST",
    agent: agent,
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(msg.payload),
});

msg.payload = await res.json();
return msg;

Summary

Item

Per Environment?

Where It Lives

API URLs / keys

Different per env

siteEnvVars in Helm values override file

Certificate file content

Different per env

Kubernetes secret (per namespace)

Certificate mount path

Same across envs

/certs/custom/ (fixed in Helm values)

TLS config node / Function code

Same across envs

In flows.json

flows.json is identical across all environments. The only environment-specific inputs are the Helm values file and the Kubernetes secret.