Of course! Using a Python script to send data to Zabbix is a very common and powerful way to monitor custom metrics. The standard and most reliable method for this is by using the zabbix_sender command-line utility, which Python can execute.

Here's a comprehensive guide covering everything from the basics to advanced Python scripts.
Core Concept: Why zabbix_sender?
Zabbix is designed for high-frequency data collection. Running a new Python script for every single metric check would be inefficient and could overload your Zabbix server.
The zabbix_sender utility is optimized for this:
- Batch Processing: It can send hundreds or thousands of values in a single, efficient network packet.
- Reliability: It handles communication with the Zabbix server/proxy and reports success/failure for each item.
- Simplicity: It uses a simple, space-delimited text format for input.
Our Python script's job will be to:

- Collect the data we want to monitor.
- Format this data into the format
zabbix_senderexpects. - Execute the
zabbix_sendercommand with our formatted data. - (Optional) Parse the output to check if the data was successfully sent.
Part 1: Prerequisites
Before you start, ensure you have the following:
- Zabbix Server/Proxy: A running Zabbix instance.
- Zabbix Agent: A Zabbix agent installed on the machine where you'll run the Python script. This agent must be configured to allow active checks.
zabbix_senderBinary: This is usually included with the Zabbix agent package. You can verify its location:- On Debian/Ubuntu:
which zabbix_sender(usually/usr/bin/zabbix_sender) - On RHEL/CentOS:
which zabbix_sender(usually/usr/bin/zabbix_senderor/usr/local/bin/zabbix_sender)
- On Debian/Ubuntu:
- Zabbix Configuration File (
zabbix_agentd.conf): You need to know the location of this file (usually/etc/zabbix/zabbix_agentd.conf). It must contain the server's address:Server=192.168.1.100 # Your Zabbix server IP ServerActive=192.168.1.100 # For active checks Hostname=my-python-host # The exact hostname as defined in Zabbix
Part 2: The zabbix_sender Data Format
The zabbix_sender utility reads data from standard input (stdin). The format is one line per metric, with the following space-delimited fields:
<hostname> <key> <value> [<timestamp>]
<hostname>: The exact hostname of the host in Zabbix.<key>: The key you want to update (e.g.,custom.disk.space,custom.cpu.usage).<value>: The actual numerical value you are sending.<timestamp>(Optional): The Unix timestamp for the value. If omitted, the current time is used. It's good practice to let Zabbix handle the timestamp.
Example:
my-python-host custom.disk.space /dev/sda1 75.5

