Why Does Ignition Show Gateway Startup Issue on macOS?

Daniel Price10 min read
B&R AutomationHMI / SCADATroubleshooting
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Follow the packet. The installer ends by checking whether the gateway it just started is reachable. The browser, when you point it at the gateway, makes its own HTTP request to the same listener. When the wrapper log shows Jetty bound to 0.0.0.0:8088 but the installer reports Gateway Startup Issue – Unable to start the gateway, the gateway is up. The failure is in the check. This reference covers Ignition 8.0.16 on macOS Catalina 10.15.6, installed to /usr/local/ignition on a company-managed MacBook Pro. In that case, entering localhost:8088 directly in the browser reached the gateway and cleared the problem.

What request does the installer make, and where does it stop?

When installation finishes, the Java Service Wrapper starts the gateway as a daemon. The log confirms this with --> Wrapper Started as Daemon. The installer's last page then decides pass or fail based on whether it can see the gateway running. On failure it shows the Gateway Startup Issue page with an Open Logs button that opens the wrapper log.

The data path has three hops, all on the same machine:

Hop Component Evidence it worked
1. Service start Java Service Wrapper Standard Edition 64-bit 3.5.42, running as a daemon Wrapper Started as Daemon, then Launching a JVM
2. Application start Gateway JVM (11.0.7+10-LTS) with embedded Jetty 9.4.24.v20191120 Started o.e.j.w.WebAppContext@...{Commissioning,/,...,AVAILABLE}
3. Network listener Jetty ServerConnector, HTTP/1.1 Started ServerConnector@...{HTTP/1.1,[http/1.1]}{0.0.0.0:8088}

All three hops completed. The log has no stack trace, no jvm 2 restart, and no bind error. Whatever the installer tested stopped somewhere between the installer process and the TCP listener, not inside the gateway. When the browser request to localhost:8088 succeeds, the listener is confirmed reachable over loopback and the installer verdict is a false negative.

What does the wrapper log prove, line by line?

Read the log as a startup timeline. The left timestamp column comes from the wrapper (local time). The bracketed timestamps inside gateway log lines come from the gateway's own logging pattern.

Wrapper time Event Meaning
14:44:00 Wrapper started as daemon, version 3.5.42 Service launched by the OS
14:44:02 Launching a JVM, WrapperManager: Initializing First JVM instance (jvm 1) created
14:44:04 logback.xml found at /usr/local/ignition/data/logback.xml Install directory and data folder are readable
14:44:09–14:44:12 Appenders built: SysoutAppender, SQLiteAppender named DB, async wrappers SysoutAsync and DBAsync; SQLite JDBC 3.23.1 Log database created and writable
14:44:12 W [WebResourceManagerImpl] metro keystore alias fallback to metro-key Warning only; default applied
14:44:13 WebServerManager setup, property defaults, Setup complete Web server configuration resolved
14:44:13 Commissioning State updated from commissioning to needs_commissioning Fresh install waiting for first-run setup
14:44:14 Commissioning web app AVAILABLE at / Setup wizard is being served
14:44:14 Connector on 0.0.0.0:8088, Started @11704ms Listening on all IPv4 interfaces, port 8088

The@11704msfigure is JVM uptime when Jetty finished starting. Most of that time went to building the SQLite appender, from about 14:44:09.598 to 14:44:12.036. A first boot on a laptop that scans newly written files is slower than later boots.

The bracketed gateway time ([18:44:12]14:44:12). That offset is a timezone difference between the two logging paths. It is not a clock fault and not related to the startup verdict. Correlate by offset, not by assuming the two columns match.

The jvm 1 column matters. It counts JVM instances. If the wrapper had detected a crash or a hung JVM, the next lines would show jvm 2. A single jvm 1 run ending on a started connector means one clean start.

Is the gateway.metroKeystoreAlias warning the fault?

No. The full line is:

W [WebResourceManagerImpl] Unable to read "gateway.metroKeystoreAlias" gateway.xml property. Assuming metro keystore alias is: metro-key

It is the only W (warning) line in the log, so it gets blamed. Read it literally: the property is missing from gateway.xml, and the gateway substitutes the default alias metro-key. A fresh install has no custom keystore configuration, so the property is expected to be missing and the fallback is the designed behavior. The next line starts Jetty logging, and the web server completes setup within a second. A fatal keystore problem would stop the sequence with an exception. It would not be followed by a clean Setup complete and a started connector.

