Skip to content

How to Deploy Zabbix on a VPS

Updated Sep 2026

verified on Ubuntu 26.04 · Aug 2026
We earn commissions when you shop through the links below. Full disclosure →

Self-host Zabbix on a VPS — the full package install, database schema import, nginx frontend and agent, with the two steps that quietly break on a fresh Ubuntu box.

Before you start
  • A VPS with real memory — 4 GB is a sane starting point, most of it for the database
  • A fresh Ubuntu 24.04 or 26.04 server with root/sudo SSH access
  • A domain you can point at the server, on a box separate from what you monitor
  • Comfort with apt, systemd and editing config files — this is not a container install
Need a box for this guide? Kamatera's free tier lets you spin one up now.Start free on Kamatera → (opens in new tab)

What Zabbix is

Zabbix is enterprise infrastructure monitoring: agents on every host, plus SNMP, IPMI, JMX and agentless checks, wrapped in templates, network auto-discovery, escalation-aware alerting and long-term trend storage. It is AGPL-3.0, free to self-host with no host or user cap, and it is the open-source answer to a Datadog-shaped bill.

It is also, by a distance, the heaviest deploy on this site. Zabbix is not a container you start and forget — a working install is four moving parts:

  • the Zabbix server, a C daemon that does the collecting, the trend calculation and the alerting
  • a database — MySQL, MariaDB, Percona or PostgreSQL — which is where almost all the load and almost all the disk goes
  • a PHP frontend served by nginx, which is the web UI you actually look at
  • an agent on every host you want to watch, including this one

Upstream ships packages from its own apt repository rather than a one-line container, and the package name encodes your database choice (zabbix-server-mysql versus zabbix-server-pgsql), so the decision is made at install time and is awkward to reverse. This guide takes the MySQL path, which is what the packages in our install snippet assume.

Be honest about whether you need this. Zabbix wants a database server, a web server, a PHP-FPM pool and an agent everywhere before it draws a single graph, and its payoff — templates, auto-discovery, escalation chains, proxies for remote sites — only shows up when you are watching an estate rather than a handful of boxes. For three servers, Netdata or Uptime Kuma gets you a live view in one command, and Zabbix vs Checkmate walks through that fork properly. If you are here because you have racks, VMs, switches and an on-call rota, read on.

Server sizing

Zabbix sizing is really database sizing. The server daemon itself is modest; what grows is the volume of collected values, the history retention window and the trend tables behind it — and that lands squarely on MySQL.

Upstream's smallest published sizing example is 2 CPU cores and 8 GiB of memory for roughly a thousand monitored metrics. Read that as an example, not a floor. It is one point on a curve, published to show the shape of the scaling, and a lab install watching a few hosts runs comfortably in far less. Size against your own metric count and retention, not against that number.

  • 2 GB RAM / 2 vCPU — a lab or a handful of hosts, short retention. It works; it is not where you want to be six months in.
  • 4 GB RAM / 2–4 vCPU — the sane default for a small real fleet, what the catalogue lists, and the size this guide assumes.
  • 8 GB+ and more cores — hundreds of hosts, thousands of items, or long retention with several concurrent frontend users.

Disk deserves its own thought: history and trends accumulate continuously, and the thing that grows is the MySQL data directory. 40–80 GB of fast NVMe is a comfortable start for a small fleet — watch it for a month before deciding whether to trim retention or buy disk. This is the workload where a host that sells generous RAM and disk per dollar earns its keep, so Hetzner and Contabo both make the shortlist.

And the placement rule that applies to every monitor: do not run Zabbix on the machine it monitors. Give it its own box, ideally on a different provider or region from the fleet it watches.

Prepare the server

Start from a fresh Ubuntu 24.04 or 26.04 install, update it, and make yourself a non-root user:

sudo apt update && sudo apt upgrade -y
sudo adduser deploy && sudo usermod -aG sudo deploy

Lock the firewall down to SSH plus the reverse proxy ports. Zabbix's own frontend port stays internal, and so does MySQL:

sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw enable
sudo ufw status verbose

One port you will need to open later, once you start adding hosts: 10051/tcp, which is where the Zabbix server receives data from agents in active mode. Leave it closed for now and open it to specific source addresses when you know which hosts are reporting in. Agents in passive mode listen on 10050 on their side, which means the agent boxes need the inbound rule, not this one.

Where to host itaffiliate disclosure
Hetzner Cloudrun it on
2 vCPU · 4 GB RAM · 80 GB SSD · $23.59/mo
Get Hetzner Cloud (opens in new tab)
Contaboalso works on
4 vCPU · 8 GB RAM · 100 GB SSD · $4.95/mo
Get Contabo (opens in new tab)
Kamaterafree trial
1 vCPU · 1 GB RAM · 20 GB SSD · $4.00/mo
Start free on Kamatera → (opens in new tab)

Paid link — we earn a commission if you shop through it.

Install the database server

This is the first thing that catches people out, and it is the reason the install snippet on the app page starts where it does. Every Zabbix walkthrough you will find runs sudo mysql to create the database — but a fresh Ubuntu VPS has no MySQL server on it. sudo mysql on a bare box does not fail interestingly; it fails with a socket error that reads like a permissions problem, and people lose twenty minutes to it.

Install the database server first, before anything Zabbix-related, and confirm it is actually up:

sudo apt install -y mysql-server
sudo mysql -e "select version();"

If that prints a version, the socket works and sudo mysql will behave for the rest of this guide. If it does not, stop and fix it here — every later step depends on it.

Add the Zabbix repository

Upstream publishes a small zabbix-release .deb whose only job is to drop their apt repository and signing key onto your system. The download URL encodes your distribution release, and this is the second thing that breaks.

Most guides — and, until recently, our own snippet — hardcode a path like ubuntu24.04. Install that on a 26.04 box and apt happily accepts the release package and then fails to resolve a single Zabbix package, because the repository it points at is built for a distribution you are not running. The error arrives one step later than the mistake, which is what makes it annoying.

The fix is to stop hardcoding it. /etc/os-release already knows which release you are on, so source it and interpolate ${VERSION_ID}:

. /etc/os-release && wget https://repo.zabbix.com/zabbix/7.4/release/ubuntu/pool/main/z/zabbix-release/zabbix-release_latest+ubuntu${VERSION_ID}_all.deb
sudo dpkg -i zabbix-release_latest+ubuntu${VERSION_ID}_all.deb && sudo apt update

That is correct on any Ubuntu release upstream builds for, today and after your next dist-upgrade. The 7.4 segment in the path is the Zabbix release line, not your distribution — upstream's download page lists the lines they currently publish, and swapping that segment is how you move between them later.

If apt update prints a 404 for the Zabbix repository, upstream has not built packages for your Ubuntu release yet. That is a real answer, not a misconfiguration: use the previous LTS, or take the container route instead.

Install the Zabbix packages

Four packages, matching the four moving parts, plus the SQL scripts that create the schema:

sudo apt install -y zabbix-server-mysql zabbix-frontend-php zabbix-nginx-conf zabbix-sql-scripts zabbix-agent
  • zabbix-server-mysql — the server daemon, built against MySQL
  • zabbix-frontend-php — the PHP web UI
  • zabbix-nginx-conf — a ready-made nginx server block for that frontend
  • zabbix-sql-scripts — the schema, shipped gzipped, imported by hand below
  • zabbix-agent — the agent, so this box monitors itself from day one

Nothing works yet: the server has no database to talk to and no password to talk to it with. That is the next two sections.

Create the database and import the schema

Create the database, a dedicated user, and grant it access. Change CHANGEME — pick a long random password and keep it somewhere, because you need it twice in a moment:

sudo mysql -e "create database zabbix character set utf8mb4 collate utf8mb4_bin; create user zabbix@localhost identified by 'CHANGEME'; grant all privileges on zabbix.* to zabbix@localhost;"

