Install Netprobe Router using Helm
Overview Copied
Use this guide to install Netprobe Router in Kubernetes with the itrs/netprobe-router Helm chart.
Netprobe Router receives Geneos or OpenTelemetry telemetry on a single gRPC port and forwards it to one or more downstream destinations, for example ITRS Analytics.
You must provide a router config.yml at installation with --set-file config.file=.... The chart packages starting-point examples under examples/ (a Helm values file and a full router-config.yml). The chart does not apply these examples automatically, and they are not ready to install unchanged. Copy them, edit hosts, Ingress, TLS paths, and credentials for your environment, and then pass your copies to Helm.
For the configuration schema, see Netprobe Router configuration. A configuration written for a later Netprobe Router package might fail on an older image. Use the configuration that matches the package version in the image tag.
Prerequisites Copied
Create a Secret with your ITRS Docker registry credentials, unless one already exists:
kubectl create secret docker-registry itrsdocker \
--docker-server=https://docker.itrsgroup.com \
--docker-username=<EMAIL_ADDRESS> \
--docker-password=<PASSWORD>
If your cluster pulls the image another way (a mirror or node-level registry credentials), see Private registries and air-gapped installations.
Installer permissions Copied
The chart creates only namespace-scoped resources, so the account that runs helm install or helm upgrade needs a namespace Role, not a ClusterRole. The account needs get, list, watch, create, update, patch, and delete on:
| Resource | API group | Required |
|---|---|---|
configmaps, services |
core | Always |
deployments |
apps |
When persistence.enabled=false (default) |
statefulsets |
apps |
When persistence.enabled=true |
ingresses |
networking.k8s.io |
When ingress.enabled=true |
serviceaccounts |
core | When serviceAccount.create=true |
poddisruptionbudgets |
policy |
When podDisruptionBudget.enabled=true |
secrets |
core | Always. Helm stores its release records as Secrets. |
For example, a Role for an installation with persistence and an Ingress:
kubectl create role netprobe-router-installer -n <NAMESPACE> \
--verb=get,list,watch,create,update,patch,delete \
--resource=configmaps,services,secrets,statefulsets.apps,ingresses.networking.k8s.io
The chart does not create cluster-scoped resources. The namespace must already exist. helm install --create-namespace requires permission to create namespaces. The kubelet reads the Secrets that the chart references (TLS, credentials, and image pull). The chart does not create those Secrets, so the installer does not need access to them.
Container image Copied
The chart runs docker.itrsgroup.com/netprobe-router. The image packages the released Netprobe Router application as a non-root container. It does not contain an active Collection Agent configuration.
You must provide the configuration file. The image reads it from /app/config/config.yml. Starting the image without that file is an error. The image does not set destinations, routing rules, authentication, TLS settings, or persistence requirements.
Image tag Copied
Tags follow the Netprobe Router package version and image revision:
<package-version>-r<revision>
For example:
docker.itrsgroup.com/netprobe-router:1.0.0-r0
By default, the chart pulls docker.itrsgroup.com/netprobe-router:<appVersion>-r0.
Paths the chart uses Copied
The image contains other package files. The chart uses these paths:
| Path | Use |
|---|---|
/app/collection-agent.jar |
Collection Agent executable packaged in the image |
/app/plugins |
Router and Geneos workflow plugins packaged in the image |
/app/config/config.yml |
Configuration file you provide. It is not in the image. |
/app/data |
Mount point for the persistence volume |
The process defaults to the unprivileged java user with user ID (UID) and group ID (GID) 10000.
Ports and health Copied
The image declares:
| Port | Purpose |
|---|---|
4317/tcp |
Routing ingress port |
8080/tcp |
Health-probe port |
The mounted configuration determines which collectors and probes listen. Declaring a port does not enable it.
Kubernetes uses the probes in the workload definition. If the mounted configuration disables the health probe, set healthProbe.enabled=false. If the mounted configuration uses another port, set healthProbe.port to the same value as listenPort in config.yml. A mismatch causes the chart to reject the installation. For the packaged example, see Edit the router configuration.
Persistence Copied
Container storage is ephemeral. Configuring a store directory does not make it persistent.
For store-and-forward, or any workflow state that must survive replacement, configure the router to use /app/data. When persistence.enabled is true, the chart mounts a persistent volume claim (PVC) there. A PVC does not inherit the image permissions on /app/data. The chart sets podSecurityContext.fsGroup to 10000 so that the volume is group-writable. securityContext only sets runAsUser and runAsGroup. The application under /app remains readable if securityContext.runAsUser is not 10000.
Do not configure two router instances to write to the same store. Store locking protects against accidental sharing. It is not a clustered persistence mechanism. With persistence enabled, the chart creates one PVC per pod, and each replica has its own store.
The package default maxFileLength can preallocate substantial disk space. Store-and-forward opens one store per data kind for each destination. Choose maxFileLength and capacity in your configuration according to the expected batch size, the retention requirement, and the available storage.
Memory Copied
Set an explicit memory limit in production. The chart caps the JVM maximum heap at heapSizePercent percent of that limit (default "75.0"). Do not set JDK_JAVA_OPTIONS. The chart already sets that variable, and a duplicate can drop its flags.
Configuration overview Copied
| Item | Behavior |
|---|---|
| Naming | Resources are named from the Helm release name, the first argument of helm install. helm install my-router produces my-router-netprobe-router. If the release name already contains netprobe-router, the chart uses the release name alone. A name longer than 54 characters is shortened to 54 characters and given a hash suffix. fullnameOverride and nameOverride (commented out in values.yaml) rename resources. Set these values only at installation. nameOverride changes the immutable selector. Renaming a release that uses persistence leaves its PVCs behind. |
| Config | Required --set-file config.file=.... Optional --set-file config.logback=.... |
| Workload | Deployment by default. StatefulSet when persistence.enabled=true. |
| Service | ClusterIP on port When |
| Ingress | Optional TLS Ingress. You provide the gRPC annotations. |
| Pod extensions | extraVolumes, extraVolumeMounts, initContainers, hostAliases, and dnsConfig pass standard Kubernetes pod settings through. For example, use them to mount a corporate certificate authority (CA) bundle from a ConfigMap. |
| Disruption budget | Optional PodDisruptionBudget (podDisruptionBudget.enabled, disabled by default). Limits how many pods a node drain evicts at once. |
| Ephemeral storage | /tmp emptyDir limited to tmpSizeLimit (default 256Mi). Optional persistence.emptyDirSizeLimit for the data emptyDir when persistence is disabled. |
Senders connect to the Service, or to the Ingress hostname, not to individual pods. Changing persistence.enabled switches between a Deployment and a StatefulSet. That change is disruptive. For more information, see Change persistence mode.
To see all chart default options:
helm show values itrs/netprobe-router
helm show values lists chart defaults only. To obtain the packaged example files, extract the chart.
Example installation Copied
This example shows inbound TLS gRPC on the collector and outbound TLS gRPC to two ITRS Analytics ingestion endpoints, with store-and-forward on disk and ingestion passwords from Secrets.
Example values.yaml and router-config.yml files are included in the Helm chart under examples/. Treat them as templates. Replace placeholder hostnames (*.example.com), Ingress class and annotations, Secret names, and certificate paths before installation.
Obtain and edit the example files Copied
Unpack the published chart with helm pull --untar. helm show values cannot list these files.
helm pull itrs/netprobe-router --untar --untardir ./netprobe-router-chart
The examples are under ./netprobe-router-chart/netprobe-router/examples/.
Copy both files to a writable location:
cp ./netprobe-router-chart/netprobe-router/examples/values.yaml ./my-router-values.yaml
cp ./netprobe-router-chart/netprobe-router/examples/router-config.yml ./my-router-config.yml
Then edit both files. Keep the values and the configuration aligned. Secret names and keys in my-router-values.yaml must match the Secrets you create. ${env:...} expressions and mount paths in my-router-config.yml must match secretEnv and secretMounts.
Edit the values file Copied
-
Service name — the example sets
service.name: netprobe-routerso that Gateways keep the same DNS name regardless of the release name. Change it to match the DNS name you will give Gateways. Only one release per namespace can use a fixed name. A second release in the same namespace (for example, one per tenant or region) conflicts with that name. Removeservice.name(the default Service name is the resource name in Configuration overview) or use a different name. -
Persistence — leave
persistence.enabled: trueto keep store-and-forward on disk (StatefulSet). Setsize, and optionallystorageClassName, for your cluster. Setenabled: falseonly if you also disable store-and-forward in the configuration (Deployment with ephemeral storage). -
secretRevision— set any non-empty string, for example"1". Change it to a new value on every later Secret rotation when you runhelm upgrade. -
secretEnv— for each ingestion credential, setsecretandkeyto your Kubernetes Secret name and key. Change the environment variablenameonly if you also change the matching${env:...}expression in the configuration. Remove the second pair (IAX_B_*) if you have a single destination. -
secretMounts— setsecretto your TLS and trust Secrets. KeepmountPathin sync with the paths in the configuration (/secrets/router-tlsand/secrets/iaxin the example). -
Ingress — set
hostnameto the DNS name senders will use. SetingressClassNamefor your controller. Settls.secretNameto the Ingress TLS Secret for that hostname. Clients see the certificate in that Secret, and it is often from a public CA. With cert-manager, name the Secret that cert-manager should create (it does not need to exist yet) and add the issuer annotation underingress.annotations.tls.secretNameis optional. When it is empty, the Ingress has notls:block. Setenabled: falseand remove the block if you only need in-cluster ClusterIP access.The NGINX
backend-protocolannotation is the Ingress-to-pod hop, not the certificate that clients see.GRPCS(packaged example) — the collector hastls.certFileandtls.keyFile.GRPC— TLS ends at the Ingress. Omit collectortls:(the same pattern as ITRS Analytics ingestion).
Do not combine
GRPCwith collector TLS, orGRPCSwith a plaintext collector.
Edit the router configuration Copied
- Downstream reporters — replace
ingest-a.example.comandingest-b.example.comwith your ITRS Analytics (or other) ingestion hosts and ports. Remove the second reporter and its entry underroutingWorkflow.routesif you have one destination. - Credentials — keep
usernameandpasswordas${env:IAX_A_USERNAME}(andIAX_B_USERNAME) unless you renamed those environment variables in the values file. - Outbound TLS — point
tls.trustChainFileat the mounted CA path (in the example,/secrets/iax/ca.crt). KeepusePlainText: falsefor TLS ingestion. - Store-and-forward — leave
directory: /app/dataandworkflow.storeDirectory: /app/datawhen persistence is enabled. If you changepersistence.mountPath, every store directory must be inside it. Otherwise, the chart rejects the installation. KeepmaxFileLength(64MiB in the example) unless you have calculated store sizes for your PVC. DisablestoreAndForward, or remove it, if you disabled persistence. - Collector — keep
pluginDirectory: /app/plugins(the container path, not./plugins). The packaged example usesport: ${env:ROUTING_PORT}, which the chart sets fromservice.port. A numeric default such as${env:ROUTING_PORT:4317}is also accepted. If you write a literal port instead, it must equalservice.port. Otherwise, the chart rejects the installation. The packaged example enables collector TLS (certFileandkeyFileat/secrets/router-tls/...), which requires Ingressbackend-protocol: GRPCS. To terminate TLS only at the Ingress, remove the collectortls:block and useGRPCinstead. - Health probe — the packaged example uses
listenPort: ${env:HEALTH_PROBE_PORT}, which the chart sets fromhealthProbe.port. A numeric default such as${env:HEALTH_PROBE_PORT:8080}is also accepted. A literallistenPortmust equalhealthProbe.port. Otherwise, the chart rejects the installation.
The chart does not edit your configuration. The ConfigMap keeps the text ${env:ROUTING_PORT} (or ${env:HEALTH_PROBE_PORT:8080}), and the router substitutes the value at startup from the environment variable the chart sets from service.port or healthProbe.port. The pod always has both variables set, so a default such as :8080 is never used inside the chart.
Files mounted from Secrets must be readable by the runtime user (UID 10000 by default). Do not put credentials in the image, in configuration that you commit to source control, or on the command line.
Create Secrets Copied
Create Kubernetes Secrets that match the names and keys in your edited values. The packaged examples use these placeholders:
kubectl create secret tls router-tls \
--cert=router.crt --key=router.key
kubectl create secret generic iax-trust-chain \
--from-file=ca.crt=iax-ca.pem
kubectl create secret generic iax-a-credentials \
--from-literal=username=<USER_A> --from-literal=password=<PASSWORD_A>
kubectl create secret generic iax-b-credentials \
--from-literal=username=<USER_B> --from-literal=password=<PASSWORD_B>
Install the chart Copied
Pass your edited copies. The paths in the following command are illustrative.
helm install my-router itrs/netprobe-router \
-f ./my-router-values.yaml \
--set-file config.file=./my-router-config.yml
With persistence.enabled (as in the example values), that command deploys a StatefulSet with one PVC per pod at /app/data. Each replica has its own store. PVCs are kept on uninstall. Set persistence.storageClassName to use a storage class other than the cluster default.
In-cluster senders Copied
Gateways and Netprobes in the cluster can publish to:
<service-name>.<namespace>.svc.cluster.local:<service.port>
<service-name> is service.name when that value is set. Otherwise, it is the resource name from Configuration overview. <service.port> is service.port (default 4317). The packaged example sets service.name: netprobe-router, so the address is netprobe-router.<namespace>.svc.cluster.local:4317.
Use TLS that matches the collector settings in config.yml.
External senders Copied
ClusterIP is not reachable from outside Kubernetes. Enable a standard Ingress in your values (see the packaged example). The chart does not set gRPC backend annotations for you.
Point Gateways at your Ingress hostname, for example:
router.example.com:443
Clients always see the certificate in the Ingress TLS Secret (ingress.tls.secretName, when set). That can be the same public CA as other Ingresses on the platform. Separately, NGINX must match how the collector is configured:
| Collector TLS in config.yml | NGINX backend-protocol |
|---|---|
tls.certFile and tls.keyFile set (packaged example) |
"GRPCS" |
No collector tls: (TLS ends at the Ingress) |
"GRPC" |
| Controller | Typical annotation |
|---|---|
| NGINX Ingress | nginx.ingress.kubernetes.io/backend-protocol: "GRPC" or "GRPCS" |
| AWS ALB | alb.ingress.kubernetes.io/backend-protocol-version: GRPC |
Kubernetes does not restart pods when referenced Secrets change. On every credential or certificate rotation, change secretRevision and run helm upgrade. The checksum annotation rolls all router pods. The revision value does not need to be confidential, and it must not contain credential material.
Logging Copied
By default, the router uses the Logback configuration packaged inside the Collection Agent executable and writes logs to standard output.
To use a custom Logback configuration, provide the file when you install the chart:
helm install my-router itrs/netprobe-router \
-f ./my-router-values.yaml \
--set-file config.file=./my-router-config.yml \
--set-file config.logback=./logback.xml
Private registries and air-gapped installations Copied
By default, the chart pulls docker.itrsgroup.com/netprobe-router:<appVersion>-r0 by using the itrsdocker image pull Secret from Prerequisites. If that Secret does not exist in the namespace, the pod stays in ImagePullBackOff.
Image pull Secrets Copied
Set imagePullSecrets in your values file:
# Use your own registry Secret:
imagePullSecrets:
- name: my-registry-creds
# Or none, when the nodes already authenticate to the registry or the registry is public:
imagePullSecrets: []
Mirrored image Copied
Copy the image to your registry, and then point the chart at it. image.tag defaults to <appVersion>-r0, so set it if your mirror uses another tag.
docker pull docker.itrsgroup.com/netprobe-router:<TAG>
docker tag docker.itrsgroup.com/netprobe-router:<TAG> my-registry.internal/itrs/netprobe-router:<TAG>
docker push my-registry.internal/itrs/netprobe-router:<TAG>
helm install my-router ./netprobe-router-<VERSION>.tgz \
-f ./my-router-values.yaml \
--set-file config.file=./my-router-config.yml \
--set image.repository=my-registry.internal/itrs/netprobe-router \
--set image.tag=<TAG> \
--set 'imagePullSecrets[0].name=my-registry-creds'
Offline chart Copied
On a connected machine, download the chart and transfer the .tgz to the installation environment. Its itrs-common dependency is bundled, so the installation does not need network access to the chart repository.
helm pull itrs/netprobe-router --version <VERSION>
This command writes netprobe-router-<VERSION>.tgz.
Upgrade Copied
Use helm upgrade to update an installed release. Provide --set-file on every upgrade. Change the persistence mode only when you must, because that change replaces the workload.
Change persistence mode Copied
Choose the persistence mode when you first install the chart, and avoid changing it on a live release. persistence.enabled changes the workload resource:
false: Deployment with ephemeralemptyDirstorage.true: StatefulSet with one retained PVC per replica.
Kubernetes cannot convert a Deployment into a StatefulSet in place. Helm removes one workload and creates the other. Pods from both workloads might briefly coexist because they share the same Service selector. Routing might be unavailable during the replacement. The chart does not migrate store-and-forward data.
If the mode must change:
- Schedule a maintenance window, and stop or redirect senders.
- Scale the current Deployment or StatefulSet to zero.
- When you enable persistence, confirm that
config.ymluses store-and-forward under/app/data. - Run
helm upgradewith the newpersistence.enabledvalue, and provide--set-file config.file=...again. For more information, see Update the release. - Verify the new pods and the routing endpoint before you restore sender traffic.
When you disable persistence, archive any required store-and-forward data first. StatefulSet PVCs are retained. Delete them manually when you no longer need them.
Update the release Copied
Provide --set-file again. Helm does not keep file contents from the previous command.
helm upgrade my-router itrs/netprobe-router \
-f ./my-router-values.yaml \
--set-file config.file=./my-router-config.yml
Changing config.file or config.logback updates a checksum annotation, so pods roll and pick up the new files.
Troubleshooting Copied
Use the following checks to troubleshoot Helm installation and Kubernetes deployment problems.
| Symptom | Check |
|---|---|
| Container exits immediately | Confirm that a readable file is passed with --set-file config.file and is mounted at /app/config/config.yml. |
| Plugin class cannot be loaded | Confirm that pluginDirectory is /app/plugins and that the configuration matches the Netprobe Router version in the image tag. |
| Health check remains unhealthy | Confirm that the configuration enables the health probe, and that listenPort equals healthProbe.port (8080 in the packaged example). |
| Store-and-forward state disappears | Confirm that persistence is enabled and that the store directory is /app/data. A path inside the container is not persistent. |
| Store cannot be opened | Confirm that /app/data is writable by the runtime user and is not shared with another router. |
| TLS file is missing or unreadable | Confirm that the Secret is mounted at the configured path and is readable by the runtime user (UID 10000 by default). |
Pod stays in ImagePullBackOff |
Confirm that the itrsdocker image pull Secret exists in the namespace, or set imagePullSecrets. For more information, see Private registries and air-gapped installations. |
When you need more detail, follow the Collection Agent logging guidance for the packaged version and provide a custom Logback configuration with --set-file config.logback=....