Skip to content

Running nats-sink As A Service

This page provides systemd guidance for Oracle Linux and Debian. The service model works for every sink type. The examples run nats-sink from a Python virtual environment, load JSON configuration from /etc/nats-sinks/config.json, and optionally load secrets from /etc/nats-sinks/nats-sink.env.

These examples are intentionally conservative because many mission-support and defence environments run long-lived services under operating-system control, with explicit service users, reviewed configuration paths, and protected environment files for secrets. The same pattern works for simple file sink deployments and Oracle-backed operational event stores.

Layout

/opt/nats-sinks/venv              Python virtual environment
/etc/nats-sinks/config.json       Runtime JSON config
/etc/nats-sinks/nats-sink.env     Secret environment file
/etc/nats-sinks/observability.prometheus.json
/var/lib/nats-sink                Service working directory
/var/lib/node_exporter/textfile_collector
/etc/systemd/system/nats-sink.service
/etc/systemd/system/nats-sink-prometheus-textfile.service
/etc/systemd/system/nats-sink-prometheus-textfile.timer
/etc/systemd/system/nats-sink-prometheus-http.service
/etc/systemd/system/nats-sink-otlp.service
/etc/systemd/system/nats-sink-otlp.timer
/etc/systemd/system/nats-sink-nats-monitoring.service
/etc/systemd/system/nats-sink-nats-monitoring.timer

Service Flow

sequenceDiagram
    participant systemd
    participant CLI as nats-sink CLI
    participant R as Runner
    participant S as Selected Sink

    systemd->>CLI: start nats-sink run /etc/nats-sinks/config.json
    CLI->>R: build runner
    R->>S: start sink
    R->>R: consume batches
    systemd->>CLI: SIGTERM
    CLI->>R: graceful stop path

Optional Prometheus textfile export runs as a separate service:

sequenceDiagram
    participant Sink as nats-sink.service
    participant Snapshot as metrics.json
    participant Timer as systemd timer
    participant Obs as nats-sink-observe
    participant Policy as observability policy
    participant Prom as textfile collector

    Sink->>Snapshot: write local metrics snapshot
    Timer->>Obs: run prometheus-textfile command
    Obs->>Policy: read allow-list policy
    Obs->>Snapshot: read snapshot only when export is enabled
    Obs->>Prom: write nats_sinks.prom

Optional native Prometheus HTTP export also runs separately:

sequenceDiagram
    participant Snapshot as metrics.json
    participant Http as nats-sink-prometheus-http.service
    participant Policy as observability policy
    participant Prom as Prometheus

    Http->>Policy: read allow-list policy
    Prom->>Http: GET /metrics
    Http->>Snapshot: read local snapshot
    Http-->>Prom: return approved Prometheus text

Optional OpenTelemetry OTLP export also runs separately:

sequenceDiagram
    participant Sink as nats-sink.service
    participant Snapshot as metrics.json
    participant Timer as nats-sink-otlp.timer
    participant Obs as nats-sink-observe
    participant Policy as observability policy
    participant Collector as OpenTelemetry Collector

    Sink->>Snapshot: write local metrics snapshot
    Timer->>Obs: run otlp-export command
    Obs->>Policy: read allow-list policy
    Obs->>Snapshot: read snapshot only when OTLP is enabled
    Obs->>Collector: POST bounded OTLP/HTTP JSON

Optional NATS server monitoring collection also runs separately. It reads only policy-approved endpoint fields and never participates in ACK decisions:

sequenceDiagram
    participant Timer as nats-sink-nats-monitoring.timer
    participant Obs as nats-sink-observe
    participant Policy as observability policy
    participant NATS as NATS monitoring endpoint
    participant Snapshot as nats-monitoring.json

    Timer->>Obs: run nats-monitoring-poll command
    Obs->>Policy: read endpoint and field allow lists
    Obs->>NATS: fetch approved endpoint with timeout
    Obs->>Snapshot: write sanitized snapshot

Unified Installer

Install from a checkout with the unified script:

sudo scripts/install-systemd.sh

The script reads /etc/os-release, detects Debian-family systems or Oracle Linux, installs the right Python package prerequisites, creates the nats-sink service user, installs the main nats-sink.service, installs the disabled Prometheus textfile service/timer assets, installs the disabled native Prometheus HTTP service asset, installs the disabled OTLP service/timer assets, installs the disabled NATS monitoring service/timer assets, and enables only the main sink service. Prometheus, OTLP, Elastic Observability, Grafana Alloy, Splunk HEC, OCI Monitoring, StatsD, Datadog, Amazon CloudWatch, Azure Monitor, syslog, and NATS monitoring sharing remain disabled until the observability policy and the selected observability service are explicitly enabled.

