maborak.com~/blog/zabbix-6-7-mistakes-before-8software & infrastructure engineering
Back to all articles

Zabbix 6.0 and 7.0 upgrade and configuration traps, and where they return in 8.0

The upgrade and configuration mistakes Zabbix documented for 6.0 and 7.0, the same traps in 8.0's notes, and a tested 7.0 to 8.0 upgrade in containers.

By the Maborak engineering team · · 11 min read

Every Zabbix major release ships upgrade notes, and the 6.0, 7.0 and 8.0 notes repeat the same kinds of warning: the database got too old, a schema change had to be done by hand, stored text was rewritten, a proxy stopped receiving configuration. This post collects the traps Zabbix documented for 6.0 and 7.0, shows where each one returns in 8.0, and adds a second, shorter list of configuration mistakes the documentation warns about. A 7.0 to 8.0 upgrade was run in containers to check the most important ones.

Checked 29 October 2026. Upgrade notes, requirements and parameter defaults are from the Zabbix documentation for 6.0, 7.0 and 8.0 linked under Sources, marked [vendor]. Zabbix still labels the 8.0 documentation as a development version, and 8.0 is at release candidate 3, so its notes can change before the LTS. The probes ran Zabbix 7.0.32 and 8.0.0rc3 in Docker and are marked [reproduced]. The post lists documented traps; it makes no claim about how often each one happens.

Background

The Zabbix documentation lists 6.0 LTS, 7.0 LTS and 7.4 as supported, with 8.0 planned as the next LTS. An upgrade starts the new server against the old database and the server rewrites the schema on first start; the earlier note on preparing an 8.0 inventory covers that mechanism. Direct upgrades to 8.0 are supported from 2.0.x onward, so a 6.0 estate can jump straight to 8.0, crossing every change listed below in one maintenance window. [vendor]

Mechanics

Upgrade traps, and their 8.0 versions

1. The database is below the new minimum, and the server refuses to start. Since 6.0 the server and proxy exit when they detect an unsupported database version, unless AllowUnsupportedDBVersions=1 is set. 6.0 required MySQL 8.0, MariaDB 10.5, PostgreSQL 13 or TimescaleDB 2.0.1. 8.0 raises the minimums again: [vendor]

Database 7.4 minimum 8.0 minimum
MySQL/Percona 8.0.30 8.4.0
MariaDB 10.5.00 10.11.00
PostgreSQL 13.0 15.0
TimescaleDB 2.13.0 2.20.0

2. The frontend’s PHP is too old. 7.0 raised the minimum from PHP 7.4.0 to 8.0.0; 8.0 raises it to 8.2.0. [vendor]

3. A schema change the automatic upgrade does not do. Each release has had one:

  • 6.0: new installations got primary keys on history tables, but “there is no automatic upgrade to primary keys for existing installations”. The migration is a documented manual procedure.
  • 7.0: TimescaleDB with compression had to be converted to double-precision values before upgrading, and TimescaleDB 1.x support was removed.
  • 8.0: a MySQL database created with utf8mb3/utf8mb3_bin should be converted to utf8mb4/utf8mb4_bin before upgrading (“strongly recommended”), and on TimescaleDB the new history_json hypertable must be configured by hand. [vendor]

4. Stored text is rewritten or stops resolving.

  • 6.0: Zabbix packages switched to PCRE2, and some regular expressions changed meaning; the notes give ^[\w-\.] as a pattern that must become ^[-\\w\\.]. Positional macros ($1…$9) and user macros in item names were removed.
  • 7.0: the upgrade adds escaping to backslashes in history function parameters, which can push a parameter past the 255-character limit and break the trigger. Since 7.0.11 the upgrade skips such parameters and logs a warning.
  • 8.0: these built-in macros are dropped: {ACK.DATE}, {ACK.MESSAGE}, {ACK.TIME}, {EVENT.ACK.HISTORY}, {HOSTNAME<1-9>}, {IPADDRESS<1-9>}, {PROFILE.*}, {TRIGGER.COMMENT}, {TRIGGER.KEY}, {STATUS} and {USER.ALIAS}, replaced by the {EVENT.UPDATE.*}, {HOST.*}, {INVENTORY.*}, {TRIGGER.DESCRIPTION}, {ITEM.KEY}, {TRIGGER.STATUS} and {USER.USERNAME} forms. JavaScript preprocessing can no longer modify built-in prototype methods. [vendor]

A macro that stops resolving does not fail loudly. The notification still goes out, with the literal macro text where the value used to be. [inferred], from the notes listing these as dropped rather than converted.

5. The first patch releases carry their own traps. Upgrading from 7.0.0 to 7.0.1 or later with TimescaleDB crashed the server, and a PostgreSQL or TimescaleDB backup taken on 7.0.0 to 7.0.4 failed to restore with pg_restore (missing base36_decode function). Both have manual fixes in the known issues. The x.y.z notes matter as much as the major ones. [vendor]

6. Binary logging blocks the MySQL schema upgrade. The 6.0 and 7.0 notes both warn that with binary logging on and no superuser rights, the upgrade needs SET GLOBAL log_bin_trust_function_creators = 1, turned off again afterwards. [vendor]