The WebServerManager lines have the same pattern: each property is "not set" and falls back to a default.

Property Logged default Effect on a fresh install
gateway.forceSecureRedirect false HTTP requests are not forced to HTTPS, so plain http://localhost:8088 is served
gateway.includedCipherSuites [] No cipher allow-list override
gateway.excludedCipherSuites [] No cipher deny-list override
gateway.publicAddress.autoDetect true Gateway detects its own address for client launch URLs
gateway.publicAddress.address, .httpPort, .httpsPort blank No fixed public address; relevant only behind NAT or a reverse proxy

None of these affect whether the loopback listener accepts connections. Leave gateway.xml alone.

Why would a managed Mac report failure for a running gateway?

Layer one first, even on a single host. Here layer one is the loopback interface and the listening socket. The log shows the socket open, so move up the stack to whatever sits between the installer or browser and that socket. On a company-managed laptop there are several such layers. The measurement for each is in the next sections.

  • System or PAC proxy. Corporate Macs often have a system proxy or auto-config script. If localhost or 127.0.0.1 is missing from the bypass list, local HTTP requests go to the proxy, which cannot reach your loopback address. Diagnostic: curl --noproxy works while a proxy-aware client fails.
  • Endpoint security and application firewalls. Security agents can delay or block a freshly installed Java binary from opening sockets or accepting connections. Diagnostic: lsof shows no listener even though the log says the connector started, or connections time out rather than being refused.
  • Name resolution. localhost can resolve to IPv6 ::1 before IPv4 127.0.0.1. The connector reports 0.0.0.0:8088, an IPv4 wildcard. Diagnostic: test both 127.0.0.1 and localhost explicitly.

Only the first two are consistent with a browser succeeding right after the installer failed. The Terminal checks below tell you which one applies before you change anything.

What if your wrapper.log does not end at the 8088 connector?

A log from another machine tells you nothing about yours. Before comparing symptoms, open your own wrapper.log from the logs folder under the install directory (/usr/local/ignition in this case). Confirm it is the whole file and not a truncated view from the installer page. Then match the last meaningful line to a failure class:

Where the log ends Failing hop Next action
At or before Launching a JVM Wrapper cannot start Java Read the wrapper ERROR/FATAL lines; check install directory permissions
jvm 2, jvm 3 lines appear JVM crashing or hanging; wrapper restarting it Find the last exception before each restart
Exception during logback or SQLite appender setup Data folder not writable Check ownership of /usr/local/ignition/data
Bind or "address already in use" error at connector start Port 8088 owned by another process Identify the owner with lsof; free the port or change the gateway port
Started ServerConnector ... {0.0.0.0:8088} None; gateway is up Browse to http://localhost:8088 and commission

Only the last row applies to the case documented here. If your log ends in one of the other rows, the installer page is reporting a real failure and the rest of this procedure does not apply until that hop is fixed.

Which fix path should you take?

Four responses are common. Compare them on whether they address the hop that actually failed.

Approach Targets the failed hop? Cost Risk Outcome in this case
Add gateway.metroKeystoreAlias to gateway.xml No; the warning is a handled fallback Low Hand-editing config on a fresh install can create a real parse failure No effect on the installer check
Uninstall and reinstall No; repeats the same start sequence and check Medium Low, but first-boot timing repeats and so does the verdict Likely the same screen
Browse directly to http://localhost:8088 and commission Yes; tests the loopback HTTP path directly Minimal None Worked
Change host networking (proxy bypass, security exception) Only if the browser request also fails Needs IT involvement on managed hardware Policy changes on a company machine Not needed once the browser connected

Recommendation: dismiss the installer page, confirm the listener from Terminal, and commission through the browser. Escalate to host networking changes only if Terminal shows a listener but no local client can get an HTTP response.

How do you confirm the listener and the loopback path from Terminal?

