Skip to main content

Using Signal for Zabbix Alerts with signal-cli

Overview

This document explains how to send Zabbix alerts using Signal by integrating the signal-cli command line client. The method relies on a dedicated Signal account on the server and a simple alert script executed by Zabbix.

Characteristics of this setup:

  • End-to-end encrypted alert delivery
  • No external SaaS notification provider
  • Fully self-hosted
  • Suitable for infrastructure alerts with low to moderate volume

Environment assumed:

  • Ubuntu 22.04 (Jammy)
  • Zabbix Server
  • signal-cli native binary
  • Python alert script

Architecture

Alert flow:

  1. Zabbix trigger fires
  2. Zabbix executes an alert script
  3. The alert script calls signal-cli
  4. signal-cli sends a Signal message

Typical layout:

signal-cli binary
/usr/local/bin/signal-cli

Signal account data
/var/lib/zabbix/.local/share/signal-cli/

Zabbix alert scripts
/usr/lib/zabbix/alertscripts/

Important Notes

signal-cli is not an official Signal client

signal-cli is a third-party implementation of the Signal protocol.

Implications:

  • no official support
  • protocol changes may require upgrades
  • the binary should be periodically updated

Use a dedicated Signal number

Recommended practice:

  • register a phone number dedicated to alerting

Advantages:

  • separation from personal chats
  • simpler operational management
  • easier to re-register if needed

Signal account state is user-specific

Signal account data is stored under the home directory of the user running signal-cli.

For the Zabbix user this will be:

/var/lib/zabbix/.local/share/signal-cli/

The binary itself can remain owned by root.


Requirements

Server requirements:

  • Zabbix server installed
  • root shell access
  • outbound internet access

External requirements:

  • phone number capable of receiving SMS
  • Signal installed on the receiving device

Recommended:

  • dedicated alerting number
  • cron enabled
  • regular backups of the Signal account directory

Installing signal-cli

Use the native binary build on Ubuntu 22.04 to avoid Java runtime issues.

Install:

cd /opt

