This chapter describes the installation of a production-ready Zabbix monitoring environment.
The reference architecture uses:
The exact package names and repository paths may differ between operating system and Zabbix versions.
Before installation, verify the currently supported operating systems, database platforms and package repositories for the selected Zabbix release.
The basic deployment described in this chapter uses a single Linux server.
Administrator
│
▼
Web Browser
│
HTTPS
│
▼
Nginx Frontend
│
▼
Zabbix Frontend
│
▼
PostgreSQL
▲
│
Zabbix Server
│
▼
Monitored Systems
For smaller environments, the following components may run on the same virtual machine:
For larger deployments, the database and frontend may be separated.
The required resources depend on:
A small deployment may start with:
| Resource | Recommended minimum |
|---|---|
| CPU | 2 vCPU |
| Memory | 4 GB RAM |
| Storage | 40 GB SSD |
| Network | 1 Gbit/s virtual or physical interface |
A more practical production baseline is:
| Resource | Recommended baseline |
|---|---|
| CPU | 4 vCPU |
| Memory | 8 GB RAM |
| Storage | 80–150 GB SSD |
| Network | 1 Gbit/s |
| Backup | External database and configuration backup |
Storage should be increased according to history retention and collected values per second.
Example server parameters:
Hostname: ZBX-SRV-01
Operating system: Debian
IP address: 192.168.10.20
Subnet mask: 255.255.255.0
Default gateway: 192.168.10.1
DNS server: 192.168.10.10
Timezone: Europe/Budapest
Recommended DNS records:
zabbix.example.local
monitoring.example.local
For public or remote administrative access, use a valid domain name and trusted TLS certificate.
Before installing Zabbix, verify the following:
Update the installed packages before adding the Zabbix repository.
apt update
apt full-upgrade -y
Remove packages that are no longer required:
apt autoremove -y
Verify the operating system version:
cat /etc/os-release
Verify the kernel:
uname -a
A reboot may be required after kernel or system library updates.
reboot
Set the server hostname:
hostnamectl set-hostname ZBX-SRV-01
Verify it:
hostnamectl
Update /etc/hosts:
nano /etc/hosts
Example:
127.0.0.1 localhost
127.0.1.1 ZBX-SRV-01
192.168.10.20 ZBX-SRV-01 zabbix.example.local
Test local resolution:
getent hosts ZBX-SRV-01
getent hosts zabbix.example.local
Set the correct timezone:
timedatectl set-timezone Europe/Budapest
Verify time configuration:
timedatectl
Expected output should confirm:
Time zone: Europe/Budapest
System clock synchronized: yes
NTP service: active
Accurate time synchronization is critical for:
Install Chrony:
apt install chrony -y
Enable and start the service:
systemctl enable --now chrony
Verify synchronization:
chronyc tracking
Display configured time sources:
chronyc sources -v
The exact configuration method depends on the Debian installation and network stack.
Possible implementations include:
/etc/network/interfaces;Example using /etc/network/interfaces:
nano /etc/network/interfaces
Example configuration:
auto ens18
iface ens18 inet static
address 192.168.10.20/24
gateway 192.168.10.1
dns-nameservers 192.168.10.10 1.1.1.1
Restart networking carefully:
systemctl restart networking
When connected remotely, changing network configuration may terminate the SSH session.
Verify:
ip address show
ip route show
resolvectl status
Test connectivity:
ping -c 4 192.168.10.1
ping -c 4 1.1.1.1
ping -c 4 deb.debian.org
Install common administrative and troubleshooting packages:
apt install -y \
curl \
wget \
gnupg \
ca-certificates \
lsb-release \
nano \
vim \
unzip \
tar \
jq \
net-tools \
iproute2 \
dnsutils \
traceroute \
tcpdump \
htop \
sudo
These tools are useful for:
A host-based firewall should permit only required connections.
Typical inbound ports include:
| Port | Protocol | Purpose |
|---|---|---|
| 22 | TCP | SSH administration |
| 80 | TCP | HTTP redirect or certificate validation |
| 443 | TCP | Zabbix frontend |
| 10050 | TCP | Local Zabbix agent passive checks |
| 10051 | TCP | Zabbix server and proxy communication |
The exact rules depend on the architecture.
Install UFW if required:
apt install ufw -y
Set default policies:
ufw default deny incoming
ufw default allow outgoing
Allow SSH from the management network:
ufw allow from 192.168.10.0/24 to any port 22 proto tcp
Allow HTTPS:
ufw allow 443/tcp
Allow HTTP if required:
ufw allow 80/tcp
Allow Zabbix server communication from authorized networks:
ufw allow from 192.168.10.0/24 to any port 10051 proto tcp
Enable the firewall:
ufw enable
Verify the rules:
ufw status verbose
Do not enable firewall rules without confirming that SSH access will remain available.
Download the repository package for the selected operating system and Zabbix release.
Example structure:
wget https://repo.zabbix.com/zabbix/<VERSION>/release/debian/pool/main/z/zabbix-release/zabbix-release_latest_<VERSION>+debian<DEBIAN_VERSION>_all.deb
Install the repository package:
dpkg -i zabbix-release_latest_*.deb
Update package metadata:
apt update
Verify that Zabbix packages are available:
apt-cache policy zabbix-server-pgsql
The repository URL must match:
Install PostgreSQL:
apt install postgresql postgresql-contrib -y
Enable and start PostgreSQL:
systemctl enable --now postgresql
Verify service status:
systemctl status postgresql
Check the installed version:
psql --version
List PostgreSQL clusters:
pg_lsclusters
Switch to the PostgreSQL administrative user:
sudo -u postgres psql
Create the Zabbix database user:
CREATE USER zabbix WITH PASSWORD 'CHANGE_THIS_STRONG_PASSWORD';
Create the database:
CREATE DATABASE zabbix OWNER zabbix;
Exit PostgreSQL:
\q
Use a strong, unique password.
Do not reuse:
Test authentication:
psql -h 127.0.0.1 -U zabbix -d zabbix
Enter the database password when prompted.
Verify the current connection:
\conninfo
Exit:
\q
If authentication fails, review:
/etc/postgresql/<VERSION>/main/pg_hba.conf
Common local authentication methods include:
scram-sha-256
md5
peer
After modifying PostgreSQL configuration:
systemctl restart postgresql
Install the required Zabbix components:
apt install -y \
zabbix-server-pgsql \
zabbix-frontend-php \
php8.2-pgsql \
zabbix-nginx-conf \
zabbix-sql-scripts \
zabbix-agent2
Package names may differ depending on:
Verify installed packages:
dpkg -l | grep zabbix
Locate the PostgreSQL schema file:
find /usr/share/zabbix-sql-scripts -type f | grep server.sql
The file may be compressed.
Example import command:
zcat /usr/share/zabbix-sql-scripts/postgresql/server.sql.gz \
| sudo -u zabbix psql zabbix
Alternatively:
zcat /usr/share/zabbix-sql-scripts/postgresql/server.sql.gz \
| psql -h 127.0.0.1 -U zabbix -d zabbix
The import may take several minutes depending on system performance.
Verify database tables:
sudo -u postgres psql -d zabbix -c "\dt"
Verify the table count:
sudo -u postgres psql -d zabbix -tAc \
"SELECT count(*) FROM information_schema.tables WHERE table_schema='public';"
A successful import creates numerous Zabbix tables.
Edit the server configuration:
nano /etc/zabbix/zabbix_server.conf
Configure the database settings:
DBHost=127.0.0.1
DBName=zabbix
DBUser=zabbix
DBPassword=CHANGE_THIS_STRONG_PASSWORD
Recommended initial settings may include:
LogFile=/var/log/zabbix/zabbix_server.log
LogFileSize=0
PidFile=/run/zabbix/zabbix_server.pid
SocketDir=/run/zabbix
Do not change performance-related process counts before establishing a baseline.
Examples of settings that may require later tuning:
StartPollers=
StartPreprocessors=
StartTrappers=
StartPingers=
StartDiscoverers=
CacheSize=
HistoryCacheSize=
HistoryIndexCacheSize=
TrendCacheSize=
ValueCacheSize=
Timeout=
Premature tuning can make troubleshooting more difficult.
Restrict access to the Zabbix server configuration:
chown root:zabbix /etc/zabbix/zabbix_server.conf
chmod 640 /etc/zabbix/zabbix_server.conf
Verify:
ls -l /etc/zabbix/zabbix_server.conf
Expected permissions:
-rw-r----- root zabbix
The configuration file contains sensitive database credentials and must not be world-readable.
Edit the Zabbix Nginx configuration:
nano /etc/zabbix/nginx.conf
Configure the listening port and hostname:
listen 80;
server_name zabbix.example.local;
Example configuration:
server {
listen 80;
server_name zabbix.example.local;
root /usr/share/zabbix;
index index.php;
location = /favicon.ico {
log_not_found off;
}
location / {
try_files $uri $uri/ =404;
}
location /assets {
access_log off;
expires 10d;
}
location ~ /\.ht {
deny all;
}
location ~ /(api\/|conf[^\.]|include|locale) {
deny all;
return 404;
}
location /vendor {
deny all;
return 404;
}
location ~ [^/]\.php(/|$) {
fastcgi_pass unix:/run/php/zabbix.sock;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_index index.php;
fastcgi_param DOCUMENT_ROOT /usr/share/zabbix;
fastcgi_param SCRIPT_FILENAME /usr/share/zabbix$fastcgi_script_name;
fastcgi_param PATH_TRANSLATED /usr/share/zabbix$fastcgi_script_name;
include fastcgi_params;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_intercept_errors on;
fastcgi_ignore_client_abort off;
fastcgi_connect_timeout 60;
fastcgi_send_timeout 180;
fastcgi_read_timeout 180;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
}
}
Test the Nginx configuration:
nginx -t
Locate the Zabbix PHP-FPM configuration:
ls -l /etc/zabbix/php-fpm.conf
Edit it:
nano /etc/zabbix/php-fpm.conf
Set the timezone:
php_value[date.timezone] = Europe/Budapest
Recommended PHP values may include:
php_value[max_execution_time] = 300
php_value[memory_limit] = 256M
php_value[post_max_size] = 32M
php_value[upload_max_filesize] = 16M
php_value[max_input_time] = 300
php_value[session.auto_start] = 0
php_value[mbstring.func_overload] = 0
php_value[always_populate_raw_post_data] = -1
Restart PHP-FPM after changes.
The exact service name depends on the installed PHP version.
Example:
systemctl restart php8.2-fpm
Enable and start the required services:
systemctl enable --now \
zabbix-server \
zabbix-agent2 \
nginx \
php8.2-fpm
Restart all components:
systemctl restart \
zabbix-server \
zabbix-agent2 \
nginx \
php8.2-fpm
Check service status:
systemctl status zabbix-server
systemctl status zabbix-agent2
systemctl status nginx
systemctl status php8.2-fpm
Check active listening sockets:
ss -tulpn
Expected ports may include:
TCP 80 Nginx
TCP 443 Nginx after HTTPS configuration
TCP 10050 Zabbix Agent 2
TCP 10051 Zabbix Server
TCP 5432 PostgreSQL, usually local only
TCP 22 SSH
Filter Zabbix ports:
ss -lntp | grep -E '10050|10051'
Verify Nginx:
ss -lntp | grep -E ':80|:443'
Display the latest server log entries:
tail -n 100 /var/log/zabbix/zabbix_server.log
Follow the log in real time:
tail -f /var/log/zabbix/zabbix_server.log
Using journalctl:
journalctl -u zabbix-server -n 100 --no-pager
Typical errors include:
Check Nginx errors:
tail -n 100 /var/log/nginx/error.log
Check access logs:
tail -n 100 /var/log/nginx/access.log
Check PHP-FPM:
journalctl -u php8.2-fpm -n 100 --no-pager
Common frontend problems include:
Open the Zabbix frontend:
http://zabbix.example.local
The installation wizard verifies:
During database configuration, enter:
Database type: PostgreSQL
Database host: 127.0.0.1
Database port: 5432
Database name: zabbix
Database user: zabbix
Database password: configured database password
Set the Zabbix server name:
SARABEL Monitoring
Complete the wizard.
The default Zabbix administrative account is commonly:
Username: Admin
Password: zabbix
Usernames may be case-sensitive.
Immediately after the first login:
Do not leave the default password unchanged.
Navigate to:
Users
→ Users
→ Create user
Example:
Username: joco.admin
Name: József
Surname: Takács
Role: Super admin role
User group: Zabbix administrators
Set a strong password.
After verifying the new account, the default Admin account should be disabled or protected according to organizational policy.
The Zabbix frontend should use HTTPS.
Possible certificate sources include:
Install Certbot for Nginx:
apt install certbot python3-certbot-nginx -y
Request a certificate:
certbot --nginx -d zabbix.example.com
Certbot can automatically:
Test renewal:
certbot renew --dry-run
Check the renewal timer:
systemctl status certbot.timer
Public certificates require:
Example Nginx configuration:
server {
listen 80;
server_name zabbix.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name zabbix.example.com;
ssl_certificate /etc/letsencrypt/live/zabbix.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/zabbix.example.com/privkey.pem;
root /usr/share/zabbix;
index index.php;
location / {
try_files $uri $uri/ =404;
}
location ~ [^/]\.php(/|$) {
fastcgi_pass unix:/run/php/zabbix.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME \
/usr/share/zabbix$fastcgi_script_name;
}
}
Test and reload Nginx:
nginx -t
systemctl reload nginx
Edit the Agent 2 configuration:
nano /etc/zabbix/zabbix_agent2.conf
Configure the server addresses:
Server=127.0.0.1,192.168.10.20
ServerActive=127.0.0.1
Hostname=ZBX-SRV-01
Important:
Hostname=
must match the host name created in the Zabbix frontend when active checks are used.
Restart the agent:
systemctl restart zabbix-agent2
Enable it:
systemctl enable zabbix-agent2
Check status:
systemctl status zabbix-agent2
Install zabbix-get:
apt install zabbix-get -y
Test agent availability:
zabbix_get -s 127.0.0.1 -k agent.ping
Expected output:
1
Test hostname:
zabbix_get -s 127.0.0.1 -k system.hostname
Test system uptime:
zabbix_get -s 127.0.0.1 -k system.uptime
Test CPU count:
zabbix_get -s 127.0.0.1 -k system.cpu.num
Test memory:
zabbix_get -s 127.0.0.1 -k vm.memory.size[total]
In the frontend, navigate to:
Data collection
→ Hosts
→ Create host
Configure:
Host name: ZBX-SRV-01
Visible name: Zabbix Server
Host groups:
- Zabbix servers
- Linux servers
Add an agent interface:
IP address: 127.0.0.1
Port: 10050
Assign templates:
Linux by Zabbix agent
Zabbix server health
Template names may differ between versions.
Save the host.
Navigate to:
Monitoring
→ Latest data
Filter by:
Host: ZBX-SRV-01
Verify that values are received for:
If values are missing, verify:
A monitoring platform must have a tested database backup.
Create a backup directory:
mkdir -p /var/backups/zabbix
chown postgres:postgres /var/backups/zabbix
chmod 700 /var/backups/zabbix
Create a manual backup:
sudo -u postgres pg_dump \
-Fc \
-d zabbix \
-f /var/backups/zabbix/zabbix_$(date +%F_%H-%M).dump
List backup files:
ls -lh /var/backups/zabbix
Verify the backup:
sudo -u postgres pg_restore \
--list \
/var/backups/zabbix/zabbix_YYYY-MM-DD_HH-MM.dump
A backup that has never been tested is not a verified backup.
Create a backup script:
nano /usr/local/sbin/zabbix-db-backup.sh
Example:
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/var/backups/zabbix"
DATABASE="zabbix"
RETENTION_DAYS="14"
TIMESTAMP="$(date +%F_%H-%M-%S)"
BACKUP_FILE="${BACKUP_DIR}/${DATABASE}_${TIMESTAMP}.dump"
mkdir -p "${BACKUP_DIR}"
sudo -u postgres pg_dump \
-Fc \
-d "${DATABASE}" \
-f "${BACKUP_FILE}"
find "${BACKUP_DIR}" \
-type f \
-name "${DATABASE}_*.dump" \
-mtime +"${RETENTION_DAYS}" \
-delete
echo "Backup completed: ${BACKUP_FILE}"
Make the script executable:
chmod 750 /usr/local/sbin/zabbix-db-backup.sh
Test it:
/usr/local/sbin/zabbix-db-backup.sh
Edit root crontab:
crontab -e
Example daily backup at 02:00:
0 2 * * * /usr/local/sbin/zabbix-db-backup.sh >> /var/log/zabbix-db-backup.log 2>&1
Verify scheduled jobs:
crontab -l
Database backups should also be copied to:
Keeping backups only on the Zabbix server does not protect against complete server failure.
Important configuration paths include:
/etc/zabbix/
/etc/nginx/
/etc/php/
/etc/postgresql/
/etc/letsencrypt/
/usr/lib/zabbix/
/usr/local/share/zabbix/
Example archive:
tar -czf /var/backups/zabbix/zabbix-config_$(date +%F).tar.gz \
/etc/zabbix \
/etc/nginx \
/etc/postgresql
Certificate directories may require separate handling due to sensitive private keys.
Create a temporary restore database:
sudo -u postgres createdb zabbix_restore_test
Restore the backup:
sudo -u postgres pg_restore \
-d zabbix_restore_test \
/var/backups/zabbix/zabbix_YYYY-MM-DD_HH-MM.dump
Verify the restored tables:
sudo -u postgres psql \
-d zabbix_restore_test \
-c "SELECT count(*) FROM hosts;"
Delete the test database:
sudo -u postgres dropdb zabbix_restore_test
Restore tests should be performed regularly.
Zabbix packages normally install logrotate configuration.
Verify:
cat /etc/logrotate.d/zabbix-server
cat /etc/logrotate.d/zabbix-agent2
Test logrotate configuration:
logrotate -d /etc/logrotate.conf
Check log file sizes:
du -sh /var/log/zabbix
du -sh /var/log/nginx
du -sh /var/log/postgresql
Uncontrolled log growth can exhaust the root filesystem.
Recommended SSH controls include:
Edit SSH configuration:
nano /etc/ssh/sshd_config
Example settings:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AllowUsers monitoringadmin
Validate the configuration:
sshd -t
Restart SSH:
systemctl restart ssh
Do not disable password authentication before confirming that key-based login works.
Create a named administrative account:
adduser monitoringadmin
Grant sudo access:
usermod -aG sudo monitoringadmin
Verify:
id monitoringadmin
Configure SSH keys:
mkdir -p /home/monitoringadmin/.ssh
nano /home/monitoringadmin/.ssh/authorized_keys
Set permissions:
chown -R monitoringadmin:monitoringadmin \
/home/monitoringadmin/.ssh
chmod 700 /home/monitoringadmin/.ssh
chmod 600 /home/monitoringadmin/.ssh/authorized_keys
List enabled services:
systemctl list-unit-files --state=enabled
List listening ports:
ss -tulpn
Disable unused services only after confirming their purpose.
Example:
systemctl disable --now <service-name>
Do not disable:
Install unattended upgrades:
apt install unattended-upgrades apt-listchanges -y
Configure:
dpkg-reconfigure unattended-upgrades
Review:
/etc/apt/apt.conf.d/50unattended-upgrades
/etc/apt/apt.conf.d/20auto-upgrades
Automatic upgrades should be aligned with the organization’s change-management policy.
Critical production systems may require:
Check PostgreSQL service:
systemctl status postgresql
Check database size:
sudo -u postgres psql -d zabbix -c \
"SELECT pg_size_pretty(pg_database_size('zabbix'));"
Check active sessions:
sudo -u postgres psql -d zabbix -c \
"SELECT count(*) FROM pg_stat_activity;"
Check long-running queries:
sudo -u postgres psql -d zabbix -c "
SELECT
pid,
now() - query_start AS duration,
state,
query
FROM pg_stat_activity
WHERE state <> 'idle'
ORDER BY duration DESC;
"
In the frontend, review:
Monitoring
→ Hosts
→ Zabbix Server
Important internal metrics include:
Persistently high process utilization may require:
Use the following commands for a final system check.
hostnamectl
timedatectl
uptime
df -h
free -h
ip address show
ip route show
ss -tulpn
ping -c 4 192.168.10.1
systemctl is-active postgresql
systemctl is-active nginx
systemctl is-active php8.2-fpm
systemctl is-active zabbix-server
systemctl is-active zabbix-agent2
sudo -u postgres psql -d zabbix -c "SELECT version();"
zabbix_get -s 127.0.0.1 -k agent.ping
curl -I http://127.0.0.1
After HTTPS configuration:
curl -I https://zabbix.example.com
Check:
systemctl status zabbix-server
journalctl -u zabbix-server -n 100 --no-pager
tail -n 100 /var/log/zabbix/zabbix_server.log
Common causes:
Test manually:
psql -h 127.0.0.1 -U zabbix -d zabbix
Verify:
DBHost=
DBName=
DBUser=
DBPassword=
Check PostgreSQL authentication:
cat /etc/postgresql/*/main/pg_hba.conf
Check:
tail -n 100 /var/log/nginx/error.log
journalctl -u php8.2-fpm -n 100 --no-pager
Common causes:
Check:
systemctl status zabbix-agent2
ss -lntp | grep 10050
Test locally:
zabbix_get -s 127.0.0.1 -k agent.ping
Verify:
Server=
ServerActive=
Hostname=
Check the firewall:
ufw status verbose
Active checks require an exact hostname match.
Agent configuration:
Hostname=ZBX-SRV-01
Frontend host name:
ZBX-SRV-01
These values must match exactly.
Run:
nginx -t
Review the reported file and line number.
Common causes:
listen directive;For production use:
A successful Zabbix installation requires more than installing software packages.
The deployment must include:
After installation, the next step is to create, organize and manage monitored hosts.