The character set and collation are not decoration — utf8mb4 with the _bin collation is what Zabbix expects, and a database created with your server's defaults will produce confusing failures later.

Now import the schema. It is a gzipped SQL script that creates a couple of hundred tables, so this is the slow step — expect it to run for a minute or more on a small box, with no progress output. It will prompt for the password you just set:

# Ubuntu's MySQL has binary logging on by default, and the Zabbix schema creates
# stored functions — without this the import stops partway with ERROR 1419, leaving
# the tables created but empty. Zabbix then refuses to start, complaining that the
# "users" table is empty, which does not point at the real cause.
sudo mysql -e "set global log_bin_trust_function_creators = 1;"

zcat /usr/share/zabbix/sql-scripts/mysql/server.sql.gz | mysql --default-character-set=utf8mb4 -uzabbix -p zabbix

sudo mysql -e "set global log_bin_trust_function_creators = 0;"

Verify it landed before moving on:

# Count tables AND rows. A failed import still leaves 207 tables behind, so the
# table count alone will happily tell you everything is fine when nothing loaded.
sudo mysql -e "select count(*) as tables from information_schema.tables where table_schema = 'zabbix';"
sudo mysql -e "select count(*) as users from zabbix.users;"

A count in the low hundreds means the schema is in. Zero, or a single-digit number, means the import died partway — read the error, drop the database, and run it again rather than importing on top of a half-built schema.

If the import fails with ERROR 1418 (something about deterministic functions and binary logging), your MySQL has binary logging on and will not create the stored functions Zabbix ships. Allow it for the duration of the import and turn it back off afterwards:

sudo mysql -e "set global log_bin_trust_function_creators = 1;"
# re-run the zcat | mysql import here
sudo mysql -e "set global log_bin_trust_function_creators = 0;"

Configure the server and the frontend

Two files need editing by hand. Neither has a sensible default, and skipping either leaves you with services that start cleanly and do nothing.

First, the server's database password. Open /etc/zabbix/zabbix_server.conf, find the commented DBPassword line, and set it to the password you gave the zabbix user:

sudo nano /etc/zabbix/zabbix_server.conf
DBName=zabbix
DBUser=zabbix
DBPassword=CHANGEME

That file now holds a credential, so tighten it:

sudo chown root:zabbix /etc/zabbix/zabbix_server.conf
sudo chmod 640 /etc/zabbix/zabbix_server.conf

Second, the nginx server block. The zabbix-nginx-conf package installs /etc/zabbix/nginx.conf with its listen and server_name directives commented out — deliberately, so installing the package does not hijack port 80 on a box that is already serving something. Uncomment both:

sudo nano /etc/zabbix/nginx.conf
    listen 8080;
    server_name zabbix.example.com;

Port 8080, not 80. The reverse proxy in the next section owns 80 and 443 and forwards to this. Leaving Zabbix on 8080 behind the proxy is what keeps the PHP frontend off the public internet.

Start everything

Three services, plus a PHP-FPM restart so it picks up the frontend's configuration:

sudo systemctl enable --now zabbix-server zabbix-agent nginx
# nginx is already running from the package install, and `enable --now` does not
# restart a running service — so reload it explicitly or the Zabbix vhost you just
# edited never takes effect and port 8080 stays closed
sudo systemctl reload nginx
sudo systemctl restart "php*-fpm"

The quoted glob is deliberate — the PHP-FPM unit name carries the PHP version your distribution ships, and it changes under you across releases.

Check all three came up:

systemctl is-active zabbix-server zabbix-agent nginx
sudo tail -n 40 /var/log/zabbix/zabbix_server.log

The server log is the honest one. A clean start says it connected to the database and began housekeeping; a wrong DBPassword says so plainly and the daemon exits rather than limping.

HTTPS + domain

Point an A record for zabbix.example.com at the server's public IP and wait for it to resolve, then terminate TLS in front of the frontend. The simplest path is Automatic HTTPS with Caddy:

