Operation and maintenance

Service commands

Each role installs a different set of units. The standard systemd verbs apply to all of them:

sudo systemctl status axiom-border
sudo systemctl restart axiom-border
sudo systemctl is-active axiom-border
sudo systemctl is-enabled axiom-border

The units, depending on role: axiom-border, axiom-influxdb, containerd, oda-lite.

Where the logs live

This is the single most useful thing to know when something misbehaves, because the important detail is not in the journal:

Service Journal Own file Rotation
axiom-border systemd events and early startup errors only — with the shipped default, very little /opt/axiom-border/logs/axiom-border.logall the detail: probes, MQTT, scans, rules Automatic, tunable in configuration.yaml
axiom-influxdb Everything: startup, crashes, slow queries, compactions None journald drop-in: 500 MB, 1 day per file
containerd Everything: runtime, namespaces, OCI errors None journald drop-in
oda-lite Startup, crashes and early warnings only — 3 to 5 lines in normal operation /var/log/oda-lite/oda-lite.log — plugin activity, HTTP errors towards central, emitted metrics Automatic: every 24 h or at 100 MB, 7 archives kept
Lifecycle scripts /var/log/axiom-border-install.log — install, upgrade and uninstall appended together Not rotated; clear it by hand if it grows
The rule of thumb

To diagnose application logic, read the file. To diagnose a crash or a failure to start, read the journal. Looking for probe results in journalctl is the most common wasted hour.

Everything at once, which is often what you want:

sudo journalctl -u axiom-border -u axiom-influxdb -u containerd -u oda-lite \
  --since "5 minutes ago" --no-pager

Log levels

Axiom Border — the logger block of configuration.yaml. Set logLevel to DEBUG, INFO, WARN or ERROR, then restart the service. See Configuration.

oda-lite agent/etc/oda-lite/oda-lite.conf, [agent] section: log_level and debug. Restart the service afterwards.

InfluxDB and containerd — the level comes from binary flags in the unit file. Changing it means editing the unit, and is not recommended.

Turn DEBUG back off

DEBUG on a busy probe generates enough volume to fill a disk. Raise it to reproduce a problem, then put it back to INFO. Do not leave it on “just in case”.

Rotation and disk usage

Axiom Border’s own log rotates through the logger settings: maxSize (MB per file, default 20), maxBackups (default 20) and compress (gzip the rotated files).

The journald drop-in installed by the bundle is globalSystemMaxUse=500M and MaxFileSec=1day apply to the entire host journal, not only to Axiom Border. On a host sharing other workloads, adjust it or replace it with per-unit drop-ins.

journalctl --disk-usage
sudo journalctl --vacuum-size=500M
sudo journalctl --vacuum-time=7d

To see what is consuming space:

du -sh /var/log/oda-lite /opt/axiom-border/logs /var/log/journal/

Backup and restore

Resource Path Suggested frequency
Configuration /opt/axiom-border/config/configuration.yaml On every change
Local state — rules, aliases, executions /opt/axiom-border/db.dat Daily
InfluxDB data — metrics, alarms /var/lib/influxdb2/ Per retention policy
Logs /opt/axiom-border/logs/ On demand

Stop the service first, for a consistent snapshot:

sudo systemctl stop axiom-border
sudo tar -czf /path/backup-axiom-border-$(date +%F).tar.gz \
  /opt/axiom-border/config /opt/axiom-border/db.dat /var/lib/influxdb2
sudo systemctl start axiom-border

Restore is the inverse:

sudo systemctl stop axiom-border
sudo tar -xzf backup-axiom-border-<date>.tar.gz -C /
sudo systemctl start axiom-border

Offline maintenance

Refreshing the web and CVE vulnerability templates

The OT/ICS suite ships with the product and needs no maintenance. The web and CVE templates do, and on an air-gapped probe the refresh is manual: generate a snapshot of the upstream nuclei-templates tree on a machine with internet, transfer it, then swap it in.

# On a machine with internet
nuclei -update-templates -ut /tmp/nuclei-templates
tar -czf nuclei-templates-$(date +%F).tar.gz -C /tmp nuclei-templates

