Skip to content

Configuration ​

On a commissioned boat, nobody configures this daemon by hand. What the navigation page shows — whether there is an engine page at all, whether AIS targets appear on the chart — was decided when the boat was commissioned, written into one file, and the daemon reads it at startup.

The crew-visible consequence is simple: if a page is empty, the module behind it was probably never turned on. That is a configuration question, not a fault.

The rest of this chapter is for whoever does the turning on.

Three layers, in this order ​

The effective configuration is resolved once, at startup, from three layers:

  1. Built-in defaults. Every report module off; navigation and motor in 1 Hz mode; AIS track depth 10; route waypoint limit 500; wind damping 4; MQTT on, pointing at 127.0.0.1:1883, prefix nmea.
  2. The deploy configuration, /etc/muxen/deploy.json, written at commissioning. Overrides the defaults.
  3. The command line. Overrides both.

--report-all is applied after all three and forces every report module on, so it always wins.

There is deliberately no fourth layer: the daemon reads no environment variables of its own beyond systemd's RUNTIME_DIRECTORY and STATE_DIRECTORY. Environment files exist, but they feed the systemd unit's command line, not the daemon.

The deploy configuration ​

/etc/muxen/deploy.json is the boat's commissioning file, shared by every MUXEN service. It is a project export, not a file written for this daemon, so muxen-nmea2000 reads only one device out of it: the NavigationInstruments device (function code 12) at instance 0, which is device id 12 × 64 = 768. That device's parameters array carries the settings below.

ParameterTypeEquivalent flagEffect
ReportNavbool--report-navpublish nmea/navigation
NavHighSpeedbool--nav-high-speed10 Hz instead of 1 Hz
WindDampingint 0–9--wind-damping=Nwind smoothing, 0 = off
ReportMotorbool--report-motorpublish nmea/motor
MotorHighSpeedbool--motor-high-speed10 Hz instead of 1 Hz
ReportThrusterbool--report-thrusterpublish nmea/thruster
ReportAisbool--report-aispublish nmea/ais
AisTrackDepthint--ais-track-depth=Ntrack points kept per target
ReportRoutebool--report-routepublish nmea/route
RouteMaxWaypointsint--route-max-waypoints=Nwaypoints kept per route
DatetimeOffsetnmea or auto--datetime-offset=how local time is derived

Booleans are stored as the strings "0" and "1" ("true" and "false" are also accepted, case-insensitively). A value that parses as neither is ignored with a warning naming the parameter:

Deploy: ReportMotor has invalid boolean value 'yes', ignoring

An absent parameter means "keep the built-in default". Unknown parameters are ignored.

Nothing in that list is required. All three of these are valid and none is an error:

  • the file does not exist — Deploy: /etc/muxen/deploy.json not found, skipping deploy layer
  • the file is {}, or has no NavigationInstruments device — Deploy: no NavigationInstruments device in …, using defaults
  • the file exists but the device carries none of the parameters above

Two flags change where this layer comes from:

sh
muxen-nmea2000 --deploy-config /path/to/other.json   # read a different file
muxen-nmea2000 --no-deploy-config                    # skip the layer entirely

--no-deploy-config is what the templated unit uses, so a maintenance instance can never pick up the commissioned settings (More than one bus).

Precedence, worked through ​

deploy.jsoncommand lineeffective
absentnothingbuilt-in default
ReportMotor=1nothingmotor on
ReportMotor=0nothingmotor off
ReportMotor=0--report-motormotor on — the CLI wins
ReportMotor=1--report-allmotor on
ReportMotor=0--report-allmotor on — --report-all is applied last

One edge to know about. GLib's option parser does not report which options were actually present on the command line, so the daemon snapshots the affected fields immediately before parsing and treats any field that changed as "set by the CLI". A numeric option explicitly set to its own default — --ais-track-depth=10, say — is therefore indistinguishable from "not given", and the deploy layer can still override it. Deploys that carry explicit values are the normal case, so this is accepted rather than worked around.

Changing the unit's command line ​

muxen-nmea2000.service interpolates exactly two variables, both set in the unit itself:

ini
Environment="MUXEN_NMEA_INTERFACE=can1"
Environment="MUXEN_NMEA_NAME=0x11223344FF467788"

The unit's ExecStart is:

/usr/bin/muxen-nmea2000 --interface=${MUXEN_NMEA_INTERFACE} \
                        --name=${MUXEN_NMEA_NAME} --enable-alarms

Changing either of those, or anything else — extra report flags, a different MQTT host, verbosity — means a systemd drop-in:

sh
sudo systemctl edit muxen-nmea2000
ini
[Service]
ExecStart=
ExecStart=/usr/bin/muxen-nmea2000 --interface=${MUXEN_NMEA_INTERFACE} \
    --name=${MUXEN_NMEA_NAME} --enable-alarms --report-nav --report-ais

The empty ExecStart= is required: without it systemd appends rather than replaces.

The shared /etc/muxen/env was removed from every MUXEN unit on 2026-09-01; overrides are drop-ins now. The per-instance /etc/muxen/env.<instance> is a different mechanism and is unaffected — it uses different variable names, so the primary daemon's NAME can never leak into an instance.

Which GPS wins when there are two of them is a separate file with its own lifecycle, edited with muxen-nmea2000-config rather than by hand. It is covered in Navigation.

Its default location depends on how the daemon was started:

Started asDefault path
a systemd service (STATE_DIRECTORY set)$STATE_DIRECTORY/nav-sources.json
root, outside systemd/var/lib/muxen-nmea2000/nav-sources.json
an ordinary user$XDG_CONFIG_HOME/muxen-nmea2000/nav-sources.json

--nav-config PATH overrides all three.

Reloading without a restart ​

Navigation source configuration is re-read on SIGHUP. Nothing else is: report modules, the interface, the NAME and the MQTT settings are fixed for the life of the process.

sh
sudo systemctl reload muxen-nmea2000        # ExecReload=/bin/kill -HUP $MAINPID

On SIGHUP the daemon logs Received SIGHUP, reloading configuration…, reloads the navigation source file, and republishes nmea/config.

A change to /etc/muxen/deploy.json needs a real restart. In normal MUXEN operation you get one for free: the package installs a muxen-restart-target trigger and the unit is PartOf=muxen.target, so a deployment restarts the target and the daemon with it. Editing the file by another route needs systemctl restart muxen-nmea2000.

Reading back what took effect ​

The daemon publishes its resolved configuration to nmea/config as a retained message, once when MQTT connects, again once all four virtual devices have claimed addresses, and again on every SIGHUP:

sh
mosquitto_sub -h 127.0.0.1 -t 'nmea/config' -v

It carries the state of every module, the AIS track depth, the route waypoint limit, the datetime offset source, the GPSD settings, the configured navigation sources with the bus address each currently resolves to, the topic prefix and the daemon version. Its expireAfterSec is 3124137600 — about 99 years — because it is a statement of configuration, not a reading.

That topic is the answer to "did my change take?". The journal's one-line summary at startup is the quick version of the same thing:

Config: can1 NAME=0x11223344FF467788 addr=0x80 (100-200) MQTT Alarms Nav AIS DateTime

Integration of multiplexed solutions
MUXEN and the MUXEN logo are trademarks of MUXEN SAS.