7. Proxies, agents and their local data. An 8.0 server fully supports only 8.0 proxies. 7.0 to 7.4 proxies are “outdated”: they still send data and run remote commands, but receive no configuration changes. Proxies older than 7.0 are unsupported, and the server ignores their data. A proxy using SQLite deletes and recreates its database file when the file is older than required, losing any history still buffered there. On the agent side, agent 2 from 4.4 onward still works with 8.0, pre-1.6 agents are no longer supported, and the 7.0 Windows agent changed where it looks for its configuration file. [vendor]

8. Templates and media types stay at their old versions. 8.0’s notes say it outright: “templates and media types are not upgraded automatically”, to avoid overwriting local edits. New behaviour that lives in templates, such as 8.0’s TLS certificate verification for HTTPS templates and webhooks (the {$HTTP.TLS.VERIFY} macro), only arrives when the templates are re-imported. [vendor]

9. API access changes meaning. In 8.0, a user role with an API allow list that is empty denies all methods, where it used to allow all of them. The upgrade adds a * wildcard to such roles to keep them working, and the notes ask administrators to replace it with the methods each role needs. The hostinterface.* write methods are also removed from existing roles’ allow lists. [vendor]

Configuration traps

Active checks and the hostname. An agent’s Hostname is “case-sensitive” and “must match hostnames as configured on the server”. A mismatch does not stop the agent; the server simply refuses to send it a list of active checks. [vendor]

Caches left at their defaults. CacheSize (configuration cache) defaults to 32M and ValueCacheSize to 8M. When the value cache runs out of memory, the server writes a warning to its log every five minutes and keeps running, with trigger and calculated-item evaluation reading more from the database. The internal items zabbix[vcache,buffer,<mode>] and zabbix[vcache,cache,<parameter>] and the diaginfo=valuecache runtime command show the state. [vendor]

TimescaleDB housekeeping that never drops partitions. On TimescaleDB, “Override item history period”, “Override item trend period” and “Enable internal housekeeping” must all be enabled. Otherwise data is still stored in partitions, but “the housekeeper will not drop outdated partitions”. [vendor]

Timeouts that moved. In 7.0, item timeouts moved into the frontend’s global settings, seeded from the server’s Timeout on upgrade. Outdated proxies and agents keep using the Timeout in their own configuration files. Remote commands to active-only agents need a server or proxy timeout longer than the agent’s active check refresh interval. [vendor]

User parameters with %. 8.0 adds % to the characters UnsafeUserParameters blocks, for agent and agent 2. A user parameter that passes a % in its arguments is refused unless unsafe parameters are allowed. [vendor]

Impact

The traps fall into three groups by when they bite:

  • At start-up: database or PHP below the minimum. The server or frontend refuses to run, which is loud and easy to notice. Probe 1 shows the message.
  • During the upgrade: manual schema work skipped, binary logging, long backslash parameters, the 7.0.0 TimescaleDB crash. These stop the upgrade halfway, which is why a tested restore matters.
  • Days later, silently: dropped macros in notifications, outdated proxies running on frozen configuration, templates without the new TLS checks, API roles with a wildcard, value cache warnings, partitions that are never dropped. These are the expensive ones, because the upgrade looks finished.

A 6.0 estate going straight to 8.0 has the widest exposure: every database, PHP and schema change from 6.0 to 8.0 at once, and its 6.0 proxies become unsupported the moment the server is upgraded. [inferred], from the version tables above.

Probe

Three probes on Docker, using the official zabbix/zabbix-server-pgsql images (alpine-7.0.32, and alpine-trunk, which reports Zabbix 8.0.0rc3 (revision 11ee799)), PostgreSQL 14 and 16 Alpine images, and zabbix/zabbix-agent2:alpine-7.0.32. The shared settings are -e DB_SERVER_HOST=<db> -e POSTGRES_USER=zabbix -e POSTGRES_PASSWORD=zabbix -e POSTGRES_DB=zabbix on a private Docker network.

Probe 1: 8.0 on a database below its minimum. 8.0.0rc3 against PostgreSQL 14.24: [reproduced]

server container state: exited exit=1
Unable to start Zabbix server due to unsupported PostgreSQL database version (14.24).
Use of supported database version is highly recommended.
Override by setting AllowUnsupportedDBVersions=1 in Zabbix server configuration file at your own risk.
Zabbix Server stopped. Zabbix 8.0.0rc3 (revision 11ee799).

Probe 2: a 7.0 to 8.0 upgrade. Zabbix 7.0.32 created its schema on PostgreSQL 16, was stopped, and 8.0.0rc3 was started on the same database: [reproduced]

7.0.32 schema: dbversion=7000000 / 7000030
current database version (mandatory/optional): 07000000/07000030
required mandatory version: 07050209
starting automatic database upgrade
completed 100% of database upgrade
database upgrade fully completed
8.0 schema: dbversion=7050209 / 7050209
history_json table: history_json

The upgrade took 8 seconds, on a database holding only the default schema and no history. That number says nothing about a production database, where upgrade time grows with the history tables. On PostgreSQL the history_json table was created by the upgrade; the manual step in trap 3 applies to TimescaleDB.