# On the probe
sudo systemctl stop axiom-border
sudo mv /opt/axiom-border/vulnscan-templates /opt/axiom-border/vulnscan-templates.old
sudo tar -xzf nuclei-templates-<date>.tar.gz -C /tmp
sudo mv /tmp/nuclei-templates /opt/axiom-border/vulnscan-templates
sudo systemctl start axiom-border

Keep .old until you have confirmed a few successful scans, then delete it. Templates are read when the service starts, so a restart is required after swapping them.

Loading AI images

AI images are not part of the bundle — they are distributed separately as OCI tarballs, and resources/iaimages/ ships empty as a placeholder.

sudo nerdctl --namespace axiom-border image load -i <image>.tar
sudo nerdctl --namespace axiom-border image ls

Upgrading

Extract the new bundle of the same role on the target and run upgrade.sh. It aborts if the bundle role does not match the installed role — changing role requires uninstall followed by install.

sudo ./upgrade.sh
Flag Meaning
--force-conf-regen Regenerate the oda-lite configuration from the new template, saving the current one as .bak-<timestamp>
-h, --help Print help and exit
Variable Default Meaning
HEALTHCHECK_RETRIES 12 Attempts before declaring failure
HEALTHCHECK_INTERVAL 5 Seconds between attempts — 12 × 5 s gives a 60 s window
AXIOM_API_PORT 8083 API port used for the health check

What it does: verifies the previous installation and role match, stops the role’s services (leaving InfluxDB and containerd running, so data in flight is not disturbed), takes timestamped backups of the binary and db.dat, replaces the binary and updatable assets, starts the service, and health-checks it.

Preserved across upgrade: config/, db.dat, .influx-creds, parquet/, logs/, /var/lib/influxdb2/, resources/iaimages/ and the oda-lite configuration.

Replaced: the binary, MIBS/, resources/gojascripts/, vulnscan-templates/, the documentation and the lifecycle scripts.

Failed upgrades roll back automatically

If the health check fails all 12 attempts, upgrade.sh restores the previous binary, confirms the old version answers with HTTP 200, and exits with code 2. The probe is left operational on the old version and the backup is kept at /opt/axiom-border/axiom-border.bak-<timestamp>. An upgrade that reports a rollback has not broken anything — diagnose, then retry with a corrected bundle.

Back up db.dat before upgrading

The local state file db.dat is not migrated automatically. If a new version changes its format, the file may have to be regenerated, and that loses local state — rules, aliases and execution history. Always take a copy before upgrading, and if the new version will not start cleanly, restore the copy and revert the binary.

Uninstalling

sudo /opt/axiom-border/uninstall.sh

It stops and disables the role’s services, removes only the units it created, removes the journald and sshd drop-ins, and asks whether to keep the data — InfluxDB data, db.dat, logs and configuration.yaml. Answer non-interactively with AXIOM_KEEP_DATA=true|false.

It removes shared binaries only where the manifest marks them as installed by this deployment. It does not remove nmap, rsyslog or network-manager even if it installed them — those are host infrastructure and are left to the operator. It does not revert the netplan switch by default.

Variable Effect
AXIOM_KEEP_DATA=true|false Answers the data-retention question without prompting
AXIOM_RESTORE_NETPLAN=1 Restores /etc/netplan/ from the backup, re-applies it, and switches back to systemd-networkd
AXIOM_FORCE_CLEAN=1 Destructive. Without a manifest, claims every detected artefact as its own — including global binaries that may belong to other software such as Docker or Kubernetes

Running it on a clean system is safe and idempotent: it reports there is nothing to uninstall and exits 0.

AXIOM_FORCE_CLEAN can remove another product’s binaries

Use it only after confirming nothing else on the host depends on containerd, runc, nerdctl or the CNI plugins. Without the flag, a manifest-less uninstall stays conservative and touches only artefacts that are unmistakably Axiom Border’s.

AXIOM_RESTORE_NETPLAN=1 warns about possible connectivity loss. In practice an established SSH session usually survives, since the TCP connection is not torn down and the IP is retained — but do not count on it over a link you cannot recover from out-of-band.