VERSION=$(curl -Ls -o /dev/null -w %{url_effective} https://github.com/AsamK/signal-cli/releases/latest | sed -e 's/^.*\/v//')

curl -L -O https://github.com/AsamK/signal-cli/releases/download/v"${VERSION}"/signal-cli-"${VERSION}"-Linux-native.tar.gz

tar xf signal-cli-"${VERSION}"-Linux-native.tar.gz -C /opt

ln -sf /opt/signal-cli /usr/local/bin/signal-cli

Verify:

signal-cli --version

Example output:

signal-cli 0.14.0

Registering the Signal Account

Register the sending phone number.

Example:

signal-cli -a +37212345678 register

CAPTCHA Registration

Signal often requires CAPTCHA verification when registering from servers.

Generate a token at:

https://signalcaptchas.org/registration/generate.html

After solving the CAPTCHA you will receive a redirect URI:

signalcaptcha://signal-hcaptcha.<TOKEN>

Extract only the token portion:

signal-hcaptcha.<TOKEN>

Register using:

signal-cli -a +37212345678 register --captcha signal-hcaptcha.<TOKEN>

Verify the SMS Code

When the verification SMS arrives:

signal-cli -a +37212345678 verify 123456

If successful the Signal account is registered.


Optional Profile Name

Set a profile name:

signal-cli -a +37212345678 updateProfile --name "Zabbix Alerts"

Moving the Signal Account to the Zabbix User

If registration was done as root, the account will exist under:

/root/.local/share/signal-cli/

Move it to the Zabbix home directory.

Create directories:

mkdir -p /var/lib/zabbix/.local/share

Move data:

mv /root/.local/share/signal-cli /var/lib/zabbix/.local/share/

Fix permissions:

chown -R zabbix:zabbix /var/lib/zabbix/.local

Testing signal-cli as the Zabbix User

Send a test message:

sudo -u zabbix env HOME=/var/lib/zabbix signal-cli \
-a +37212345678 send \
-m "Test from zabbix user" \
+37298765432

If the message arrives the account migration is correct.


Sending to a Signal Group

The sending account must be a member of the group.

List groups:

signal-cli -a +37212345678 listGroups

Example output:

Id: 9a3c9c5b2f2f4e1e8d5c2e
Name: Zabbix Alerts

Send to group:

signal-cli -a +37212345678 send \
-m "Alert message" \
-g 9a3c9c5b2f2f4e1e8d5c2e

Periodic Receive

Signal expects devices to occasionally receive messages.

If the client only sends messages, logs may show:

INFO AccountHelper - The Signal protocol expects that incoming messages are regularly received.

Run periodic receive jobs.

Manual test:

signal-cli -a +37212345678 receive

Cron example:

*/30 * * * * sudo -u zabbix env HOME=/var/lib/zabbix signal-cli -a +37212345678 receive >/dev/null 2>&1

Creating the Zabbix Alert Script

Create the script:

/usr/lib/zabbix/alertscripts/signal.py

Example:

#!/usr/bin/env python3
import os
import sys
import subprocess

os.environ["HOME"]="/var/lib/zabbix"

SIGNAL_ACCOUNT="+37212345678"
TO_NUMBER="+37298765432"

def main():

    if len(sys.argv) < 4:
        return 1

    host=sys.argv[1]
    alert_message=sys.argv[2]
    event_name=sys.argv[3]

    message=f"{event_name}\nHost: {host}\n\n{alert_message}"

    cmd=[
        "/usr/local/bin/signal-cli",
        "-a",SIGNAL_ACCOUNT,
        "send",
        "-m",message,
        TO_NUMBER
    ]

    result=subprocess.run(cmd,capture_output=True,text=True)

    return result.returncode

if __name__=="__main__":
    sys.exit(main())

Make executable:

chmod +x /usr/lib/zabbix/alertscripts/signal.py

Testing the Script

Manual test:

sudo -u zabbix /usr/lib/zabbix/alertscripts/signal.py \
"TestHost" \
"This is a test alert" \
"TEST ALERT"

Creating the Zabbix Media Type

In the Zabbix web interface:

Administration → Media Types → Create

Settings:

Name: Signal
Type: Script
Script name: signal.py

Script parameters:

{HOST.NAME}
{ALERT.MESSAGE}
{EVENT.NAME}

Assigning the Media to a User

In the Zabbix UI:

Users → Media → Add

Example:

Type: Signal
Send to: signal
When active: 1-7,00:00-24:00
Severity: all

The “Send to” field can contain any placeholder value if the script ignores it.


Adding Signal to an Action

Configuration → Actions → Trigger Actions

Add operation:

Send to users: target user
Send only to: Signal

Updating signal-cli Automatically

Create:

/usr/local/sbin/update-signal-cli.sh

Script:

#!/usr/bin/env bash
set -euo pipefail

INSTALL_PATH="/opt/signal-cli"
BIN_LINK="/usr/local/bin/signal-cli"

TMPDIR=$(mktemp -d)
trap "rm -rf $TMPDIR" EXIT

VERSION=$(curl -Ls -o /dev/null -w %{url_effective} https://github.com/AsamK/signal-cli/releases/latest | sed -e 's/^.*\/v//')

cd "$TMPDIR"

curl -L -O https://github.com/AsamK/signal-cli/releases/download/v${VERSION}/signal-cli-${VERSION}-Linux-native.tar.gz

tar xf signal-cli-${VERSION}-Linux-native.tar.gz -C /opt

ln -sf "$INSTALL_PATH" "$BIN_LINK"

signal-cli --version

Make executable:

chmod +x /usr/local/sbin/update-signal-cli.sh

Test:

/usr/local/sbin/update-signal-cli.sh

Cron example:

15 4 * * 0 /usr/local/sbin/update-signal-cli.sh >> /var/log/update-signal-cli.log 2>&1

Backing Up the Signal Account

Critical directory:

/var/lib/zabbix/.local/share/signal-cli/

Backup example:

tar czf /root/signal-cli-backup.tar.gz -C /var/lib/zabbix/.local/share signal-cli

Restoring this directory restores the Signal identity.


Common Problems

UnsupportedClassVersionError

Cause:

Using Java build without the required Java version.

Fix:

Use the native binary release.


failed to read local accounts list

Cause:

Incorrect permissions or wrong HOME directory.

Fix:

Ensure ownership:

chown -R zabbix:zabbix /var/lib/zabbix/.local

Ensure scripts set:

HOME=/var/lib/zabbix

Messages send manually but not from Zabbix

Common causes:

  • wrong script parameter order
  • script not executable
  • media type not used by action
  • severity filter mismatch

Cannot send to group

Cause:

The sending account is not a member of the group.

Solution:

Add the sending number to the Signal group.


Security Considerations

Signal provides:

  • strong end-to-end encryption
  • reliable message delivery

However:

  • signal-cli must be kept updated
  • backups of the Signal account directory are critical
  • if the account directory is lost the identity may need re-registration

← Back to blog