Probe 3: active checks with the wrong case. The schema ships a host called Zabbix server. A 7.0.32 agent 2 pointed at the 8.0.0rc3 server, first with ZBX_HOSTNAME="zabbix server", then with "Zabbix server": [reproduced]

== agent Hostname="zabbix server"
cannot send list of active checks to "172.22.0.4": host [zabbix server] not found
cannot process heartbeat from host "zabbix server": host not found
== agent Hostname="Zabbix server"
(no errors in the server log)

The agent itself started normally in both cases; the error only appears in the server log. The run also shows a 7.0 agent 2 working with an 8.0 server, as the compatibility table says.

Finding dropped macros before upgrading. This query lists where notification text, trigger names and comments, and scripts still use the macros 8.0 drops. It ran on both the 7.0.32 and the upgraded 8.0.0rc3 schema on PostgreSQL 16. On the stock data it returned no rows; after one {ACK.MESSAGE} was added to an action message, it returned opmessage | 1. [reproduced]

WITH texts(source, id, txt) AS (
  SELECT 'media_type_message', mediatype_messageid, subject || ' ' || message FROM media_type_message
  UNION ALL SELECT 'opmessage', operationid, coalesce(subject,'') || ' ' || coalesce(message,'') FROM opmessage
  UNION ALL SELECT 'triggers', triggerid, description || ' ' || comments FROM triggers
  UNION ALL SELECT 'scripts', scriptid, command FROM scripts
)
SELECT source, count(*) AS rows_with_dropped_macros
FROM texts
WHERE txt ~ '\{(ACK\.(DATE|MESSAGE|TIME)|EVENT\.ACK\.HISTORY|HOSTNAME[1-9]?|IPADDRESS[1-9]?|PROFILE\.[A-Z._]+|TRIGGER\.(COMMENT|KEY)|STATUS|USER\.ALIAS)\}'
GROUP BY source ORDER BY source;

On MySQL, replace ~ with REGEXP and || with CONCAT().

Mitigation

Before the 8.0 upgrade, in this order:

  1. Upgrade the database and PHP first, as their own change: PostgreSQL 15+, MySQL 8.4+, MariaDB 10.11+ or TimescaleDB 2.20+, and PHP 8.2+. Current 6.0 and 7.0 releases already support these (7.0: PostgreSQL 13.0–18.x, MySQL 8.0.30–9.7.x, MariaDB 10.5–12.3.x, TimescaleDB 2.13–2.29.x, PHP 8.0–8.5; 6.0 frontends support PHP up to 8.3). [vendor] Run the existing Zabbix on the new database for a while before upgrading Zabbix itself.
  2. Do the manual schema work that applies: history primary keys if the database dates from before 6.0, TimescaleDB double precision, the MySQL utf8mb4 conversion, the TimescaleDB history_json hypertable.
  3. Fix stored text: run the macro query above and replace each dropped macro; check regular expressions written before 6.0; move long history function parameters into user macros.
  4. Plan the proxies: upgrade proxies right after the server. With a 6.0 estate, every proxy must move in the same window, because 6.0 proxies are unsupported by an 8.0 server. Let SQLite proxies send their buffered data before upgrading them.
  5. Rehearse on a restored copy, and restore the backup you just took with the tool you will use in an emergency. Read the x.y.z notes of the exact 8.0 release, not only 8.0.0.
  6. After the upgrade: re-import the templates and media types you rely on and set {$HTTP.TLS.VERIFY} deliberately; replace the * wildcard in API roles; check UnsafeUserParameters against user parameters that take %.
  7. Configuration review, whatever the version: compare agent Hostname values with the frontend, watch the value cache internal items, and on TimescaleDB enable the three housekeeping options.

Rollback: keep the database backup and the old packages until the upgraded server has run a full housekeeping cycle. The schema upgrade is one-way; returning to 7.x means restoring the backup.

Verification

zabbix_server -V
psql -U zabbix -d zabbix -c 'SELECT mandatory, optional FROM dbversion;'
psql -U zabbix -d zabbix -f dropped-macros.sql      # the query above; expect 0 rows
zabbix_server -R diaginfo=valuecache                # value cache usage
grep -E 'not found|unsupported|outdated' /var/log/zabbix/zabbix_server.log | tail

Then check what the version string cannot show: in Administration > Proxies, every proxy’s version and compatibility state; one notification from each media type actually received, with values instead of macro names; and the server log free of host [...] not found lines for active agents.

Sources

Back to all articles

Technologies we work with

  • Argo CD
  • Kubernetes
  • Docker
  • Podman
  • Ansible
  • Jenkins
  • Chef
  • Proxmox VE
  • Google Cloud
  • Linux
  • Red Hat
  • CentOS
  • NGINX
  • Apache
  • Traefik
  • Prometheus
  • Grafana
  • PostgreSQL
  • MySQL
  • MariaDB
  • Redis
  • etcd
  • Qdrant
  • Elasticsearch
  • Logstash
  • Kibana
  • PagerDuty
  • Go
  • Python
  • FastAPI
  • PHP
  • TypeScript
  • React