Part 3: Python Script Examples
Here are three examples, from the most basic to a more robust and reusable script.
Example 1: Basic Script (Hardcoded Values)
This is the simplest script. It sends a single, hardcoded value to demonstrate the concept.
import subprocess
import sys
# --- Configuration ---
ZABBIX_SENDER = "/usr/bin/zabbix_sender"
ZABBIX_CONFIG = "/etc/zabbix/zabbix_agentd.conf"
HOSTNAME = "my-python-host" # Must match the hostname in Zabbix
KEY = "custom.script.hello"
VALUE = "Hello from Python!"
# --- Data Preparation ---
# The data must be formatted as a string with a newline at the end
data_to_send = f"{HOSTNAME} {KEY} {VALUE}\n"
# --- Execution ---
try:
# We use a list of arguments for the command
command = [ZABBIX_SENDER, "-c", ZABBIX_CONFIG, "-i", "-"]
# The data is passed to stdin
process = subprocess.Popen(
command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
stdout, stderr = process.communicate(input=data_to_send)
if process.returncode == 0:
print("Data sent successfully.")
# You can print stdout to see the Zabbix sender's response
# print("Zabbix Sender Output:\n", stdout)
else:
print(f"Error sending data. Return code: {process.returncode}", file=sys.stderr)
print(f"Stderr: {stderr}", file=sys.stderr)
except FileNotFoundError:
print(f"Error: '{ZABBIX_SENDER}' not found. Is Zabbix agent installed?", file=sys.stderr)
except Exception as e:
print(f"An unexpected error occurred: {e}", file=sys.stderr)
Example 2: Script to Monitor Disk Usage
This script calculates the used disk space for a specific partition and sends it to Zabbix.
import subprocess
import sys
import shutil
# --- Configuration ---
ZABBIX_SENDER = "/usr/bin/zabbix_sender"
ZABBIX_CONFIG = "/etc/zabbix/zabbix_agentd.conf"
HOSTNAME = "my-python-host"
KEY = "custom.disk.space.root" # Example key
PATH_TO_CHECK = "/" # The partition to check
# --- Data Collection ---
def get_disk_usage(path):
"""Calculates disk usage percentage for a given path."""
try:
total, used, free = shutil.disk_usage(path)
# Calculate percentage, handle division by zero
usage_percent = (used / total) * 100
return round(usage_percent, 2)
except Exception as e:
print(f"Error calculating disk usage for {path}: {e}", file=sys.stderr)
return None
# --- Main Execution ---
disk_usage = get_disk_usage(PATH_TO_CHECK)
if disk_usage is not None:
data_to_send = f"{HOSTNAME} {KEY} {disk_usage}\n"
try:
command = [ZABBIX_SENDER, "-c", ZABBIX_CONFIG, "-i", "-"]
process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
stdout, stderr = process.communicate(input=data_to_send)
if process.returncode == 0:
print(f"Successfully sent disk usage for {PATH_TO_CHECK}: {disk_usage}%")
else:
print(f"Failed to send data. Return code: {process.returncode}", file=sys.stderr)
print(f"Stderr: {stderr}", file=sys.stderr)
except FileNotFoundError:
print(f"Error: '{ZABBIX_SENDER}' not found.", file=sys.stderr)
except Exception as e:
print(f"An unexpected error occurred: {e}", file=sys.stderr)
else:
print("Could not retrieve disk usage, aborting send.", file=sys.stderr)
Example 3: Advanced Script (Reusable Function & Error Parsing)
This version is more robust. It includes a reusable function and parses the zabbix_sender output to get a clear success/failure status for each item.
import subprocess
import sys
import shutil
# --- Configuration ---
ZABBIX_SENDER = "/usr/bin/zabbix_sender"
ZABBIX_CONFIG = "/etc/zabbix/zabbix_agentd.conf"
HOSTNAME = "my-python-host"
def send_zabbix_data(metrics_list):
"""
Sends a list of metrics to Zabbix using zabbix_sender.
metrics_list should be a list of strings, e.g., ["host key value", "host2 key2 value2"]
Returns True on success, False on failure.
"""
if not metrics_list:
print("No metrics to send.", file=sys.stderr)
return False
# Join all metrics with newlines
data_string = "\n".join(metrics_list) + "\n"
command = [ZABBIX_SENDER, "-c", ZABBIX_CONFIG, "-i", "-"]
try:
process = subprocess.Popen(
command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
stdout, stderr = process.communicate(input=data_string)
if process.returncode == 0:
print("Zabbix Sender executed successfully.")
# Check the detailed output for processing errors
# A successful batch can still have individual item failures
for line in stdout.splitlines():
if "processed:" in line:
parts = line.split()
# Example: "my-host custom.key1 1 (OK) processed: 1; failed: 0; total: 1"
if int(parts[-3]) > 0:
print(f"Warning: Some items failed to process. Details: {line}")
return True
else:
print(f"Error: zabbix_sender failed with return code {process.returncode}", file=sys.stderr)
if stderr:
print(f"Stderr: {stderr}", file=sys.stderr)
return False
except FileNotFoundError:
print(f"Error: '{ZABBIX_SENDER}' not found.", file=sys.stderr)
return False
except Exception as e:
print(f"An unexpected error occurred: {e}", file=sys.stderr)
return False
# --- Main Execution ---
if __name__ == "__main__":
# --- Data Collection ---
root_disk_usage = round((shutil.disk_usage("/").used / shutil.disk_usage("/").total) * 100, 2)
cpu_load = 45.5 # Example: get this from psutil or /proc/loadavg
# --- Prepare data list ---
metrics = [
f"{HOSTNAME} custom.disk.space.root {root_disk_usage}",
f"{HOSTNAME} custom.cpu.load {cpu_load}"
]
# --- Send Data ---
if send_zabbix_data(metrics):
print("All metrics processed and sent.")
else:
print("Failed to send one or more metrics.", file=sys.stderr)
sys.exit(1)
Part 4: Zabbix Configuration
To receive this data, you must configure a Zabbix template.
-
Create a Template:
- Go to Configuration -> Templates and create a new template (e.g., "Template Custom Python Metrics").
-
Create Items:
-
Go to the "Items" tab in your new template and create an item for each key you send.
-
Name:
CPU Load (Python Script) -
Key:
custom.cpu.load -
Type:
Zabbix trapper -
Data type:
Numeric (float) -
Update interval:
0(This is crucial! For trapper items, set to 0. The script controls the frequency.) -
History/Storage: Configure as needed.
-
Create another item:
-
Name:
Root Disk Space (Python Script) -
Key:
custom.disk.space.root -
Type:
Zabbix trapper -
Data type:
Numeric (float)
-
-
Create Triggers (Optional but Recommended):
Create a trigger for the disk space item to alert if usage is > 90%.
-
Link the Template:
- Link this template to the target host (
my-python-host).
- Link this template to the target host (
Part 5: Automation
Finally, you need to run your Python script periodically. The best way to do this is with a cron job.
-
Make your Python script executable:
chmod +x /path/to/your/zabbix_python_sender.py
-
Open the crontab for editing:
crontab -e
-
Add a line to run the script every 5 minutes. For example:
*/5 * * * * /usr/bin/python3 /path/to/your/zabbix_python_sender.py >/dev/null 2>&1
*/5 * * * *: At every 5th minute./usr/bin/python3: The path to your Python interpreter.>/dev/null 2>&1: This suppresses the standard output and error, so you don't get emails from cron for every run. You might want to redirect output to a log file for debugging initially (/var/log/zabbix_python_script.log 2>&1).