zabbix.example.com {
    reverse_proxy 127.0.0.1:8080
}

Caddy takes 80 and 443, fetches and renews the certificate on its own, and forwards to the nginx block you just uncommented. Nothing about Zabbix's UI needs special proxy handling — no WebSocket upgrade, no long-poll timeouts.

The frontend carries session cookies and the full map of your infrastructure, so there is no argument for plain HTTP and none for exposing 8080 directly. If you need to reach it before DNS is ready, use an SSH tunnel:

ssh -L 8080:127.0.0.1:8080 deploy@SERVER_IP

First login and hardening

Load https://zabbix.example.com. On a fresh install you get the frontend setup wizard: it checks PHP prerequisites, asks for the database connection (host localhost, database zabbix, user zabbix, and that password again), then the server name and your timezone. It writes its answers to a config file in the frontend directory and hands you the login screen.

The default credentials are Admin / zabbix, and they are documented upstream, which means every scanner on the internet knows them.

  1. Change the Admin password immediately, before anything else. User settings → Profile → Change password.
  2. Create a named Super admin account for yourself and use that day to day, leaving Admin as a break-glass account with a long password in your vault.
  3. Turn on two-factor auth under Users → Authentication → MFA, or wire the frontend to your identity provider there if you run one.
  4. Check the guest access setting. Zabbix ships a guest user; confirm it is disabled unless you want anonymous read access to your infrastructure map.
  5. Leave 8080 closed at the firewall. The Caddy block is the only door.

Add the hosts you actually want to watch

The install monitors one host: itself, via the local agent. Everything past that is the work Zabbix exists for.

On each machine you want to watch, install the agent from the same repository and point it at the server:

sudo apt install -y zabbix-agent

Then point the agent at your Zabbix server. Open its config in your editor:

sudo nano /etc/zabbix/zabbix_agentd.conf
Server=MONITOR_IP
ServerActive=MONITOR_IP
Hostname=web-01
sudo systemctl enable --now zabbix-agent
sudo ufw allow from MONITOR_IP to any port 10050 proto tcp

Two directions, and the distinction matters for firewalls. Passive checks have the server connecting out to the agent on 10050, so the agent's box needs the inbound rule (the ufw line above). Active checks have the agent connecting in to the server on 10051, so the monitoring box needs that rule instead, ideally restricted to your fleet's source addresses. Active mode scales better and is friendlier to hosts behind NAT; passive is simpler to reason about. Most fleets end up with both.

Then, in the frontend: Data collection → Hosts → Create host. Give it the same Hostname you set in the agent config, put it in a host group, and attach a template — "Linux by Zabbix agent" is the one for a plain server. Templates are the whole point: they carry the items, triggers, graphs and discovery rules, so a host goes from empty to fully instrumented by attaching one.

Finally, wire up alerting under Alerts → Media types. Zabbix ships integrations for email, webhooks and most chat platforms. Configure a media type, attach it to your user, and build an action that fires on trigger severity. Test it deliberately — stop a service and watch the alert arrive on a real device. Until you have seen that, you have a dashboard, not monitoring.

Backups

Everything that matters is in MySQL: hosts, templates, users, history, trends, and every configuration change you made in the UI. The frontend's generated config file and /etc/zabbix are worth capturing too, but they are small and easy to recreate.

sudo mysqldump --single-transaction --routines --triggers \
  -uzabbix -p zabbix | gzip > ~/zabbix-$(date +%F).sql.gz
sudo tar czf ~/zabbix-etc-$(date +%F).tar.gz /etc/zabbix

--single-transaction gives a consistent dump without locking the server out, and --routines --triggers matters here because Zabbix's schema uses both.

Two things people get wrong. History is the bulk of the dump and the least valuable part of it — if the backup is unmanageably large, dump the configuration tables and accept that a restore loses graph history; losing a month of trends is survivable, losing your host and template configuration is not. And copy it off the box: this server exists to tell you when other machines die, so it needs its own recovery story elsewhere. Test a restore once against a throwaway server, because an untested dump is a guess.