Troubleshooting by symptom

Quick triage

Symptom Look here first
Service will not start journalctl -u <service> -n 50 --no-pager
Service active but no results /opt/axiom-border/logs/axiom-border.log
Metrics not reaching InfluxDB journalctl -u axiom-influxdb plus the agent log
Monitoring node not reaching central tail -f /var/log/oda-lite/oda-lite.log | grep "outputs.http"
SSH, USB or interface events missing The agent log, filtering by plugin name
Service restart-looping journalctl -u <service> --since "5 min ago", looking for start-limit
Install or upgrade behaved oddly tail -200 /var/log/axiom-border-install.log
Disk filling up du -sh /var/log/oda-lite /opt/axiom-border/logs /var/log/journal/

Detailed cases

The service will not start. Three usual causes: the binary lost its execute bit (chmod +x /opt/axiom-border/axiom-border), a required port is occupied, or the YAML is invalid. Validate the configuration directly:

python3 -c "import yaml; yaml.safe_load(open('/opt/axiom-border/config/configuration.yaml'))"

Login fails with the correct password. login.pass must be the SHA-256 digest, not the plaintext. Recalculate and restart:

echo -n "<your-password>" | sha256sum | awk '{print $1}'

A port is in use. Identify the holder, then either stop it or move the Axiom Border port:

sudo ss -tlnp | grep -E ':(8083|1883|1888|8086|9090|9091) '

Ports 8083, 1883 and 1888 move via apiPort, mqtt.broker.port and mqtt.broker.ws.port. Ports 9090 and 9091 move in /etc/oda-lite/oda-lite.conf.

Vulnerability scans return zero findings, suspiciously. The web template directory is probably empty or incomplete:

ls /opt/axiom-border/vulnscan-templates/ | head

It should list per-protocol directories such as http/, network/ and ssl/. CVE templates live under http/cves/, not at the root. Refresh them as described above. OT/ICS findings are unaffected, because that suite ships with the product.

Sniffing captures nothing. Check the interface exists (ip -o link show), that the service runs as root (systemctl show axiom-border -p User), and that there is actually traffic (sudo tcpdump -i <iface> -c 10).

The UI loads but every API call returns 401. The JWT is missing or expired. Clear the browser’s localStorage and log in again.

nmap probes hang or take very long. A large target range with poor reachability, a high timeout, or udp: true with many ports. Narrow the range, lower the timeout, disable UDP.

High memory usage. Check for an AI capability working a large dataset, an Influx bucket with no retention policy, or logLevel: DEBUG left on.

“Orphaned installation detected.” Bundle artefacts exist without an .install-manifest. Three ways out: AXIOM_CLEAN_RESIDUALS=1 sudo bash install.sh moves the leftovers aside, sudo bash upgrade.sh preserves data, sudo bash uninstall.sh cleans best-effort.

upgrade.sh exits with code 2 reporting a rollback. The new binary failed its health check and the old one is back in service. Diagnose with the journal, the application log and the install log, then retry with a corrected bundle.

The monitoring node floods the journal with “connection refused” to port 9090. The central node is not up, a firewall blocks it, or the URL is wrong. The agent keeps retrying and buffers metrics while it waits, so a short outage loses nothing — a long one eventually will.

Error executing 'netstat': exit status 1. Minimal Ubuntu Server images lack net-tools. Not blocking — the other plugins continue:

sudo apt-get install -y net-tools && sudo systemctl restart oda-lite

NetworkManager is active but interfaces show as unmanaged. The netplan renderer was not switched over. Re-run install.sh --switch-to-network-manager; it detects the current state and completes the switch without reinstalling NetworkManager.

/etc/oda-lite/oda-lite.conf.new contains literal ${VARIABLE} placeholders. The file is an unexpanded template. Do not copy it over the live configuration — the agent would not start. Delete it and re-run the upgrade with the current bundle.

scp or tar -xzf fail with Permission denied in /tmp. Some base images ship /tmp with wrong permissions. It should be drwxrwxrwt:

ls -ld /tmp
sudo chmod 1777 /tmp