Zabbix monitoring on Ubuntu 24.04
Install Zabbix 7.0 LTS on Ubuntu 24.04: add the repo, import the MySQL schema, wire up the nginx frontend and agent2, then fix every error the setup throws.
What you are building
A single Ubuntu 24.04 VPS running the full Zabbix 7.0 LTS stack: the zabbix-server daemon that polls and alerts, a MariaDB/MySQL database that stores every metric, a PHP frontend served by nginx, and zabbix-agent2 collecting metrics from the box itself. Once it is up you point it at a second server, attach a template, and get an email the moment a disk fills or a service dies.
Zabbix is heavier than a status-page tool: a real time-series system with templates, triggers, escalation and history retention. Almost all the pain lives in three steps people skip — importing the database schema, setting the database password the server actually reads, and uncommenting two lines in the nginx config. Get those right and the rest is a wizard. If you only want up/down checks and a shareable status page, a lightweight Uptime Kuma status monitor does that in one container; Zabbix earns its weight when you want per-metric thresholds and escalation across many hosts.
Use the 7.0 LTS line: its server, frontend, agent2 and schema are versioned together, and it gets security fixes for years. Before copying the repository filename below, glance at the official Zabbix download page — the latest release package always resolves to the newest 7.0 point release, but if you would rather pin an exact 7.0-N build the page lists that filename too. Every other command here is stable across the whole 7.0 line.
Sizing, ports and prerequisites
Assume a fresh Ubuntu 24.04 KVM VPS with root or sudo. Be honest about resources: 2 GB of RAM and two vCPUs are the realistic floor for server, database, nginx and PHP on one box monitoring a handful of hosts. A 1 GB box boots but starves the database as history grows; plan on 4 GB past a few dozen hosts, and beyond a hundred move the database onto its own server. Disk grows with how many items you collect and how long you keep history; budget a few gigabytes to start, and the housekeeper prunes old data on the retention windows you set.
Ports: 80 and 443 for the frontend, public or restricted to your own IP. The server listens on TCP 10051 for active agents pushing data in, and each agent listens on TCP 10050 for the server polling it. Open only what you expose if a firewall sits in front. A DNS A record for the frontend is worth having, because you will want a real certificate on it — a Zabbix box watches every other service you run, so it belongs in the wider self-hosting stack worth running in 2026 rather than bolted on as an afterthought.
Step 1 — Add the official Zabbix 7.0 repository
The zabbix-release package does one thing: it drops the apt source list and signing key onto your system. Install it, then refresh the index.
wget https://repo.zabbix.com/zabbix/7.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_latest_7.0+ubuntu24.04_all.deb
sudo dpkg -i zabbix-release_latest_7.0+ubuntu24.04_all.deb
sudo apt update
If wget returns 404 Not Found, the filename has moved — open the Zabbix download page, pick Ubuntu 24.04, and copy the exact zabbix-release URL it prints. A clean apt update shows a repo.zabbix.com line and no key warning; The following signatures couldn't be verified because the public key is not available means the release package never installed its key, so re-run the dpkg -i and update again.
Step 2 — Install the server, frontend, and agent
Install the server built against MySQL, the PHP frontend, the nginx config, the SQL schema files, and agent2 in one go.
sudo apt install -y zabbix-server-mysql zabbix-frontend-php \
zabbix-nginx-conf zabbix-sql-scripts zabbix-agent2
zabbix-server-mysql is the server compiled for MySQL/MariaDB; for PostgreSQL install zabbix-server-pgsql instead and adjust the database steps below. zabbix-sql-scripts carries the schema you import next — it is a separate package in 7.0, and forgetting it is exactly why the import path turns up missing later. None of these pull in a database engine, so install MariaDB now if the box does not already have one.
sudo apt install -y mariadb-server
sudo systemctl enable --now mariadb
Step 3 — Create the database with the right character set
Zabbix is strict about collation: the database must be utf8mb4 with utf8mb4_bin, or the import dies partway through with foreign-key errors. Open a root shell with sudo mysql and run:
CREATE DATABASE zabbix CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
CREATE USER zabbix@localhost IDENTIFIED BY 'choose-a-strong-password';
GRANT ALL PRIVILEGES ON zabbix.* TO zabbix@localhost;
SET GLOBAL log_bin_trust_function_creators = 1;
FLUSH PRIVILEGES;
That SET GLOBAL line is a safety valve for the import. The Zabbix schema creates stored functions, and a non-SUPER account like zabbix cannot create them while binary logging is on — MySQL 8 enables binary logging by default, and some managed MariaDB builds do too (stock MariaDB does not). Skip the flag on a server with binary logging enabled and the import stops with ERROR 1419 (HY000): You do not have the SUPER privilege and binary logging is enabled. Setting it as root clears that; you turn it back off after the import. Note the password exactly — the server config needs the same string byte for byte.
Step 4 — Import the schema (the step people skip)
This is the single most common reason a fresh frontend refuses to load: the database you created is empty. Load the schema shipped in zabbix-sql-scripts into it.
zcat /usr/share/zabbix-sql-scripts/mysql/server.sql.gz | \
mysql --default-character-set=utf8mb4 -uzabbix -p zabbix
It prompts for the zabbix password, runs for thirty to sixty seconds on a small VPS, and prints nothing on success — silence is correct here. zcat: /usr/share/zabbix-sql-scripts/mysql/server.sql.gz: No such file or directory means the zabbix-sql-scripts package is not installed (back to Step 2). ERROR 1045 (28000): Access denied for user 'zabbix'@'localhost' means the password or grant from Step 3 is wrong. Once it finishes, turn the safety flag back off:
sudo mysql -e "SET GLOBAL log_bin_trust_function_creators = 0;"
Confirm the load with sudo mysql -e "SELECT COUNT(*) FROM zabbix.users;" — a number, not a doesn't exist error, means the schema is in.
Step 5 — Set the database password in zabbix_server.conf
The server reads its database credentials from /etc/zabbix/zabbix_server.conf. Only the password needs setting — DBName and DBUser already default to zabbix. Uncomment the # DBPassword= line and set it:
DBName=zabbix
DBUser=zabbix
DBPassword=choose-a-strong-password
The password must be byte-for-byte the one from Step 3. A blank or wrong DBPassword is the top cause of the "Zabbix server is not running" banner you meet later: the daemon starts, fails to log in, and exits. Nothing on screen says so — only the log does.
Step 6 — Point nginx at the frontend
The zabbix-nginx-conf package installs a server block at /etc/zabbix/nginx.conf and links it into nginx from /etc/nginx/conf.d/zabbix.conf, but it ships with the listen and server_name lines commented out, so nginx serves nothing for Zabbix until you edit them:
server {
listen 80;
server_name zabbix.example.com;
...
}
Use your domain, or the server's public IP if you have no DNS yet. Two gotchas. Ubuntu's default nginx welcome site also listens on port 80 and is marked the default server, so it wins for any request whose name it does not match — remove it or the wrong page loads: sudo rm -f /etc/nginx/sites-enabled/default. And if /etc/nginx/conf.d/zabbix.conf is somehow missing (a known packaging quirk), create the link by hand with sudo ln -s /etc/zabbix/nginx.conf /etc/nginx/conf.d/zabbix.conf. Then test and reload:
sudo nginx -t
sudo systemctl reload nginx
nginx -t should print test is successful. If it instead prints nginx: [emerg] a duplicate default server for 0.0.0.0:80, both the default site and the Zabbix block claim default_server — drop the default site and re-test. More often the test passes with only a conflicting server name warning yet the browser still shows the plain welcome page: that is the default site winning on an unmatched name, and removing it fixes it.
Step 7 — Start and enable the services
Bring up the server, the agent, PHP-FPM and nginx, and enable them so they survive a reboot.
sudo systemctl restart zabbix-server zabbix-agent2 nginx php8.3-fpm
sudo systemctl enable zabbix-server zabbix-agent2 nginx php8.3-fpm
Ubuntu 24.04 ships PHP 8.3, so the FPM service is php8.3-fpm; the zabbix-nginx-conf package runs its frontend under that same master. Confirm the server started and stayed up:
sudo systemctl status zabbix-server --no-pager
sudo tail -n 20 /var/log/zabbix/zabbix_server.log
A healthy log ends with the server started and its housekeeper, poller and trapper processes running. If you see [Z3001] connection to database 'zabbix' failed: [1045] Access denied for user 'zabbix'@'localhost', the DBPassword from Step 5 does not match — fix it and restart. A [Z3005] query failed: [1146] Table 'zabbix.dbversion' doesn't exist line means you skipped the schema import in Step 4, so the server has nothing to read.
Step 8 — Finish the web setup wizard
Browse to http://your-domain-or-ip. The Zabbix setup wizard opens. Walk through it:
- Welcome — pick a language.
- Check of pre-requisites — every row should read OK out of the box; the packaged
/etc/zabbix/php-fpm.confpool already raises the PHP limits Zabbix needs. - Configure DB connection — database type MySQL, host
localhost, port0(which means the default port or socket), database namezabbix, userzabbix, and the password from Step 3. Wrong credentials or a missing schema surface right here. - Settings — leave the Zabbix server host
localhost, port10051, and name the install. - Pre-installation summary, then Install — the wizard writes
/etc/zabbix/web/zabbix.conf.php. - Finish — log in as
Admin(capital A) with passwordzabbix, and change that password under Users immediately.
If the wizard reports Cannot create the configuration file, the web user cannot write /etc/zabbix/web/ — download the zabbix.conf.php it offers, place it there by hand with sudo, and click Finish.
Step 9 — Add the local host and attach a template
Zabbix does not monitor itself automatically. Go to Data collection → Hosts → Create host, set the host name to zabbix-server, add it to the Linux servers group, and add an Agent interface with IP 127.0.0.1, port 10050. Under Templates, link Linux by Zabbix agent (the passive-poll variant; Linux by Zabbix agent active has the agent push instead). Save.
Within a minute the host row shows a green ZBX label and metrics arrive under Monitoring → Latest data. A red ZBX label means the server cannot reach the agent — a failure mode covered below, not a mistake in this step.
Step 10 — One useful trigger and an email alert
The Linux by Zabbix agent template already ships triggers for high CPU, low memory and full disks, so you have alerts the moment you link it. To see how one is built, add your own: open the host, go to Triggers → Create trigger, name it Root filesystem over 90% full on {HOST.NAME}, set severity High, and use this expression:
last(/zabbix-server/vfs.fs.dependent.size[/,pused])>90
vfs.fs.dependent.size[/,pused] is the percent-used item the template's filesystem discovery creates for the root filesystem (7.0's Linux template collects vfs.fs.get once and derives per-mount dependent items), so the trigger fires with no extra data collection. Save it.
A trigger only recolours a row on the dashboard until an action notifies someone. Under Alerts → Media types → Email, set the SMTP server, port, sender address, connection security (usually STARTTLS) and any relay credentials, then use Test — a failure there means the SMTP path is wrong before any real alert depends on it. Give the Admin user an address under Users → Users → Admin → Media → Add and pick which severities notify. Then wire it under Alerts → Actions → Trigger actions → Create action: add a condition like Trigger severity >= Warning, and under Operations send to Admin via Email. Save and enable it. Prefer chat? Zabbix 7.0 ships webhook media types for Slack, Discord and Telegram — configure one exactly like Email and assign it to a user.
Monitoring a second server with the agent
To watch another server, add the Zabbix repository there too — Ubuntu 24.04 ships no Zabbix packages of its own — then install just the agent:
wget https://repo.zabbix.com/zabbix/7.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_latest_7.0+ubuntu24.04_all.deb
sudo dpkg -i zabbix-release_latest_7.0+ubuntu24.04_all.deb
sudo apt update
sudo apt install -y zabbix-agent2
Edit /etc/zabbix/zabbix_agent2.conf, set Server to the Zabbix server's IP (for passive polling) and, for active checks, ServerActive plus a unique Hostname that matches the host you create in the frontend:
Server=203.0.113.10
ServerActive=203.0.113.10
Hostname=web-01
Run sudo systemctl enable --now zabbix-agent2 and open TCP 10050 from the Zabbix server to this box. Back in the frontend, create a host whose Agent interface points at that machine's IP, link the Linux template, and it reports within a minute.
Failure modes, with the strings you will see
Frontend shows Error connecting to database or a database error. In the wizard's DB step, Access denied for user 'zabbix'@'localhost' (using password: YES) means the password does not match Step 3 or the GRANT never ran. If the database connects but the frontend reports The frontend does not match Zabbix database or Table 'zabbix.users' doesn't exist, the schema import in Step 4 was skipped or failed — re-run it into a freshly created database.
Orange banner: Zabbix server is not running: the information displayed may not be current. The frontend is up but cannot reach a live server. Three causes, most likely first. The service is dead — check systemctl status zabbix-server and start it. The service runs but cannot reach its database — the log shows [Z3001] connection to database 'zabbix' failed: [1045] Access denied, meaning a wrong or blank DBPassword. Or the frontend points at the wrong server, stored as host localhost port 10051 in /etc/zabbix/web/zabbix.conf.php. On Ubuntu there is no SELinux by default, so the SELinux-socket cause that RHEL guides cite does not apply; here it is almost always the database password or a stopped service.
Host shows a red ZBX label; item error Get value from agent failed: cannot connect to [[127.0.0.1]:10050]: [111] Connection refused. The agent is not running or not listening. Check systemctl status zabbix-agent2 and confirm ListenPort=10050. A timeout instead of "connection refused" means a firewall is dropping port 10050. The related Received empty response from Zabbix Agent. Assuming that agent dropped connection because of access permissions. means the agent is reachable but its Server= line does not list the Zabbix server's IP — add it and restart the agent.
Plain nginx welcome page, or 502 Bad Gateway. The welcome page means the default site is still enabled or the Zabbix block is not linked — remove /etc/nginx/sites-enabled/default and confirm /etc/nginx/conf.d/zabbix.conf exists, as in Step 6. A 502 means nginx reached PHP but PHP-FPM is down or listening on a different socket — start php8.3-fpm and check that the fastcgi_pass socket in the Zabbix block matches the running pool.
Backups, upgrades, and TLS
The database is the whole history, so back it up with mysqldump on a schedule and keep the dump off the box:
mysqldump --single-transaction zabbix | gzip > zabbix-$(date +%F).sql.gz
The /etc/zabbix config files are small and worth saving too, but they are rebuildable; the data is not. Restoring is the reverse — create the database, import the dump, point the config at it.
Upgrades within the 7.0 line are ordinary apt update && apt upgrade runs; the server applies any schema migrations on its next start, so upgrade the packages and restart zabbix-server. A future major line is a deliberate move — read its upgrade notes, dump the database first, and expect a one-way schema change. That one-way jump is exactly why you pin to an LTS line and stay on it.
Do not leave the frontend on plain HTTP. Once DNS points at the box, put a certificate on it with Certbot and Let's Encrypt on nginx for Ubuntu 24.04, which rewrites the server block to listen on 443 and redirect 80. While you are hardening a box that logs in to watch every other server you run, lock down its SSH too with fail2ban banning brute-force SSH attempts. A monitoring login should not travel in cleartext, and its front door should not sit open to password-guessers.
FAQ
Do I need the Zabbix agent, or can I monitor without it?
Both work. The agent (agent2) gives rich per-host metrics — CPU, memory, disks, processes, services, log files — and is the normal choice for servers you control. Agentless monitoring covers devices where you cannot install software: SNMP for switches and printers, ICMP ping for reachability, HTTP checks for endpoints, IPMI for hardware health. Most real deployments mix the two.
Why does the frontend say the Zabbix server is not running?
The frontend works but cannot reach a live zabbix-server process. Usually the server started, failed to log in because DBPassword in zabbix_server.conf is wrong or blank, and dropped straight back out. Run systemctl status zabbix-server and read /var/log/zabbix/zabbix_server.log; a [Z3001] connection to database ... failed line confirms it. Fix the password and restart, and the banner clears within a minute.
Do I have to use MySQL, or can I use PostgreSQL?
Either is fully supported: zabbix-server-mysql with MariaDB or MySQL as this guide does, or zabbix-server-pgsql with PostgreSQL. For small installs the choice barely matters. For large ones, PostgreSQL with the TimescaleDB extension partitions the history tables and makes housekeeping far cheaper, which is why big sites lean toward it. Pick one and stay with it — migrating later means a full export and re-import, not a config switch.
How much RAM does a Zabbix all-in-one server need?
For one box monitoring a handful of hosts, 2 GB is a realistic floor and 4 GB is comfortable past a few dozen hosts; the memory goes mostly to the database cache and the server's pollers. A 1 GB VPS runs but starves the database as history grows. Beyond a hundred hosts, split the database onto its own server and tune its buffer pool rather than piling more RAM into one machine.
Is Zabbix overkill for a couple of servers?
It can be. If all you need is "is it up?" and a page you can share, a lighter tool like Uptime Kuma for uptime and status monitoring is faster to stand up and easier to read at a glance. Choose Zabbix when you want per-metric thresholds, historical graphs, templated hosts and escalation rules — the things a simple pinger cannot give you across a fleet.