Upgrades

Point releases within a line are ordinary apt upgrades:

sudo apt update && sudo apt upgrade
sudo systemctl restart zabbix-server

The server applies any database schema changes itself on the next start, and writes what it did to /var/log/zabbix/zabbix_server.log. Watch that log after every upgrade — on a large history table the migration can take a long time, and the daemon is not serving while it runs.

Moving between major release lines is a bigger operation: install a new zabbix-release package pointing at the new line, apt update, upgrade the packages, then let the server migrate the schema on start. Back up first, and mean it — schema migrations are one-way, and rolling back means restoring the dump. Read upstream's upgrade notes for the specific jump, and check your agents are within the supported version window before you move the server.

Troubleshooting

apt install cannot find any zabbix-* packages. The release .deb you installed points at a repository for a different Ubuntu version — the ${VERSION_ID} problem from earlier. Check what you actually got with grep -r zabbix /etc/apt/sources.list.d/, remove the release package, and reinstall it with the sourced-os-release command above.

sudo mysql cannot connect to the local socket. No database server is installed, or it failed to start. sudo apt install -y mysql-server, then sudo systemctl status mysql.

The schema import errors with ERROR 1418. Binary logging is refusing the stored functions. Set log_bin_trust_function_creators on for the import as shown above, then off again.

zabbix-server starts and immediately stops. Almost always the database. sudo tail -n 50 /var/log/zabbix/zabbix_server.log names the reason directly — a wrong DBPassword, a missing database, or a schema that never imported.

The setup wizard says the frontend cannot connect to the database. The wizard uses its own credentials, entered in the browser, separate from zabbix_server.conf. Retype the password rather than assuming; a pasted trailing space is the classic version of this.

Connection refused, or nginx serves a default page. The listen and server_name lines in /etc/zabbix/nginx.conf are still commented, or a stale /etc/nginx/sites-enabled/default is claiming the port. sudo nginx -t then sudo systemctl reload nginx.

The frontend loads but shows a PHP error or a blank page. PHP-FPM did not restart after the frontend was installed. Run sudo systemctl restart "php*-fpm" and read /var/log/nginx/error.log.

The dashboard says "Zabbix server is not running" even though it is. The frontend reaches the server over the network, not the database. Confirm it is listening (ss -lntp | grep 10051) and that the address and port in the frontend's config point at it.

A host stays grey / "no data". Check the direction. For passive checks the server must reach the agent on 10050 — test with zabbix_get -s AGENT_IP -k agent.ping from the monitoring box. For active checks the agent must reach the server on 10051, and its Hostname must exactly match the host name in the frontend. Mismatched hostnames are the most common cause by a wide margin.

Verification + next steps

You are done when you can: load https://zabbix.example.com over a valid certificate, log in with a password that is not zabbix, see this server's own agent reporting green data, add a second host with a template attached and watch its metrics arrive, then deliberately break something and receive the alert on a real device.

From there the work is templates and escalation, not installation: group your hosts, lean on the shipped templates before writing your own, set trigger severities that mean something, and build escalation chains so a 3am alert reaches a person rather than an inbox. If you want prettier dashboards on top, Zabbix is a perfectly good data source for Grafana — Deploy Grafana on a VPS covers that side. For the ranked host picks see Best VPS for Monitoring & Uptime, and the monitoring catalogue has the lighter options if this install has convinced you that you wanted one of those.

Next steps