Work from the socket outward so each step tests one more hop.

  1. Confirm the socket is open and owned by the gateway JVM:
    lsof -nP -iTCP:8088 -sTCP:LISTEN
    Expect a java process in LISTEN state on port 8088. No output means nothing is listening. Go back to the log table above. The listener may take a moment on first boot, so re-run it before concluding.
  2. Request the page over IPv4 loopback, bypassing any proxy:
    
    Any HTTP status line (2xx or 3xx) proves Jetty answered, and a redirect toward the commissioning page is normal. Connection refused means no listener. A hang or timeout points to a local firewall or security agent.
  3. Repeat by name to test resolution:
    
    If 127.0.0.1 answers and localhost does not, the name is resolving to an address the IPv4 wildcard connector does not serve. Use  in the browser.
  4. Check the system proxy configuration:
    scutil --proxy
    If an HTTP proxy or auto-config URL is active, confirm the exceptions list includes localhost and 127.0.0.1. If curl --noproxy works but the browser does not, the proxy is intercepting loopback traffic. Ask IT to add the bypass rather than disabling the proxy yourself.
  5. Watch the log live while you make the request:
    tail -f /usr/local/ignition/logs/wrapper.log
    New lines as you browse confirm the request reaches the gateway. No new lines while the browser shows an error means the request stopped before the listener.

How do you commission the gateway from the browser?

The log line Commissioning State updated from commissioning to needs_commissioning means the gateway is serving only the first-run wizard at /. Until you complete it, the full gateway does not start.

  1. Close the installer's Gateway Startup Issue page. The gateway runs as a daemon under the wrapper and keeps running when the installer closes.
  2. Open http://localhost:8088, or if the name test failed. Use plain HTTP. With gateway.forceSecureRedirect defaulting to false, there is no forced HTTPS redirect at this stage.
  3. Step through the commissioning wizard: accept the license, create the initial administrator account, and confirm the HTTP and HTTPS ports. Keep HTTP on 8088 unless something else on the machine needs that port.
  4. Start the gateway from the wizard's final step and keep the page open while it moves from commissioning to normal startup.
  5. Keep tail -f on wrapper.log running during this step. The commissioning state line updates again and module startup messages follow. Warnings about unset optional properties at this stage are the same kind of defaults described above.

If the wizard page loads but submitting it fails, inspect the wrapper.log lines written at the moment of submission. A failure there is a gateway-side error with its own exception, not a network-path issue.

How do you verify the gateway after commissioning?

A single successful page load proves the path once. Verification proves it survives a restart, because the daemon has to come up again without the installer.

  1. Refresh http://localhost:8088. The gateway web interface home page should load instead of the commissioning wizard. Log in with the administrator account created during commissioning.
  2. Re-run lsof -nP -iTCP:8088 -sTCP:LISTEN and confirm the java listener is still present. If you configured an HTTPS port during commissioning, run the same command against that port.
  3. Scan wrapper.log since commissioning for any jvm 2 lines. Their absence means the JVM did not crash during the transition out of commissioning.
  4. Confirm the wrapper is registered with launchd so it starts on boot:
    sudo launchctl list | grep -i ignition
    A matching entry means the daemon is loaded.
  5. Reboot the Mac. Wait for the log to show a new Started ServerConnector ... {0.0.0.0:8088} line, then run and load http://localhost:8088 in the browser. An HTTP status line from curl and the gateway home page (not the commissioning wizard) in the browser confirm the gateway starts on its own and is reachable over loopback after a cold boot.

FAQ

What happens if I ignore the gateway.metroKeystoreAlias warning in the Ignition wrapper log?

Nothing breaks. The gateway uses the default alias metro-key and continues. In the documented log, Jetty completed setup and opened 0.0.0.0:8088 about two seconds after that warning.

What happens if I close the installer on the Gateway Startup Issue screen?

The gateway keeps running, because the Java Service Wrapper started it as a daemon independent of the installer. Browse to http://localhost:8088 to reach the commissioning wizard.

What happens if localhost:8088 refuses the connection after install?

Run lsof -nP -iTCP:8088 -sTCP:LISTEN. If no java listener appears, open wrapper.log and look for jvm 2 restarts, a port bind error, or an exception before the ServerConnector line. If a listener exists, test with curl --noproxy '*' to rule out name resolution and proxy interception.

The wrapper column prints local time, and the bracketed gateway timestamps use a different timezone. In the documented log, 14:44:12 wrapper time matches [18:44:12] gateway time. Correlate events by that fixed offset; it does not indicate a fault.

Back to blog