The installer works in two modes. When it is run from a local git checkout, it copies tracked example configuration files and systemd unit files from that checkout. When it is downloaded and run as a standalone script, it fetches those same service/configuration assets from GitHub using NATS_SINKS_INSTALL_REF. That makes the documented install command a true one-line GitHub install rather than a command that silently depends on being inside a cloned repository.

The Python package installed into /opt/nats-sinks/venv is controlled by NATS_SINKS_PACKAGE_SPEC. If that variable is not set, main installs the latest nats-sinks package from PyPI, while release tags such as v0.3.0 install the matching package version such as nats-sinks==0.3.0. Operators can override the package spec when they need optional extras, for example nats-sinks[oracle]==0.3.0 for Oracle support.

The older scripts/install-systemd-debian.sh and scripts/install-systemd-oracle-linux.sh paths remain compatibility wrappers that delegate to scripts/install-systemd.sh.

Debian Single-Command Install From GitHub

On Debian or Ubuntu-like systems, an administrator with sudo access can download the installer from GitHub and run it as root with one command:

curl -fsSL https://raw.githubusercontent.com/ProjectCuillin/nats-sinks/main/scripts/install-systemd.sh | sudo env NATS_SINKS_INSTALL_REF=main sh

The command runs the installer as root, installs the Python package into /opt/nats-sinks/venv, creates the dedicated nats-sink system account, and configures nats-sink.service to run as that low-privilege service user.

For a release-pinned install after this script is included in a tagged release, use the same tag in both the download URL and NATS_SINKS_INSTALL_REF:

curl -fsSL https://raw.githubusercontent.com/ProjectCuillin/nats-sinks/vX.Y.Z/scripts/install-systemd.sh | sudo env NATS_SINKS_INSTALL_REF=vX.Y.Z sh

For Oracle-backed deployments, include the Oracle optional dependency in the package spec:

curl -fsSL https://raw.githubusercontent.com/ProjectCuillin/nats-sinks/vX.Y.Z/scripts/install-systemd.sh | sudo env NATS_SINKS_INSTALL_REF=vX.Y.Z NATS_SINKS_PACKAGE_SPEC='nats-sinks[oracle]==X.Y.Z' sh

Oracle Linux Single-Command Install From GitHub

On Oracle Linux, use the same unified installer. The script detects Oracle Linux from /etc/os-release and uses dnf for the required operating-system packages:

curl -fsSL https://raw.githubusercontent.com/ProjectCuillin/nats-sinks/main/scripts/install-systemd.sh | sudo env NATS_SINKS_INSTALL_REF=main sh

For a release-pinned install after this script is included in a tagged release:

curl -fsSL https://raw.githubusercontent.com/ProjectCuillin/nats-sinks/vX.Y.Z/scripts/install-systemd.sh | sudo env NATS_SINKS_INSTALL_REF=vX.Y.Z sh

The same Oracle optional dependency override can be used on Oracle Linux:

curl -fsSL https://raw.githubusercontent.com/ProjectCuillin/nats-sinks/vX.Y.Z/scripts/install-systemd.sh | sudo env NATS_SINKS_INSTALL_REF=vX.Y.Z NATS_SINKS_PACKAGE_SPEC='nats-sinks[oracle]==X.Y.Z' sh

The one-command form is convenient for labs, test hosts, and controlled automation. In sensitive production environments, prefer downloading the installer first, reviewing it, and then running it:

curl -fsSLO https://raw.githubusercontent.com/ProjectCuillin/nats-sinks/main/scripts/install-systemd.sh
less install-systemd.sh
sudo env NATS_SINKS_INSTALL_REF=main sh install-systemd.sh

Debian Manual Steps