How to self-host Zabbix →More self-hosted monitoring tools →Best VPS for Monitoring & Uptime →Automatic HTTPS with Caddy →Run Claude Code with Ollama on Your Own VPS →Deploy Coolify on a VPS →How to Deploy Actual Budget on a VPS →How to Deploy AnythingLLM on a VPS →How to Deploy Appwrite on a VPS →How to Deploy Audiobookshelf on a VPS →How to Deploy Authelia on a VPS →How to Deploy authentik on a VPS →How to Deploy Baserow on a VPS →How to Deploy Beszel on a VPS →How to Deploy Bitwarden on a VPS →How to Deploy BookStack on a VPS →How to Deploy CapRover on a VPS →How to Deploy Checkmate on a VPS →How to Deploy Directus on a VPS →How to Deploy docker-mailserver on a VPS →How to Deploy Docmost on a VPS →How to Deploy Dokku on a VPS →How to Deploy Dokploy on a VPS →How to Deploy Firefly III on a VPS →How to Deploy Forgejo on a VPS →How to Deploy Gatus on a VPS →How to Deploy Ghostfolio on a VPS →How to Deploy Gitea on a VPS →How to Deploy GitLab on a VPS →How to Deploy GlitchTip on a VPS →How to Deploy Grafana on a VPS →How to Deploy Graylog on a VPS →How to Deploy Headscale on a VPS →How to Deploy Healthchecks on a VPS →How to Deploy Home Assistant on a VPS →How to Deploy Immich on a VPS →How to Deploy Jan on a VPS →How to Deploy Jellyfin on a VPS →How to Deploy Karakeep on a VPS →How to Deploy Keycloak on a VPS →How to Deploy Leantime on a VPS →How to Deploy LibreChat on a VPS →How to Deploy Linkwarden on a VPS →How to Deploy LocalAI on a VPS →How to Deploy Mailcow on a VPS →How to Deploy Mailu on a VPS →How to Deploy Matomo on a VPS →How to Deploy Mattermost on a VPS →How to Deploy Meilisearch on a VPS →How to Deploy Memos on a VPS →How to Deploy n8n on a VPS →How to Deploy Navidrome on a VPS →How to Deploy NetBird on a VPS →How to Deploy Netdata on a VPS →How to Deploy Nextcloud on a VPS →How to Deploy Next.js to a VPS →How to Deploy Nginx Proxy Manager on a VPS →How to Deploy NocoDB on a VPS →How to Deploy ntfy on a VPS →How to Deploy Ollama on a VPS →How to Deploy Open WebUI on a VPS →How to Deploy OpenHands on a VPS →How to Deploy OpenObserve on a VPS →How to Deploy OpenProject on a VPS →How to Deploy Outline on a VPS →How to Deploy Pangolin on a VPS →How to Deploy Paperless-ngx on a VPS →How to Deploy Passbolt on a VPS →How to Deploy Plane on a VPS →How to Deploy Plausible Analytics on a VPS →How to Deploy Pocket ID on a VPS →How to Deploy PocketBase on a VPS →How to Deploy Prometheus on a VPS →How to Deploy Psono on a VPS →How to Deploy Radarr on a VPS →How to Deploy Rocket.Chat on a VPS →How to Deploy SigNoz on a VPS →How to Deploy Sonarr on a VPS →How to Deploy Stalwart on a VPS →How to Deploy Stirling-PDF on a VPS →How to Deploy Supabase on a VPS →How to Deploy Synapse on a VPS →How to Deploy Taiga on a VPS →How to Deploy TeamPass on a VPS →How to Deploy Tinyauth on a VPS →How to Deploy Traefik on a VPS →How to Deploy Trilium on a VPS →How to Deploy Twenty CRM on a VPS →How to Deploy Umami on a VPS →How to Deploy Uptime Kuma on a VPS →How to Deploy Vaultwarden on a VPS →How to Deploy Vikunja on a VPS →How to Deploy wg-easy on a VPS →How to Deploy Wiki.js on a VPS →How to Deploy Zitadel on a VPS →How to Deploy Zulip on a VPS →Docker & Compose on Ubuntu 26.04 →Building AI Workflows with n8n →Install Open WebUI with Ollama →Adding AI-Powered Insights to Plausible Analytics →Building AI-Powered Apps with Supabase and pgvector →

We use analytics cookies (Google Analytics, PostHog) to see which guides are useful. No ad networks, no cross-site tracking. See our privacy policy.