Setting up signal-cli on Linux

Icinga2-Notifications and Signal Bot Concepts

Yannik Schmidt
5 min readOct 4, 2021

For lack of alternatives Telegram had been my messenger of choice until Signal became more widespread, mainly thanks to the What’s App privacy policy changes. However I found myself still using a Telegram bot for my server monitoring notifications. Now it’s time to move to signal for this as well and wave telegram bye, bye.

signal-cli Setup

Setting up the Signal console client is a bit clunky, but not hard. First you need a Linux distribution which features glibc>=2.29, check this first with ldd --version.As always my distribution is Debian (Bullseye), if you are using Arch or some other distribution, you can check, if the library requirements which I will build from scratch, may already be packaged. You also need a phone number, which isn’t registered with Signal yet, to which you may receive calls or text messages once at registration. I just used my landline.

You will need the following repositories:

You also need the Java-11 JRE or higher, rust and the gradle build system.

Go to the signal-cli project-root and build it:

Build signal-cli

Go to libsignal-client project-root, change to the java-directory and make sure to remove android from the build options, otherwise this will take ages:

Build libsignal

Go to zkgroup project-root and build it:

You have to make the built libraries available for Java, either copy them to the java-library path (make sure they are readable for all users) or add them to the LD_LIBRARY_PATH environment variable, whenever you intend to use the signal-cli binary.

To get the default java-library-path execute:

Show java library path

Usually, on Linux, that’s /usr/java/packages/lib/, though this directory might not exist yet, so:

Move the built-libraries to the correct locations

Or do something along the lines of the following code snippet, to use the environment-variable solution:


Now go to signal-cli project-root. We will have to make some preparations. First, prepare your phone number, if you use a number which does not support SMS, use the --voice-switch to receive a call instead. Your full phone number means your number, including your country code (including a leading +) and including your area code (without any leading zeros).

You also need a captcha-token. For this open a browser tab first. Then open the developer console, then make sure to have ‘persist-logs’ on, and only after that navigate to the captcha-generator.

You may or may not actually have to solve a captcha. After you the check succeeded, you will likely get a pop-up to open signal, ignore that and look into the dev-console, there should be something similar to:


Copy everything after signalchaptcha:// and use it as the token for the --captcha-argument. Be advised, the token isn't valid very long:

Register account with signal-cli

You will now get a SMS/call with the verification-code, which you can use with:

You should consider setting a pin directly after, for help with this and other options use. You have to use signal-cli receive regularly, otherwise your account will be flagged inactive and potentially deleted. You may omit the -u option if you only have registered one account with this user on this machine. Data (including private keys) is saved to ~/.local/share/signal-cli/. Here are some of the relevant commands:

signal-cli usage

Connecting Icinga2 Monitoring

Running the Gateway & Connecting Icinga2

Clone the signal-http-gateway repository.

Add the target number(s) (one per line) to a file called signal_targets.txt in the root directory of the project and execute the server:

Run the HTTP-Gateway Server for Icinga2 to connect to

HTTP Request

The HTTP request must be a POST-request, with Content-Type: application/json and a json-field containing the key “message” with the value being the message you want to send.

The following locations are supported:

Supported HTTP-Locations

You can test your setup with curl:

Send a POST request with curl to check your setup.

Configuring Icinga2

If you already followed the “Icinga Notifications in Telegram” tutorial in the past, then you don’t have to do anything! It will already work, for all those who are new to it, here is a rundown of how to configure Icinga.

The following part assumes you already have a working Icinga2-instance. Icinga-web is not needed. On most systems just installing Icinga via the package-manager will create a working instance ( apt install icinga2 on Debian). If you don't care about the explanation you can just clone the atlantishq monitoring-tools into /etc/icinga2/monitoring-tools/ and add an

Include Icinga2-config file

at the end of icinga.conf. Make sure the directory and it’s contents can be read by Icinga. Here’s a breakdown of the contents:

Notification Command

Notification Command

This is the “NotificationCommand”. It’s the actual command executed to send a notification, as you can see, we are just using curl to send a POST request to the server, which will send out the signal message.

However this gets very messy, very quickly so I created a small python-script that emulates the arguments of the mail notification script already in included Icinga, so you can just copy and paste the NotificationCommand for mail notifications (usually in notifications.conf in /etc/icinga2) and replace the path/name.

Notification Object

We then need to apply this notification to something:


By convention we import the mail-service-notification object as a basis, then we define user_groups for which we want to send notification. This (in our case) is not about whom to send notifications, but for which services we send notifications. Usually you just want the icingadmins group here because this group already exists by default and will get notifications for anything (unless you already changed this behavior). If you have a special notification group with additional filters, rules, etc, you can use this group instead (or use a single user with the “user = … “-directive).

Finally “ assign where host.address” is basically an “ assign everywhere” since every service likely has an address. Obviously you could use something more specific here, like assign where host.vars.signal_notifications. Please refer to the Icinga2 Documentation for information about the exact workings of the assign-directive.

Signal Messages arriving

If you liked this tutorial and want to see more, drop me a star on the GitHub project. If you have any questions or suggestions, just leave a comment or join the Discord.




Yannik Schmidt

Python programmer at heart, Linux Expert at work, GameDev on my good days, web developer on the bad days.