sudo apt-get update
sudo apt-get install -y python3 python3-venv python3-pip
sudo useradd --system --home-dir /var/lib/nats-sink --create-home --shell /usr/sbin/nologin nats-sink
sudo install -d -o nats-sink -g nats-sink /var/lib/nats-sink
sudo install -d -o nats-sink -g nats-sink /var/lib/node_exporter/textfile_collector
sudo install -d /etc/nats-sinks /opt/nats-sinks
sudo python3 -m venv /opt/nats-sinks/venv
sudo /opt/nats-sinks/venv/bin/python -m pip install --upgrade pip
sudo /opt/nats-sinks/venv/bin/python -m pip install nats-sinks
sudo install -m 0640 -o root -g nats-sink examples/file-basic/config.json /etc/nats-sinks/config.json
sudo install -m 0640 -o root -g nats-sink examples/systemd/nats-sink.env /etc/nats-sinks/nats-sink.env
sudo install -m 0640 -o root -g nats-sink examples/systemd/observability.prometheus.json /etc/nats-sinks/observability.prometheus.json
sudo install -m 0644 examples/systemd/nats-sink.service /etc/systemd/system/nats-sink.service
sudo install -m 0644 examples/systemd/nats-sink-prometheus-textfile.service /etc/systemd/system/nats-sink-prometheus-textfile.service
sudo install -m 0644 examples/systemd/nats-sink-prometheus-textfile.timer /etc/systemd/system/nats-sink-prometheus-textfile.timer
sudo install -m 0644 examples/systemd/nats-sink-prometheus-http.service /etc/systemd/system/nats-sink-prometheus-http.service
sudo install -m 0644 examples/systemd/nats-sink-otlp.service /etc/systemd/system/nats-sink-otlp.service
sudo install -m 0644 examples/systemd/nats-sink-otlp.timer /etc/systemd/system/nats-sink-otlp.timer
sudo install -m 0644 examples/systemd/nats-sink-nats-monitoring.service /etc/systemd/system/nats-sink-nats-monitoring.service
sudo install -m 0644 examples/systemd/nats-sink-nats-monitoring.timer /etc/systemd/system/nats-sink-nats-monitoring.timer
sudo systemctl daemon-reload
sudo systemctl enable nats-sink

Oracle Linux Manual Steps

sudo dnf install -y python3 python3-pip
sudo useradd --system --home-dir /var/lib/nats-sink --create-home --shell /sbin/nologin nats-sink
sudo install -d -o nats-sink -g nats-sink /var/lib/nats-sink
sudo install -d -o nats-sink -g nats-sink /var/lib/node_exporter/textfile_collector
sudo install -d /etc/nats-sinks /opt/nats-sinks
sudo python3 -m venv /opt/nats-sinks/venv
sudo /opt/nats-sinks/venv/bin/python -m pip install --upgrade pip
sudo /opt/nats-sinks/venv/bin/python -m pip install nats-sinks
sudo install -m 0640 -o root -g nats-sink examples/file-basic/config.json /etc/nats-sinks/config.json
sudo install -m 0640 -o root -g nats-sink examples/systemd/nats-sink.env /etc/nats-sinks/nats-sink.env
sudo install -m 0640 -o root -g nats-sink examples/systemd/observability.prometheus.json /etc/nats-sinks/observability.prometheus.json
sudo install -m 0644 examples/systemd/nats-sink.service /etc/systemd/system/nats-sink.service
sudo install -m 0644 examples/systemd/nats-sink-prometheus-textfile.service /etc/systemd/system/nats-sink-prometheus-textfile.service
sudo install -m 0644 examples/systemd/nats-sink-prometheus-textfile.timer /etc/systemd/system/nats-sink-prometheus-textfile.timer
sudo install -m 0644 examples/systemd/nats-sink-prometheus-http.service /etc/systemd/system/nats-sink-prometheus-http.service
sudo install -m 0644 examples/systemd/nats-sink-otlp.service /etc/systemd/system/nats-sink-otlp.service
sudo install -m 0644 examples/systemd/nats-sink-otlp.timer /etc/systemd/system/nats-sink-otlp.timer
sudo install -m 0644 examples/systemd/nats-sink-nats-monitoring.service /etc/systemd/system/nats-sink-nats-monitoring.service
sudo install -m 0644 examples/systemd/nats-sink-nats-monitoring.timer /etc/systemd/system/nats-sink-nats-monitoring.timer
sudo systemctl daemon-reload
sudo systemctl enable nats-sink
sudo systemctl start nats-sink

Operations

Check status:

systemctl status nats-sink
journalctl -u nats-sink -f

Restart after config changes:

sudo systemctl restart nats-sink

Enable the Prometheus textfile timer only after the observability policy has been reviewed and explicitly enabled:

sudo systemctl enable --now nats-sink-prometheus-textfile.timer
systemctl status nats-sink-prometheus-textfile.timer
journalctl -u nats-sink-prometheus-textfile.service -n 50

The Prometheus service uses nats-sink-observe, not nats-sink. It reads the metrics snapshot and writes only policy-approved metrics. If the policy remains disabled, it writes a harmless comment and does not require the snapshot to exist. See Prometheus Integration for policy examples and node_exporter guidance; in the documentation navigation, that page lives under the Observability section.

Enable the native Prometheus HTTP service only when the policy explicitly enables prometheus.http_endpoint.enabled and the chosen listener address is approved for the host:

