Install on Linux
This page installs Mugnsoft components as Linux services (systemd, Upstart or SysV are detected automatically). One script does everything for a component: download, signature check, configuration, service installation, registration with the Webserver and start. The manual steps it automates are listed below it.
Before you start
- A 64-bit Linux host (Debian, Ubuntu, RHEL and derivatives, see requirements), and a root shell (
sudo -i). curl(orwget) andgpg:
# Debian / Ubuntu
apt-get install -y curl gnupg
# RHEL / Rocky / Alma / CentOS / Fedora
dnf install -y curl gnupg2
- The network flows below open in your firewalls. Every component listens on its own API port, and the Webserver connects back to it:
| From | To | Port (default) | Why |
|---|---|---|---|
| Users’ browsers | Webserver | TCP 9090 (PortWEB) |
Web UI |
| Monitors, Integrators, Sentinel Agents | Webserver | TCP 8050 (PortAPI) |
Registration and data push |
| Webserver | each Monitor / Integrator / Sentinel Agent | its port (packages: monitor 8072, integrator 8060, sentinel 8078) |
The Webserver drives the component |
- Install the Webserver first: every other component needs two values it produces.
- One component of each kind per host: the service names are fixed (
MugnsoftWebserver,MugnsoftMonitor,MugnsoftIntegrator,MugnsoftDiscoveryAgent,MugnsoftWebserverFront).
Your values
The commands on this page use these example values. Replace them with yours; everything else is copied as-is.
| Value | Where it comes from | Example (replace it!) |
|---|---|---|
| Webserver address | IP or DNS name of the Webserver host, as the other hosts reach it | 10.0.0.5 |
| Webserver API port | PortAPI of webserver.json (default) |
8050 |
_sharedInternal |
Printed at the end of the Webserver installation (step 1), also in webserver.json |
3f9c...e1 (64 hex characters) |
| License file | license_MNS.dat sent by Mugnsoft, one per Webserver and per Integrator, bound to its host |
./license_MNS.dat |
| Location | Free text shown on the Webserver (monitor, sentinel) | Paris |
Option A — Script (recommended)
Download mugnsoft-install.sh onto each host, then run, as root, the line for the component that host gets:
curl -fsSLO https://doc.mugnsoft.com/materials/mugnsoft-install.sh
1. Webserver (first). Put your license_MNS.dat next to the script, then:
bash mugnsoft-install.sh webserver --license ./license_MNS.dat
The end of its output gives what the other hosts need, for example:
Webserver installed in /opt/mugnsoft/webserver
Web UI: https://<this host>:9090/ (admin / admin, change it at first login)
API: <this host>:8050 -> the --webserver value for the components
_sharedInternal (pass it to every component as --shared-internal):
3f9c...e1
2. Monitor probe, Integrator, Sentinel Agent, each on its host, with your Webserver address and _sharedInternal:
# monitor probe
bash mugnsoft-install.sh monitor --webserver 10.0.0.5:8050 --shared-internal 3f9c...e1 --location Paris
# Integrator (it has its own license file)
bash mugnsoft-install.sh integrator --webserver 10.0.0.5:8050 --shared-internal 3f9c...e1 --license ./license_MNS.dat
# Sentinel Agent
bash mugnsoft-install.sh sentinel --webserver 10.0.0.5:8050 --shared-internal 3f9c...e1 --location Paris
Each run ends with ... installed ... and registered with 10.0.0.5:8050. Then enable the component.
What the script does, in order, stopping at the first problem with a message saying what to fix:
- Checks the arguments, that it runs as root and that the install folder does not exist yet (it never overwrites an installation).
- Downloads
mugnsoft-<component>-<version>.linux-amd64.tar.gzand its.sigfromhttps://mugnsoft.com/bin/<version>/, and verifies the signature with Mugnsoft’s public GPG key (in a throw-away keyring). - Extracts it to
/opt/mugnsoft/<component folder>and makes the programs executable. - Writes the values into the component’s
.jsonfile (and copies the license file). - Runs
<component> install: a monitor, Integrator or Sentinel Agent registers with the Webserver at this point, and the service is only created if that succeeds. - Starts the service. The service is enabled at boot.
| Option | Default | Use |
|---|---|---|
--version <x.y.z> |
4.2.0 |
Release to install |
--dir <path> |
/opt/mugnsoft |
Parent folder of the installation |
--package <file> |
download | Install a package you already have, .tar.gz or .zip (its .sig must sit next to it) |
--no-verify |
off | Skip the signature check (not recommended) |
--name <name> |
<component>-<short hostname> |
Component name on the Webserver: letters, digits, hyphens |
--port <port> |
the package’s | Component API port |
--advertise <ip> |
auto-detected | Monitor only: the IP the Webserver must dial to reach the probe (see Check the address) |
--webserver-url <url> |
https://<webserver host>:9090 |
Integrator only: Web UI link used in notifications |
--api-port, --web-port |
8050, 9090 |
Webserver only |
--front-pki <dir> |
— | Webserver-Front only, see Install the Webserver-Front |
bash mugnsoft-install.sh --help prints the same list.
#!/usr/bin/env bash
# mugnsoft-install.sh - download, verify, configure, install and start one Mugnsoft
# component as a Linux service (systemd, Upstart or SysV), in one command.
#
# Usage (as root):
# bash mugnsoft-install.sh <component> [options]
#
# Components:
# webserver the Webserver (needs --license)
# monitor a monitor probe
# integrator an Integrator (needs --license)
# sentinel a Sentinel Agent (discovery_agent)
# webserver-front a Webserver-Front (needs --front-pki)
#
# Options:
# --version <x.y.z> release to install (default 4.2.0)
# --dir <path> parent folder (default /opt/mugnsoft); the component
# is installed in <path>/<package folder>
# --package <file> install this package (.tar.gz or .zip) instead of downloading it
# --no-verify skip the GPG signature check (not recommended)
# webserver, integrator:
# --license <file> the license_MNS.dat received from Mugnsoft
# webserver:
# --api-port <port> API port (default 8050)
# --web-port <port> web UI port (default 9090)
# monitor, integrator, sentinel:
# --webserver <host:port> the Webserver API address (host:API port, e.g. 10.0.0.5:8050)
# --shared-internal <value> the "_sharedInternal" value of the Webserver's webserver.json
# --name <name> component name (default <component>-<short hostname>)
# --port <port> component API port (default: the package's)
# --location <text> location shown on the Webserver (monitor, sentinel)
# --advertise <ip> monitor only: IP address the Webserver must use to
# reach this probe (default: auto-detected)
# --webserver-url <url> integrator only: Webserver web UI URL used in
# notifications (default https://<webserver host>:9090)
# webserver-front:
# --front-pki <dir> the front/ folder produced by mugnsoft-front-pki.sh
#
# Examples (replace the values with yours):
# bash mugnsoft-install.sh webserver --license ./license_MNS.dat
# bash mugnsoft-install.sh monitor --webserver 10.0.0.5:8050 --shared-internal 3f9c...e1 --location Paris
set -euo pipefail
VERSION=4.2.0 DIR=/opt/mugnsoft PACKAGE="" VERIFY=1
LICENSE="" API_PORT="" WEB_PORT="" WEBSERVER="" SHARED="" NAME="" PORT="" LOCATION=""
ADVERTISE="" WEBSERVER_URL="" FRONT_PKI=""
BASE_URL="https://mugnsoft.com/bin"
die() { echo "ERROR: $*" >&2; exit 1; }
info() { echo "==> $*"; }
usage() { sed -n '2,/^$/p' "$0" | sed 's/^# \{0,1\}//'; exit "${1:-0}"; }
fetch() { if command -v curl >/dev/null; then curl -fsSL -o "$2" "$1"; else wget -q -O "$2" "$1"; fi; }
[ $# -ge 1 ] || usage 1
COMPONENT="$1"; shift
case "$COMPONENT" in -h|--help) usage 0 ;; esac
while [ $# -gt 0 ]; do
[ $# -ge 2 ] || [ "$1" = "--no-verify" ] || die "option $1 needs a value"
case "$1" in
--version) VERSION="$2" ;; --dir) DIR="$2" ;;
--package) PACKAGE="$2" ;; --no-verify) VERIFY=0; shift; continue ;;
--license) LICENSE="$2" ;; --api-port) API_PORT="$2" ;;
--web-port) WEB_PORT="$2" ;; --webserver) WEBSERVER="$2" ;;
--shared-internal) SHARED="$2" ;; --name) NAME="$2" ;;
--port) PORT="$2" ;; --location) LOCATION="$2" ;;
--advertise) ADVERTISE="$2" ;; --webserver-url) WEBSERVER_URL="$2" ;;
--front-pki) FRONT_PKI="$2" ;;
*) die "unknown option: $1 (see --help)" ;;
esac
shift 2
done
# component -> package name, folder, binary, config file
case "$COMPONENT" in
webserver) PKG=webserver; BIN=webserver ;;
webserver-front) PKG=webserver-front; BIN=webserver-front ;;
monitor) PKG=monitor; BIN=monitor ;;
integrator) PKG=integrator; BIN=integrator ;;
sentinel|discovery_agent) COMPONENT=sentinel; PKG=discovery_agent; BIN=discovery_agent ;;
*) die "unknown component '$COMPONENT' (webserver, monitor, integrator, sentinel, webserver-front)" ;;
esac
CONF="$BIN.json"
TARGET="$DIR/$PKG"
# ---- checks, before anything is written -------------------------------------
[ "$(id -u)" -eq 0 ] || die "run as root (sudo bash $0 ...)"
for c in tar gzip sed grep; do command -v "$c" >/dev/null || die "'$c' is required: install it first"; done
num() { [[ "$2" =~ ^[0-9]+$ ]] && [ "$2" -ge 1 ] && [ "$2" -le 65535 ] || die "$1 must be a port number, got '$2'"; }
safe() { [[ "$2" =~ ^[A-Za-z0-9._:/@+=,\ -]*$ ]] || die "$1 contains characters that are not allowed: '$2'"; }
[ -n "$API_PORT" ] && num --api-port "$API_PORT"
[ -n "$WEB_PORT" ] && num --web-port "$WEB_PORT"
[ -n "$PORT" ] && num --port "$PORT"
case "$COMPONENT" in
webserver|integrator)
[ -n "$LICENSE" ] || die "--license <license_MNS.dat> is required for the $COMPONENT"
[ -f "$LICENSE" ] || die "license file not found: $LICENSE" ;;
esac
case "$COMPONENT" in
monitor|integrator|sentinel)
[ -n "$WEBSERVER" ] || die "--webserver <host:port> is required (the Webserver API address)"
[[ "$WEBSERVER" =~ ^[A-Za-z0-9.-]+:[0-9]+$ ]] || die "--webserver must look like host:port, e.g. 10.0.0.5:8050"
[ -n "$SHARED" ] || die "--shared-internal is required: copy \"_sharedInternal\" from the Webserver's webserver.json"
[[ "$SHARED" =~ ^[A-Za-z0-9._~!#%^*+=-]+$ ]] || die "--shared-internal holds unexpected characters"
NAME="${NAME:-$COMPONENT-$(hostname -s)}"
[[ "$NAME" =~ ^[A-Za-z0-9-]+$ ]] || die "--name may hold letters, digits and hyphens only: '$NAME'"
safe --location "$LOCATION"; safe --advertise "$ADVERTISE"; safe --webserver-url "$WEBSERVER_URL" ;;
webserver-front)
[ -n "$FRONT_PKI" ] || die "--front-pki <dir> is required: the front/ folder made by mugnsoft-front-pki.sh"
[ -f "$FRONT_PKI/config/ssl/certificates/webserver.pem" ] && [ -f "$FRONT_PKI/webserver-front.json" ] \
|| die "$FRONT_PKI does not look like the front/ folder of mugnsoft-front-pki.sh" ;;
esac
[ -e "$TARGET" ] && die "$TARGET already exists: this script installs, it does not upgrade (see the Upgrade section of the docs)"
WORK=$(mktemp -d); trap 'rm -rf "$WORK"' EXIT
# ---- 1. get the package -----------------------------------------------------
FILE="mugnsoft-$PKG-$VERSION.linux-amd64.tar.gz"
if [ -z "$PACKAGE" ]; then
info "Downloading $FILE"
fetch "$BASE_URL/$VERSION/$FILE" "$WORK/$FILE" || die "download failed: $BASE_URL/$VERSION/$FILE"
[ "$VERIFY" -eq 1 ] && { fetch "$BASE_URL/$VERSION/$FILE.sig" "$WORK/$FILE.sig" || die "signature download failed"; }
PACKAGE="$WORK/$FILE"
else
[ -f "$PACKAGE" ] || die "package not found: $PACKAGE"
[ "$VERIFY" -eq 1 ] && { [ -f "$PACKAGE.sig" ] || die "no $PACKAGE.sig next to the package: download it too, or pass --no-verify"; cp "$PACKAGE.sig" "$WORK/pkg.sig"; }
fi
# ---- 2. verify its signature -------------------------------------------------
if [ "$VERIFY" -eq 1 ]; then
command -v gpg >/dev/null || die "'gpg' is required to verify the package (or pass --no-verify)"
info "Verifying the GPG signature"
SIG="$PACKAGE.sig"; [ -f "$WORK/pkg.sig" ] && SIG="$WORK/pkg.sig"
export GNUPGHOME="$WORK/gnupg"; mkdir -m 700 "$GNUPGHOME"
fetch "$BASE_URL/Mugnsoft_public.key" "$WORK/key" || die "could not download Mugnsoft_public.key"
gpg --batch --quiet --import "$WORK/key" 2>/dev/null
gpg --batch --verify "$SIG" "$PACKAGE" 2>/dev/null || die "SIGNATURE CHECK FAILED for $PACKAGE: do not install it"
info "Signature OK"
else
echo "WARNING: signature not verified (--no-verify)" >&2
fi
# ---- 3. extract ---------------------------------------------------------------
info "Extracting to $TARGET"
mkdir -p "$DIR"
mkdir "$WORK/x"
case "$PACKAGE" in
*.zip) command -v unzip >/dev/null || die "'unzip' is required for a .zip package"; unzip -q "$PACKAGE" -d "$WORK/x" ;;
*) tar -xzf "$PACKAGE" -C "$WORK/x" ;;
esac
[ -d "$WORK/x/$PKG" ] || die "unexpected package layout: no $PKG/ folder inside"
mv "$WORK/x/$PKG" "$TARGET"
cd "$TARGET"
chmod +x "./$BIN"
[ -d exec ] && find exec -maxdepth 1 -type f \( -name ffmpeg -o -name harx \) -exec chmod +x {} +
[ -d exec/jdk/bin ] && chmod +x exec/jdk/bin/*
[ -f "$CONF" ] || die "$CONF missing from the package"
# set "key": "value" in a flat JSON file, adding the key when it is absent
setkey() {
local key="$1" val="$2"
if grep -q "\"$key\"[[:space:]]*:" "$CONF"; then
sed -i -E "s|(\"$key\"[[:space:]]*:[[:space:]]*)\"[^\"]*\"|\1\"$val\"|" "$CONF"
else
sed -i -E "0,/\{/s|\{|{\n \"$key\": \"$val\",|" "$CONF"
fi
}
# ---- 4. configure ---------------------------------------------------------------
info "Configuring $CONF"
case "$COMPONENT" in
webserver)
cp "$LICENSE" ./license_MNS.dat
[ -n "$API_PORT" ] && setkey PortAPI "$API_PORT"
[ -n "$WEB_PORT" ] && setkey PortWEB "$WEB_PORT" ;;
webserver-front)
cp -r "$FRONT_PKI"/. . ;;
*)
setkey _sharedInternal "$SHARED"
setkey webserver "$WEBSERVER"
setkey name "$NAME"
setkey description "Mugnsoft $COMPONENT on $(hostname -s)"
[ -n "$PORT" ] && setkey port "$PORT"
[ "$COMPONENT" != integrator ] && setkey location "$LOCATION"
[ "$COMPONENT" = monitor ] && [ -n "$ADVERTISE" ] && setkey ip "$ADVERTISE"
[ "$COMPONENT" = sentinel ] && setkey discoFSGMonitored "/"
if [ "$COMPONENT" = integrator ]; then
cp "$LICENSE" ./license_MNS.dat
setkey os linux
setkey webserverUrl "${WEBSERVER_URL:-https://${WEBSERVER%%:*}:9090}"
fi ;;
esac
# ---- 5. install the service (components register with the Webserver here) ------
info "Installing the service"
OUT=$("./$BIN" install 2>&1) || true
echo "$OUT" | grep -v '^time=' || true
# "Service '<name>' installed." - not "Service '<name>' not installed."
echo "$OUT" | grep -q "Service '[^']*' installed\." || die "the service was not installed - read the messages above, fix, then run: cd $TARGET && ./$BIN install"
# ---- 6. start it ----------------------------------------------------------------
info "Starting the service"
"./$BIN" start 2>&1 | grep -v '^time=' || true
echo
case "$COMPONENT" in
webserver)
SI=$(sed -n -E 's/.*"_sharedInternal"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/p' "$CONF")
echo "Webserver installed in $TARGET"
echo " Web UI: https://<this host>:$(sed -n -E 's/.*"PortWEB"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/p' "$CONF")/ (admin / admin, change it at first login)"
echo " API: <this host>:$(sed -n -E 's/.*"PortAPI"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/p' "$CONF") -> the --webserver value for the components"
echo " _sharedInternal (pass it to every component as --shared-internal):"
echo " $SI" ;;
webserver-front)
echo "Webserver-Front installed in $TARGET - now configure ADMIN (see the Webserver-Front install page)." ;;
*)
echo "$COMPONENT '$NAME' installed in $TARGET and registered with $WEBSERVER."
echo "Last step: on the Webserver, open Settings (gear) > Components, right-click '$NAME' and choose enable server." ;;
esac
Option B — Step by step
The same steps by hand. Set the two variables first, then run the blocks in order.
1. Download and verify (on every host; COMPONENT is webserver, monitor, integrator or discovery_agent):
VERSION=4.2.0; COMPONENT=monitor # <- change COMPONENT for each host
F=mugnsoft-$COMPONENT-$VERSION.linux-amd64.tar.gz
curl -fsSLO https://mugnsoft.com/bin/$VERSION/$F
curl -fsSLO https://mugnsoft.com/bin/$VERSION/$F.sig
curl -fsSLO https://mugnsoft.com/bin/Mugnsoft_public.key
gpg --import Mugnsoft_public.key
gpg --verify $F.sig $F # must print: Good signature from "Mugnsoft ..."
mkdir -p /opt/mugnsoft && tar -xzf $F -C /opt/mugnsoft && cd /opt/mugnsoft/$COMPONENT
chmod +x ./$COMPONENT
gpg adds WARNING: This key is not certified with a trusted signature!: that is expected, what matters is Good signature. A BAD signature means the file is corrupt or altered: delete it.
2. Webserver host: copy the license, install and start, then read _sharedInternal (the Webserver writes a random one into webserver.json at install):
cp /path/to/license_MNS.dat /opt/mugnsoft/webserver/
cd /opt/mugnsoft/webserver
./webserver install && ./webserver start
grep _sharedInternal webserver.json
3. Monitor / Integrator / Sentinel Agent host: edit the .json file of the component (monitor.json, integrator.json or discovery_agent.json) and set:
| Key | Set it to | Note |
|---|---|---|
_sharedInternal |
the Webserver’s value | Must be identical to the Webserver’s |
webserver |
10.0.0.5:8050 |
Webserver address and API port |
name |
a unique name, e.g. monitor-paris-1 |
Letters, digits, hyphens. The packaged value is an example |
location, description |
your text | The packaged values are examples |
port |
keep, or another free port | The Webserver must reach it |
ip |
monitor only, optional | The IP the Webserver must dial, when the host has several |
webserverUrl |
https://10.0.0.5:9090 |
Integrator only |
discoFSGMonitored |
/ |
Sentinel Agent on Linux: the packaged C: matches no Linux file system |
For the Integrator, also copy its license_MNS.dat into /opt/mugnsoft/integrator/. Then install (this registers the component) and start:
./monitor install && ./monitor start # or ./integrator, ./discovery_agent
install must print status: Success twice (registration, then certificate) and Service '...' installed.
Enable the components
A registered component is pending until you enable it:
- Sign in to
https://10.0.0.5:9090/(admin/admin; you must choose a new password at first sign-in). - Open the gear menu → components, right-click the new component → enable server. A success message confirms that the Webserver reached it.
Check the address. The Webserver connects to the IP address the component registered. A host with several network interfaces can register the wrong one: then enable server fails with getting access token: Failed. Make sure the component is running. Right-click the component → edit server, set the IP the Webserver can reach, save, then enable it again. For a monitor you can also pass --advertise <ip> (or set ip in monitor.json) before installing.
Manage the services
| Task | Command (from the component folder) | systemd equivalent |
|---|---|---|
| Status | — | systemctl status MugnsoftMonitor |
| Stop / start / restart | ./monitor stop, ./monitor start, ./monitor restart |
systemctl restart MugnsoftMonitor (or stop, start) |
| Run in the foreground (debugging) | ./monitor stop && ./monitor run |
— |
| Register again with the Webserver | ./monitor register |
— |
| Remove the service | ./monitor uninstall (then delete the folder) |
— |
| Logs | log/monitor.log in the component folder |
journalctl -u MugnsoftMonitor |
Replace monitor / MugnsoftMonitor with webserver / MugnsoftWebserver, integrator / MugnsoftIntegrator, discovery_agent / MugnsoftDiscoveryAgent. Removing a component from the Webserver is done on its Components page (right-click → delete server).
To upgrade, follow the order and the checks in the release notes of the target version; this page covers new installations.
Monitor probes: browsers for EUM journeys
URL, API, TCP, ping and the other monitor types need nothing more. EUM (Web UI) journeys need a browser on the probe host: install Chrome, Edge or Firefox there. At start, the monitor downloads the matching webdriver if it can reach the Internet. Otherwise, download the driver that matches the browser version yourself and put it in /opt/mugnsoft/monitor/drivers/:
| Browser | Driver | Download |
|---|---|---|
| Chrome | chromedriver |
Chrome for Testing |
| Firefox | geckodriver |
geckodriver releases |
| Edge | msedgedriver |
Edge WebDriver |
To record journeys, use Mugnsoft Pulse or the Mugnsoft IDE.
Troubleshooting
| Message | Cause and fix |
|---|---|
self pre-registering ... status: Failed / Make sure that component can reach the Webserver API endpoint. |
The host cannot reach webserver on the API port: check the value, the Webserver service, the firewalls. Test with curl -k https://10.0.0.5:8050/ping (answers {"message":"pong"}) |
a server is already defined with this name |
A component with this name exists on the Webserver (an earlier attempt). Delete it on the Components page, or choose another --name, then run install again |
a server is already defined with this IP and port |
A component already registered this IP address and port (a previous installation of this host). Delete it on the Components page, or use another --port, then run install again |
No license .../license_MNS.dat file found |
Integrator: copy its license file into its folder (--license with the script) |
license not valid for this machine |
The license file belongs to another host: licenses are bound to the host (machine-id on Linux) |
enable server: getting access token: Failed |
The Webserver cannot reach the component: wrong registered IP (see Check the address), or the component’s port is blocked |
enable server / Push Settings: rejected the internal login (HTTP 401) |
_sharedInternal differs from the Webserver’s: fix it in the component’s .json and restart the component |
<folder> already exists (script) |
The script never overwrites an installation: choose another --dir, or remove the old one first (uninstall) |
SIGNATURE CHECK FAILED (script) / BAD signature |
The package is corrupt or altered: download it again; never install it |
See also
- Requirements — supported systems and sizing
- Install the Webserver-Front — the read-only DMZ replica
- Folder structure — what each component folder holds
- Components Deep Dive — every setting of every component
- Hardening —
_sharedInternal, certificates and the rest of the security settings