Overview
Prometheus and Grafana are great for time-series metrics and dashboards, but they leave you to build alerting logic and host inventory yourself. Zabbix takes the opposite approach: it ships as a single opinionated platform with agent-based polling, a built-in trigger/action engine, host inventory, network discovery, and alerting — all configured through one web frontend, no separate rules engine required.
This project deploys a complete Zabbix 7.0 LTS stack in Docker (the current long-term-support line, patched through 7.0.19), monitors a mix of Linux hosts and Docker containers with Zabbix Agent 2, adds an SNMP-polled network device, and routes trigger-based alerts to a Discord channel. By the end you'll have a single pane of glass for host health, service availability, and infrastructure alerting that runs entirely on your own hardware.
What you'll build:
- Zabbix server + web frontend + PostgreSQL 16 in Docker Compose
- Zabbix Agent 2 monitoring the Docker host and its containers
- A remote Linux host monitored over active checks
- An SNMP-polled network device (router/switch/UPS)
- Trigger-based Discord alerting via a custom webhook media type
Architecture
┌────────────────────────────────────────────────┐
│ Monitoring Host (Docker) │
│ │
│ ┌────────────┐ ┌────────────┐ ┌───────────┐│
│ │ zabbix-web │◄─►│zabbix-server│◄─►│ postgres ││
│ │ (nginx) │ │ :10051 │ │ :5432 ││
│ │ :8080 │ └──────┬─────┘ └───────────┘│
│ └────────────┘ │ │
└───────────────────────────┼──────────────────────┘
│ active/passive checks
┌──────────────┼──────────────┐
▼ ▼ ▼
┌───────────────┐ ┌──────────┐ ┌───────────────┐
│ Docker host │ │ Linux VM │ │ SNMP device │
│ Zabbix Agent 2│ │ Agent 2 │ │ (switch/UPS) │
│ + containers │ │ │ │ no agent needed│
└───────────────┘ └──────────┘ └───────────────┘
│
▼
┌───────────────┐
│ Discord Webhook│
│ (trigger action)│
└───────────────┘
The server component evaluates incoming item data against triggers and fires actions — in this build, an HTTP webhook media type that posts formatted alerts to Discord. Agents run in active mode by default in this guide, meaning each host pulls its own check configuration from the server and pushes data back, which works cleanly through NAT and firewalls without opening inbound ports to every monitored host.
Prerequisites
- Docker Engine 24+ and Docker Compose v2
- 2 GB RAM minimum for the server stack (4 GB recommended once dashboards and history grow)
- A Linux host or VM to monitor remotely (Agent 2 install)
- Optional: an SNMP-capable switch, router, or UPS on the same network
- A Discord server with permission to create webhooks
Step 1 — Docker Compose Stack
Create a project directory and a .env file for credentials:
mkdir zabbix-stack && cd zabbix-stack
cat > .env <<'EOF'
POSTGRES_USER=zabbix
POSTGRES_PASSWORD=CHANGE_ME_STRONG_PASSWORD
POSTGRES_DB=zabbix
TZ=America/Edmonton
EOFCreate docker-compose.yml:
services:
postgres-server:
image: postgres:16-alpine
container_name: zabbix-postgres
restart: unless-stopped
env_file: .env
volumes:
- zbx-postgres-data:/var/lib/postgresql/data
zabbix-server:
image: zabbix/zabbix-server-pgsql:7.0-ubuntu-latest
container_name: zabbix-server
restart: unless-stopped
env_file: .env
environment:
- DB_SERVER_HOST=postgres-server
ports:
- "10051:10051"
volumes:
- zbx-server-data:/var/lib/zabbix
depends_on:
- postgres-server
zabbix-web:
image: zabbix/zabbix-web-nginx-pgsql:7.0-ubuntu-latest
container_name: zabbix-web
restart: unless-stopped
env_file: .env
environment:
- DB_SERVER_HOST=postgres-server
- ZBX_SERVER_HOST=zabbix-server
- PHP_TZ=America/Edmonton
ports:
- "8080:8080"
depends_on:
- zabbix-server
zabbix-agent:
image: zabbix/zabbix-agent2:7.0-ubuntu-latest
container_name: zabbix-agent
restart: unless-stopped
env_file: .env
environment:
- ZBX_HOSTNAME=docker-host
- ZBX_SERVER_HOST=zabbix-server
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /:/rootfs:ro
group_add:
- "988" # docker group GID on the host — check with `getent group docker`
volumes:
zbx-postgres-data:
zbx-server-data:Bring it up:
docker compose up -d
docker compose logs -f zabbix-serverWait for server #0 started [main process] before continuing. First boot runs the database schema import and takes 1-2 minutes.
Step 2 — Frontend Setup Wizard
Open http://YOUR_HOST:8080. The setup wizard confirms the database connection (pre-filled from environment variables) and creates the default Admin account with password zabbix.
Immediately change the default password: Administration → Users → Admin → change password. The default credential is public knowledge and internet-facing Zabbix frontends running it are routinely scanned.
Once logged in, go to Data collection → Hosts and find the pre-seeded Zabbix server host. It polls 127.0.0.1:10050 for local agent data that doesn't exist in this container layout — either disable it or repoint its interface, otherwise you'll get a permanent "unreachable" alert:
Data collection → Hosts → Zabbix server → Interfaces → remove or leave disabled if you won't monitor the server container itself.
Step 3 — Monitor the Docker Host and Containers
The zabbix-agent container is already running with access to the Docker socket. Register it as a host:
Data collection → Hosts → Create host
- Host name:
docker-host(must matchZBX_HOSTNAMEabove) - Templates:
Linux by Zabbix agentandDocker by Zabbix agent 2 - Interfaces: Agent,
zabbix-agent, port10050(or leave blank for active-only checks) - Host groups:
Docker Hosts
Save, then check Monitoring → Latest data, filter by docker-host. Within a minute you should see CPU, memory, filesystem, and per-container metrics (docker.container_info, docker.container_stats) populating.
If data doesn't appear, check the agent log:
docker compose logs zabbix-agent | grep -i "failed\|error"A common cause is the docker group GID mismatch — confirm it with getent group docker on the host and update group_add in the compose file to match.
Step 4 — Add a Remote Linux Host
On the target Linux machine, install Agent 2 directly (not in Docker):
wget https://cdn.zabbix.com/zabbix/binaries/stable/7.0/latest/zabbix_agent2-7.0-linux-3.0-amd64-static.tar.gz
tar xzf zabbix_agent2-*.tar.gz -C /opt/Edit /opt/zabbix_agent2/etc/zabbix_agent2.conf:
Server=YOUR_ZABBIX_SERVER_IP
ServerActive=YOUR_ZABBIX_SERVER_IP
Hostname=linux-vm-01Start it (or install as a systemd service using the package repo instead of the static tarball for production use). In the frontend, create a matching host linux-vm-01, assign the Linux by Zabbix agent template, and set the interface type to Agent (active checks) since this host initiates outbound connections rather than accepting inbound polls.
Step 5 — SNMP Monitoring for Network Devices
Most switches, routers, and UPS units expose SNMP without needing an agent installed. Create a host for one:
Data collection → Hosts → Create host
- Host name:
core-switch-01 - Interfaces: SNMP, target IP, port
161, SNMP version 2 (or 3 if your gear supports it — prefer v3 with auth+priv for anything beyond a fully trusted LAN) - SNMP community: your device's read-only community string (default
publicshould be changed on the device itself) - Templates: search for a vendor-matched template, e.g.
Generic SNMPas a baseline, or a specific one shipped for your switch/UPS model
Zabbix's built-in template library covers most common network gear. For unsupported devices, use Configuration → Discovery with an SNMP OID walk to build a custom item set from whatever the device exposes.
Step 6 — Discord Alerting via Webhook
Create a Discord webhook: Server Settings → Integrations → Webhooks → New Webhook, copy the URL.
In Zabbix: Alerts → Media types → Import, or build one manually as a Webhook media type with this JavaScript body (paste into the media type's Script field):
try {
var params = JSON.parse(value),
req = new HttpRequest(),
payload = {
content: null,
embeds: [{
title: params.subject,
description: params.message,
color: params.status === 'PROBLEM' ? 15548997 : 5763719
}]
};
req.addHeader('Content-Type: application/json');
req.post(params.webhook_url, JSON.stringify(payload));
if (req.getStatus() >= 300) {
throw 'Discord webhook returned status ' + req.getStatus();
}
return 'OK';
} catch (error) {
Zabbix.log(4, '[Discord webhook] ' + error);
throw 'Discord notification failed: ' + error;
}Add parameters webhook_url, subject, message, status mapped to Zabbix macros ({ALERT.SUBJECT}, {ALERT.MESSAGE}, {EVENT.NAME} etc. — customize per your trigger action). Then:
- Users → Admin → Media — add the Discord media type with your webhook URL
- Alerts → Actions → Trigger actions — create a new action, condition
Trigger severity >= Average, operation "Send message" to the Admin user via the Discord media type
Testing Your Setup
Force a test problem to confirm the pipeline end to end:
# Fill disk temporarily on the monitored host to cross a threshold trigger
fallocate -l 5G /tmp/zbx-test-fileWatch Monitoring → Problems in the frontend — a disk space trigger should appear within one polling interval (default 60s for most templated items). Confirm the Discord message arrives in the target channel, then clean up:
rm /tmp/zbx-test-fileThe problem should auto-resolve in Zabbix and (if you configured a recovery message) post a second Discord message confirming resolution.
Also verify agent connectivity directly, bypassing the frontend:
docker exec zabbix-server zabbix_get -s docker-host -k agent.ping
# Expect: 1Deployment and Hardening
- Put the frontend behind a reverse proxy with TLS (Traefik, Caddy, Nginx) — don't expose port 8080 directly to the internet
- Restrict frontend access to your management VLAN or VPN; Zabbix's frontend has historically been a target for CVEs (SQL injection, auth bypass) — keep it patched and firewalled regardless
- Back up the PostgreSQL volume on a schedule — host configuration, templates, and history all live there:
docker exec zabbix-postgres pg_dump -U zabbix zabbix | gzip > zabbix-backup-$(date +%F).sql.gz - Rotate the default SNMP community string and prefer SNMPv3 for anything beyond a trusted LAN segment
- Use Zabbix API tokens, not user passwords, for any automation or scripts that query the API — generate under Users → API tokens
Extensions and Next Steps
Auto-discovery: Configure a network discovery rule (Data collection → Discovery) to sweep a subnet and auto-register hosts responding to ICMP/SNMP, reducing manual host creation as your homelab grows.
Low-level discovery (LLD): Extend the Docker template with custom LLD rules to auto-discover containers by label, so new containers get monitored the moment they start without manual host edits.
Grafana integration: Zabbix's history data can feed Grafana via the community Zabbix data source plugin if you want richer dashboards layered on top of Zabbix's own alerting — pairing the Prometheus/Grafana stack for metrics with Zabbix for agent-based inventory and alerting is a common hybrid pattern.
Maintenance windows via automation: Script Zabbix maintenance windows around planned deploys or reboots using the API, so trigger actions don't fire false alerts during expected downtime — useful if you're already scripting container recreates or patch cycles elsewhere in your stack.
PDF reporting: Zabbix 6.4+ supports native scheduled PDF reports of dashboards for stakeholders who want a weekly summary without frontend access — requires the optional zabbix-web-service container alongside the core stack.