sudo systemctl enable --now nats-sink-prometheus-http.service
systemctl status nats-sink-prometheus-http.service
journalctl -u nats-sink-prometheus-http.service -n 50

The native endpoint reads the same local snapshot and policy as the textfile connector. It should remain separate from nats-sink.service so a scrape endpoint failure cannot change JetStream ACK behavior.

Enable the OTLP export timer only when the observability policy explicitly enables otlp.enabled, the collector endpoint has been reviewed, any required header values are provided through /etc/nats-sinks/nats-sink.env, and the collector is an approved operational information-sharing boundary:

sudo systemctl enable --now nats-sink-otlp.timer
systemctl status nats-sink-otlp.timer
journalctl -u nats-sink-otlp.service -n 50

The OTLP service uses nats-sink-observe otlp-export. It reads only the local metrics snapshot and observability policy, sends only policy-approved metric names, and exits non-zero if the collector is unavailable after bounded attempts. See OpenTelemetry OTLP Integration for policy examples, dry-run output, timeout and retry controls, and limitations.

For Grafana Alloy, keep Alloy as a separate service and use nats-sink-observe grafana-alloy-export as the nats-sinks side of the handoff. The profile can also render a starter Alloy River snippet with nats-sink-observe grafana-alloy-config. See Grafana Alloy Profile for service separation, generated config, and security guidance.

For Splunk HEC, run nats-sink-observe splunk-hec-export as a separate oneshot service or timer with read access to the metrics snapshot, read access to the observability policy, and a protected environment variable containing the HEC token. See Splunk HEC Integration for HEC event format, token handling, TLS expectations, and service guidance.

For OCI Monitoring, run nats-sink-observe oci-monitoring-export as a separate oneshot service or timer with read access to the metrics snapshot, read access to the observability policy, and the least-privilege OCI identity needed to post custom metrics in the approved compartment. See OCI Monitoring Integration for OCI SDK extra installation, authentication modes, custom metric format, and service guidance.

For StatsD, run nats-sink-observe statsd-export as a separate oneshot service or timer with read access to the metrics snapshot and observability policy, and send datagrams only to the approved StatsD-compatible listener. See StatsD Integration for datagram format, best-effort transport limitations, and service guidance.

For Amazon CloudWatch, run nats-sink-observe cloudwatch-export as a separate oneshot service or timer with read access to the metrics snapshot and observability policy, plus only the AWS identity permissions needed to call cloudwatch:PutMetricData for the reviewed namespace. See Amazon CloudWatch Integration for request shape, IAM guidance, dimension controls, and service guidance.

For Azure Monitor, run nats-sink-observe azure-monitor-export as a separate oneshot service or timer with read access to the metrics snapshot and observability policy, plus only the Azure identity permissions needed to post custom metrics for the reviewed resource. See Azure Monitor Integration for REST request shape, bearer token handling, dimension controls, and service guidance.

For syslog, run nats-sink-observe syslog-export as a separate oneshot service or timer with read access to the metrics snapshot and observability policy, and send bounded RFC 5424-style messages only to the approved syslog listener. See Syslog Bridge for message format, best-effort transport limitations, and service guidance.

Enable the NATS server monitoring timer only when the observability policy explicitly enables nats_server_monitoring.enabled, endpoint and field allow lists have been reviewed, and the monitoring endpoint is reachable only through approved management networking:

sudo systemctl enable --now nats-sink-nats-monitoring.timer
systemctl status nats-sink-nats-monitoring.timer
journalctl -u nats-sink-nats-monitoring.service -n 50

The service writes /var/lib/nats-sink/nats-monitoring.json and, when nats_server_monitoring.prometheus_enabled is true, writes /var/lib/node_exporter/textfile_collector/nats_sinks_nats_monitoring.prom. Both files contain only policy-approved endpoint paths and field values.

Use systemctl stop nats-sink for graceful shutdown. Messages in a completed destination commit but not yet ACKed may redeliver; idempotency must handle duplicates. Oracle deployments should install nats-sinks[oracle] and use an Oracle configuration file. File sink deployments can use the base package and should ensure the configured output directory is owned by the service user.

If payload encryption is enabled, install nats-sinks[crypto] and place the base64 key environment variable named by encryption.key_b64_env in the protected service environment file. Keep that file readable only by root and the nats-sink service group. Do not put direct encryption.key_b64 values in tracked config files.

For sensitive operational deployments, also document who owns the service, where logs are collected, how DLQ alerts are handled, how output directories or Oracle tables are backed up, and which team is allowed to rotate NATS, Oracle, and encryption credentials.