The Perspective module becomes faulted immediately after a Docker-based Ignition Maker gateway updates to 8.1.25 (b2023021409). The startup log reports java.lang.NoClassDefFoundError: org/eclipse/jetty/websocket/servlet/WebSocketServlet, followed by java.lang.ClassNotFoundException. Follow the class-loading path before investigating ports, browsers, certificates, or WebSocket traffic: the failure occurs inside the gateway while the module runs setup(), before a Perspective client can establish a session.
Where does the Perspective startup path stop?
The container image supplies the gateway runtime, its core libraries, and the bundled module versions intended to run with that runtime. A mapped volume can replace the image's visible directory tree at the mount target. If the entire modules directory is mapped, the gateway sees the module files stored in the volume instead of the matching files supplied by the updated image.
During startup, the gateway module manager discovers the visible Perspective module and creates its module class loader. The call path then reaches com.inductiveautomation.perspective.gateway.GatewayHook.setup. That code attempts to resolve org.eclipse.jetty.websocket.servlet.WebSocketServlet, but the class is unavailable to the loaded module. Java first reports the unresolved class as ClassNotFoundException; the dependent module initialization then fails with NoClassDefFoundError.
| Path stage | Expected source | Observed result |
|---|---|---|
| Container filesystem | Gateway and bundled modules from the same image release | A volume-mounted modules directory can hide the image's bundled files |
| Module discovery | Perspective module matched to the gateway runtime | The gateway can discover an older persisted module file |
| Class loading | Module references classes available in the runtime |
WebSocketServlet cannot be resolved |
| Module setup |
GatewayHook.setup completes |
Perspective is marked faulted |
| Client connection | Perspective session proceeds to HTTP and WebSocket handling | This stage is never reached |
Which symptoms distinguish a module mismatch?
The decisive symptom is not merely a faulted module. It is a bundled module failing during setup() because a runtime class cannot be found immediately after the container image changes. That combination points to an incompatible set of module and gateway files.
| Symptom | Likely layer | Diagnostic implication |
|---|---|---|
NoClassDefFoundError for org/eclipse/jetty/websocket/servlet/WebSocketServlet
|
Java class path and module compatibility | The loaded Perspective module expects a class absent from its effective runtime |
Failure originates at GatewayHook.setup
|
Gateway module initialization | No client request, network route, or WebSocket handshake is involved yet |
Problem appears with 8.1.25 (b2023021409)
|
Container update boundary | Compare image-supplied files with files retained by volumes |
| Perspective module exists in a mapped modules directory | Container filesystem overlay | The mount can replace the compatible module bundled in the image |
Rollback reports a volume at 8.1.25 with an 8.1.24 image |
Persisted gateway state | The older image refuses to open state already advanced by the newer release |
A missing browser connection, proxy error, or closed port would occur later in the path. Changing firewall rules cannot add a Java class to the module class loader. Layer one for this incident is the effective container filesystem: determine which module file the mount exposes.
How do the available module-mount approaches compare?
Two deployment patterns matter. Mapping the complete modules directory persists everything in that directory, including first-party modules that should normally advance with the gateway image. Mapping only individual third-party module files preserves the image's release-matched bundled modules while still automating installation of external additions.
| Approach | Bundled module ownership | Upgrade behavior | Mismatch risk | Recommendation |
|---|---|---|---|---|
| Map the entire modules directory | The volume overrides the directory delivered by the image | Persisted files can survive while the gateway runtime advances | High when a bundled module depends on runtime libraries changed by the update | Remove this mapping |
| Map individual third-party module files | The image continues to supply bundled modules | Gateway and bundled modules advance together | Limited to compatibility of each separately managed third-party module | Preferred when external modules must be injected |
| Use image-supplied bundled modules without a modules mount | The image owns the complete bundled set | The release remains internally aligned | Lowest for first-party module skew | Preferred when no third-party module injection is required |
Use the image as the version boundary for Perspective and other bundled components. Treat third-party modules as individually managed artifacts. This separates gateway data persistence from executable module ownership and prevents a volume from silently freezing an older bundled module.
Which Compose settings affect this fault?
The reported container starts from inductiveautomation/ignition:latest with the following additional arguments:
-n OSCR
-m 1024
--
wrapper.java.initmemory=512
-Dignition.allowunsignedmodules=true
| Setting | Function in the reported deployment | Effect on this failure |
|---|---|---|
inductiveautomation/ignition:latest |
Selects a moving image reference | Allows a container recreation or pull to cross a release boundary |
-n OSCR |
Supplies the configured gateway name | Does not control Java dependency resolution |
-m 1024 |
Supplies the configured memory value | Increasing it will not provide the missing Jetty class |
wrapper.java.initmemory=512 |
Sets initial JVM memory | Not a correction for ClassNotFoundException
|
-Dignition.allowunsignedmodules=true |
Allows unsigned modules under the configured policy | Signature acceptance does not repair binary or class-path incompatibility |
| Whole modules-directory mapping | Persists and overlays the module directory | Direct cause of the stale bundled Perspective module remaining visible |
The mutable latest reference also makes the update boundary less explicit. For controlled maintenance, select an explicit image version supported by the deployment process, validate it against a copy of the persisted gateway state, and then promote it. The immediate repair, however, is to correct module ownership rather than tune JVM memory or unsigned-module policy.
How should the module mapping be corrected?
- Stop the gateway container so the effective filesystem cannot change while the deployment definition is edited.
- Inspect every Compose volume declaration whose target is the gateway's modules directory. Identify whether the complete directory is mapped from a host directory or named volume.
- Inventory files in that mapped source. Separate third-party modules intentionally supplied by the deployment from bundled modules, including the persisted Perspective module.
- Remove the whole-directory mapping from the Compose definition. Do not carry the old Perspective module into the corrected deployment.
- If third-party modules must remain automated, map their individual module files into the modules directory. Keep each mapping narrow enough that it cannot conceal the rest of the image-supplied directory.
- Recreate the container using the
8.1.25 (b2023021409)runtime or a later deliberately selected image compatible with the persisted gateway state. Recreating is necessary because merely restarting an existing container does not change its mount definition. - Allow the gateway to load the Perspective module delivered with the selected image. Watch module initialization from discovery through
GatewayHook.setup.
Preserve the gateway's data volume throughout this change. The target is the incorrect executable-module overlay, not project configuration or gateway state. Before removing any stored module file, verify that it belongs to the mapped modules source and that a recoverable copy exists if it is a separately licensed or third-party artifact.
Why is an in-place rollback to 8.1.24 rejected?
The rollback attempt reports:
ERROR: Version mismatch on existing volume (8.1.25) versus image (8.1.24), Ignition image version must be greater or equal to volume version.
The persisted volume has already been opened or marked by 8.1.25. The 8.1.24 image is older than that stored state, so the startup guard rejects the combination. This is separate from the Perspective class-loading fault: one concerns persisted gateway-state compatibility, while the other concerns module files hidden by a mount.
| Recovery path | Required state | Use in this case |
|---|---|---|
Run 8.1.25 with corrected module mapping |
Current 8.1.25 volume |
Recommended; fixes the identified cause without downgrading stored state |
Run 8.1.24
|
A gateway volume or backup from before it was advanced to 8.1.25
|
Use only as a true rollback with matching older state |
Run 8.1.24 against the current volume |
Current 8.1.25 volume |
Rejected by the version check |
Do not bypass the guard by manually editing version markers. Stored schemas and serialized state can change across versions, and a marker edit does not reverse those changes. If rollback is mandatory, restore the complete pre-update state into a separate deployment and pair it with the matching image. For this incident, staying on 8.1.25 and exposing its bundled Perspective module is the shorter and safer path.
How is the corrected data path verified?
- Confirm the recreated container no longer mounts a volume over the complete modules directory.
- Confirm every remaining module mount targets only an intended third-party module file.
- Open the gateway module status and verify that Perspective is running rather than faulted.
- Search the current startup log for
org/eclipse/jetty/websocket/servlet/WebSocketServlet,NoClassDefFoundError, andClassNotFoundException. None should recur during Perspective setup. - Confirm startup proceeds beyond
com.inductiveautomation.perspective.gateway.GatewayHook.setupwithout the module manager marking Perspective faulted. - Launch a Perspective session. This extends verification beyond class loading: the browser must reach the gateway, load the application, and maintain the live session transport.
- Restart the recreated container once more and repeat the module-status, startup-log, and session checks. This proves the correction resides in the deployment definition rather than in a one-time container state.
FAQ
Can I fix the Perspective fault by increasing Docker memory?
No. The configured -m 1024 and wrapper.java.initmemory=512 values do not supply the missing WebSocketServlet class. Correct the modules-directory mapping.
Does allowing unsigned modules fix NoClassDefFoundError?
No. -Dignition.allowunsignedmodules=true changes module acceptance policy, not binary compatibility or Java class availability.
Can I run Ignition 8.1.24 with a volume upgraded to 8.1.25?
No. Startup rejects that pairing because the image version must be greater than or equal to the existing volume version. Restore pre-8.1.25 state if a true rollback is required.
Does mounting one third-party module hide bundled modules?
A narrow individual-file mapping leaves the other image-supplied module files visible. A mapping over the complete modules directory can hide the bundled set and preserve incompatible versions.
Can I consider the repair complete when Perspective is running?
Verify it survives container recreation and restart: Perspective must remain running, the missing-class exceptions must stay absent from the new startup log, and a Perspective session must load and maintain its live connection.