
Monitoring is the first thing people skip on a new server and the first thing they miss when something breaks at 3 a.m. A disk fills quietly, a certificate expires over a weekend, a database replica drifts behind, and nobody knows until a customer complains. Zabbix exists to stop that sequence. It is free, mature, and scales from a single VPS to tens of thousands of monitored hosts.
Ubuntu 26.04 LTS makes a solid base for it. The release ships MariaDB 11.8.6 with full support in main, MySQL 8.4, PHP 8.5.2 and Apache 2.4.65. There is one trap. Ubuntu’s own repositories carry a Zabbix package that stays frozen for the life of the release, so the sensible route is the official Zabbix repository.
The version question matters too. Zabbix 8.0 LTS is on the roadmap for November 2026, and release candidate 2 shipped on 21 October. At the time of writing, no final day had been announced. For a production box today, Zabbix 7.0 LTS is the safe, boring, correct choice. This guide uses it, with MariaDB and Apache, then covers HTTPS, firewall rules, tuning, and the errors that actually show up in real deployments.
Which Zabbix Version Should You Install?
Short answer: 7.0 LTS. Longer answer: it depends on how much risk the machine can carry.
Zabbix publishes LTS releases with long support windows. For 8.0, the stated lifecycle is full support until Q3 2029 and limited support until Q3 2031. That is the long-term destination. But one tutorial author put it plainly: on Ubuntu 26.04, the 8.0 repository package did not yet give a stable build when they wrote it. Another guide notes that the 8.0 release package also enables an unstable source where RC packages live. That is fine for a lab. It is not what anyone wants on a billing-critical monitoring server.
Here is how the choice usually plays out:
| Situation | Recommended version | Reason |
|---|---|---|
| Production monitoring, starting now | 7.0 LTS | Stable, long supported, well documented |
| Lab or proof of concept | 8.0 RC | Test new features and the new frontend |
| Existing 7.4 install | Plan move to 8.0 LTS at GA | 7.4 full support runs only until 8.0 LTS arrives |
The latest_7.0 release package tracks the 7.0 line, so apt upgrade moves you through point releases and will not jump to 8.0 by surprise. That behavior is exactly what you want from a monitoring system.
Architecture and Sizing Before You Type Anything
Zabbix has four moving parts. The server collects and processes data. The database stores it. The web frontend (PHP) displays it. Agents run on monitored machines. Proxies sit in remote networks and forward data when needed.
For a small setup, everything lives on one machine. That is perfectly fine for up to a few hundred hosts. Beyond that, split the database off first. The database is nearly always the bottleneck, and it is the part that hurts most when it gets slow.
Rough starting points that have held up across real environments:
- Up to 100 hosts: 2 vCPU, 4 GB RAM, 40 GB SSD.
- 100 to 500 hosts: 4 vCPU, 8 GB RAM, NVMe or fast SSD, 100 GB or more.
- 500+ hosts: dedicated database server, 16 GB RAM or more, and a serious look at partitioning.
Disk grows faster than people expect. History and trends data pile up, and the default retention is generous. Plan for it now rather than resizing a full volume during an outage.
Prerequisites and Preparation
A fresh Ubuntu 26.04 LTS server with sudo access is all you need. A static IP or a stable DNS name helps, because agents will point at it for years. Make sure the clock is right, since Zabbix graphs and triggers depend on correct time.
Update the system first:
sudo apt update
sudo apt full-upgrade -y
sudo reboot
If a new kernel was installed, the reboot is not optional. Running mismatched module versions is the sort of thing that bites later.
Check time sync and set a locale that will not complain:
timedatectl status
sudo timedatectl set-timezone Asia/Jakarta
sudo apt install -y wget curl ca-certificates gnupg
Swap in your own timezone. If timedatectl shows System clock synchronized: yes, you are good. If not, systemd-timesyncd or chrony will sort it out.
One cleanup worth doing: if the Zabbix packages from Ubuntu’s own repository were ever installed on this host, remove them and any leftover files before continuing. Zabbix’s own upgrade documentation warns against upgrading directly from distribution packages, because it can leave a broken installation.
dpkg -l | grep -i zabbix
On a clean server, this prints nothing. Good.
Step 1: Add the Official Zabbix Repository
Zabbix publishes one release package per Ubuntu version. Reading VERSION_ID from /etc/os-release picks the correct one, so the same lines work across releases:
. /etc/os-release
wget "https://repo.zabbix.com/zabbix/7.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_latest_7.0+ubuntu${VERSION_ID}_all.deb"
sudo dpkg -i "zabbix-release_latest_7.0+ubuntu${VERSION_ID}_all.deb"
sudo apt update
That package only drops the repository definition and signing key on the system. It does not install any monitoring service. Confirm that apt now sees the official candidate:
apt policy zabbix-server-mysql
The candidate version should come from repo.zabbix.com, not from Ubuntu’s universe. If it shows the Ubuntu one, the repo file did not land correctly, and everything after this point will go sideways.
If a 404 appears on the download, the exact filename on the mirror may have changed. Browse https://repo.zabbix.com/zabbix/7.0/ubuntu/pool/main/z/zabbix-release/ and pick the file ending in ubuntu26.04_all.deb.
Step 2: Install MariaDB
Zabbix supports MariaDB and MySQL (and PostgreSQL). On 26.04, MariaDB 11.8 LTS is a first-class package, and Ubuntu now ships an AppArmor profile and a hardened systemd service for it. One important detail: MySQL and MariaDB servers are mutually exclusive on Ubuntu for now. Pick one and stay with it.
sudo apt install -y mariadb-server
sudo systemctl enable --now mariadb
sudo mariadb-secure-installation
For the secure installation prompts, set or confirm the root auth method, remove anonymous users, disallow remote root login, and drop the test database. On modern MariaDB, root uses unix socket authentication by default, which is actually safer than a password. Keep it.
Create the Zabbix database and user
sudo mariadb
CREATE DATABASE zabbix CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
CREATE USER 'zabbix'@'localhost' IDENTIFIED BY 'UseALongRandomPasswordHere';
GRANT ALL PRIVILEGES ON zabbix.* TO 'zabbix'@'localhost';
SET GLOBAL log_bin_trust_function_creators = 1;
QUIT;
The utf8mb4 with utf8mb4_bin pairing is required. Zabbix compares strings case-sensitively in several places, and a case-insensitive collation causes subtle, nasty bugs. The log_bin_trust_function_creators flag is only needed during the schema import when binary logging is enabled. It gets turned back off later.
Generate a proper password instead of inventing one:
openssl rand -base64 24
Step 3: Install Zabbix Server, Frontend and Agent 2
One command pulls in the lot:
sudo apt install -y zabbix-server-mysql zabbix-frontend-php zabbix-apache-conf zabbix-sql-scripts zabbix-agent2
What each package does:
zabbix-server-mysql: the server daemon built for MySQL/MariaDB.zabbix-frontend-php: the web interface files.zabbix-apache-conf: a ready-made Apache config exposing the UI at/zabbix.zabbix-sql-scripts: the initial schema and data.zabbix-agent2: the modern Go-based agent, which also monitors the server itself.
Agent 2 is worth choosing over the legacy agent for new installs. It supports plugins, handles concurrent checks better, and is the one Zabbix is investing in.
Apache and the required PHP modules come in as dependencies. Check:
apache2 -v
php -v
Step 4: Import the Initial Schema
This is the step people rush, and it is where a surprising number of installs fail quietly. The import can take a minute or two on slow disks. Be patient.
zcat /usr/share/zabbix-sql-scripts/mysql/server.sql.gz | mysql --default-character-set=utf8mb4 -uzabbix -p zabbix
Enter the password you set earlier. No output means success. Verify:
mysql -uzabbix -p -e "SELECT COUNT(*) FROM zabbix.users;"
You should see a small positive number. Now switch the trust flag off again:
sudo mariadb -e "SET GLOBAL log_bin_trust_function_creators = 0;"
Why bother? With binary logging on, that flag allows non-privileged users to create stored functions with potentially unsafe behavior. It was a temporary convenience. Close the door behind you.
Step 5: Configure the Zabbix Server
Open the config:
sudo nano /etc/zabbix/zabbix_server.conf
Set the database password:
DBHost=localhost
DBName=zabbix
DBUser=zabbix
DBPassword=UseALongRandomPasswordHere
Or do it non-interactively, which is handy in automation:
sudo sed -i 's/^# DBPassword=.*/DBPassword=UseALongRandomPasswordHere/' /etc/zabbix/zabbix_server.conf
grep -E '^(DBHost|DBName|DBUser|DBPassword)' /etc/zabbix/zabbix_server.conf
The config file contains a credential, so permissions matter. The packages set sensible ownership, but check:
ls -l /etc/zabbix/zabbix_server.conf
It should be readable by root and the zabbix group, not world-readable. If it is, fix it with sudo chmod 640 /etc/zabbix/zabbix_server.conf.
Step 6: Start the Services
sudo systemctl restart zabbix-server zabbix-agent2 apache2
sudo systemctl enable zabbix-server zabbix-agent2 apache2
sudo systemctl status zabbix-server --no-pager
The status should read active (running). Do not trust that alone. Read the log:
sudo tail -n 40 /var/log/zabbix/zabbix_server.log
Healthy output ends with lines like “server #0 started” for the various process types. Any line mentioning “cannot connect to database” or “access denied” means go back to Step 5.
Step 7: Finish Setup in the Web Installer
Visit http://your-server-ip/zabbix in a browser. The wizard walks through these screens:
- Welcome and language.
- Prerequisite check. Every row should be green.
- Database connection: type MySQL, host
localhost, port0(default socket/port), databasezabbix, the user and password you made. - Settings: server name and timezone.
- Summary and install.
The default login is Admin with password zabbix. Change it immediately. Go to the user profile, set a long password, and ideally enable two-factor authentication under Users > Authentication. A default-credential Zabbix instance on a public IP is an invitation, and automated scanners look for exactly this.
Better still, create a personal named admin account and disable Admin or set it to a locked-down role. Named accounts make the audit log meaningful.
Securing the Frontend with HTTPS
Credentials crossing the network in cleartext is not acceptable, even on an internal network. Use TLS.
For a public DNS name, Let’s Encrypt is the practical answer:
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apache -d zabbix.example.com
Certbot edits the Apache vhost and sets up renewal through a systemd timer. Confirm the timer:
systemctl list-timers | grep certbot
sudo certbot renew --dry-run
For internal-only hosts without public DNS, an internal CA or a self-signed certificate works, but agents and browsers will need to trust it.
A cleaner URL helps as well. Many admins prefer serving the UI from the root of a dedicated hostname rather than /zabbix. You can do this by editing the Apache config at /etc/zabbix/apache.conf or by defining a vhost with DocumentRoot /usr/share/zabbix. Either is fine. Just be consistent, and remember that package upgrades may overwrite files in /etc/zabbix/ that you changed without care, so keep your custom vhost in /etc/apache2/sites-available/ instead.
Also add basic security headers:
sudo a2enmod headers
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Strict-Transport-Security "max-age=31536000"
Only enable HSTS after confirming HTTPS works everywhere. Rolling it back is annoying.
Firewall Configuration
Zabbix uses two ports that matter: 10051/TCP for the server (receives data from active agents and proxies) and 10050/TCP for passive agent checks. The web interface uses 80 and 443.
On the Zabbix server with UFW:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow from 10.0.0.0/24 to any port 10051 proto tcp
sudo ufw enable
sudo ufw status verbose
Replace 10.0.0.0/24 with the real network your agents live on. Do not open 10051 to the world. Anyone who can reach that port can attempt to send data to your server.
On monitored hosts, only the Zabbix server (or proxy) IP needs to reach 10050:
sudo ufw allow from 10.0.0.5 to any port 10050 proto tcp
Order of operations matters here. Always allow SSH before enabling UFW on a remote machine. Locking yourself out of a VPS is a rite of passage, but one worth skipping.
If the host runs AlmaLinux or another RHEL-family distro elsewhere in your fleet, the equivalent is:
sudo firewall-cmd --permanent --add-port=10050/tcp
sudo firewall-cmd --reload
And on those systems, SELinux will block things in ways that look like network faults. Check getsebool -a | grep zabbix and set httpd_can_connect_zabbix or zabbix_can_network as needed.
Installing the Agent on Other Hosts
Monitoring only the Zabbix server is a sad little dashboard. Add real machines.
On another Ubuntu or Debian host, install the same release package, then the agent:
. /etc/os-release
wget "https://repo.zabbix.com/zabbix/7.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_latest_7.0+ubuntu${VERSION_ID}_all.deb"
sudo dpkg -i "zabbix-release_latest_7.0+ubuntu${VERSION_ID}_all.deb"
sudo apt update
sudo apt install -y zabbix-agent2
Edit /etc/zabbix/zabbix_agent2.conf:
Server=10.0.0.5
ServerActive=10.0.0.5
Hostname=web01.example.com
Server lists who may poll the agent (passive checks). ServerActive is where the agent sends data on its own (active checks). Hostname must match the host name configured in the Zabbix UI exactly, character for character. A mismatch is the number one reason active checks silently never appear.
sudo systemctl enable --now zabbix-agent2
sudo systemctl status zabbix-agent2 --no-pager
For Debian, the repo path uses debian instead of ubuntu. For AlmaLinux, use the rhel path and install with dnf install zabbix-agent2. The agent config is identical.
Add the host in the frontend
Go to Data collection > Hosts > Create host. Enter the hostname, assign the template “Linux by Zabbix agent” (or the active variant), add a group, and set the agent interface to the host IP on port 10050. Within a minute or two, the availability icon should turn green.
If you manage dozens of hosts, do not click this through by hand. Use network discovery, auto-registration for active agents, or the API. Auto-registration pairs well with configuration management tools like Ansible, which can push the agent config at provisioning time.
Troubleshooting Common Errors
Here is where the time usually goes.
“Zabbix server is not running” in the frontend
The banner appears when the UI cannot reach the server process. Causes in order of likelihood: the service crashed on a bad config, the database password is wrong, or the frontend points at the wrong server host/port.
sudo systemctl status zabbix-server --no-pager
sudo journalctl -u zabbix-server -n 50 --no-pager
sudo tail -n 50 /var/log/zabbix/zabbix_server.log
Fix the cause, then restart. If the service is running but the banner persists, check SELinux on RHEL-family systems, or confirm the $ZBX_SERVER and $ZBX_SERVER_PORT values in /etc/zabbix/web/zabbix.conf.php.
“Cannot connect to the database” or “Access denied”
Re-test the credentials directly:
mysql -uzabbix -p -h localhost zabbix -e "SELECT 1;"
If this fails, the user or password is wrong, or the grant is missing. If it works, the password in zabbix_server.conf or zabbix.conf.php differs from what you typed. Special characters such as # in a password can also break parsing in the config file. Use a long alphanumeric password to avoid the whole class of problem.
Schema import fails with “You do not have the SUPER privilege”
That is the binary logging setting biting. Run SET GLOBAL log_bin_trust_function_creators = 1; as root, redo the import, then switch it back to 0. If you drop and retry, drop the partially filled database first, or you will hit “table already exists”.
Agent shows red or “Cannot connect” in the host list
Work through the checklist in order:
sudo systemctl status zabbix-agent2 --no-pager
sudo ss -tlnp | grep 10050
sudo tail -n 30 /var/log/zabbix/zabbix_agent2.log
From the Zabbix server, test directly:
sudo apt install -y zabbix-get
zabbix_get -s 10.0.0.20 -p 10050 -k agent.ping
A reply of 1 means the path works. “Connection refused” suggests the agent is down or the port is wrong. A timeout points at a firewall. “Not permitted” or “no active checks” often points to a mismatch in Server= or Hostname=.
Active checks never show data
Nine times out of ten, Hostname in the agent file does not match the host name in the UI. The log will say something like “no active checks on server” or “host not found”. Match them exactly, including case, and restart the agent.
PHP errors on the prerequisites page
If the wizard complains about max_execution_time, post_max_size, or the timezone, edit the PHP values in /etc/zabbix/apache.conf, then reload Apache:
sudo systemctl reload apache2
Be aware that Ubuntu 26.04 ships a very new PHP (8.5.2). If a page throws deprecation warnings or white screens after an update, read /var/log/apache2/error.log and confirm that the installed frontend package explicitly supports the PHP version in use. The official Zabbix packages for 26.04 are built to work with it, but mixing in third-party PHP repositories like Ondrej’s PPA is a common source of trouble. Avoid mixing unless there is a clear need.
The database grows out of control
Check table sizes:
SELECT table_name, ROUND((data_length+index_length)/1024/1024) AS size_mb
FROM information_schema.tables
WHERE table_schema='zabbix'
ORDER BY (data_length+index_length) DESC LIMIT 10;
Almost always, history, history_uint and trends lead the list. Lower item history retention, remove unused templates, or raise the update interval for noisy items. Checking every item every 10 seconds when once per minute would do is a classic self-inflicted problem.
Slow frontend
Dashboards with dozens of heavy widgets hit the database hard. Reduce widget refresh rates, enable PHP OPcache (usually on by default), and verify the buffer pool is sized correctly. A slow UI usually points at a slow database rather than at Apache.