How to update to 1.9.0
HertzBeat 1.9.0 Upgrade Guide
1.9.0 is not a drop-in replacement for the 1.8.x jar or image. The runtime, the configuration file, the authorization rules, the collector credential format, the GreptimeDB table schema and several alerting semantics all changed incompatibly. Read this guide in full before you start.
This guide applies to an upgrade from a released 1.8.x to a released 1.9.0. On 1.6.x / 1.7.x, first upgrade step by step to 1.8.x using the 1.7.0 upgrade guide and the guides for the versions in between, verify that it runs, and only then use this guide.
Follow the HertzBeat New Version Upgrade
Am I Affected? Quick Check
| Your deployment or usage | Sections to read |
|---|---|
| Installation package | Runtime, Manager configuration, Authorization rules |
| Docker / Docker Compose | Manager configuration, Authorization rules, Docker Compose |
| Helm | Helm deployments; do not just override the image tag on a chart that has not been adapted |
| GreptimeDB enabled | GreptimeDB; skipping this stops log ingestion |
| Remote collectors deployed | Collectors; they must reach 1.9.0 before the manager does |
| SFTP, Synology, NVIDIA, Redis Sentinel or push style monitors | Monitors |
| Scheduled thresholds, silences or group convergence | Alerting and notification |
| Scripts or third-party systems calling the HertzBeat API | API consumers |
Third-party jars in ext-lib/, or the template marketplace deployed | Dependencies, Removed features |
| Your own plugins | Plugin developers |
Recommended Order and Maintenance Window
There is no fixed dependency between the collectors and GreptimeDB, but both must be ready before manager 1.9.0 starts. The recommended order:
- Use the quick check above to identify every applicable section and rehearse in a test environment. Download and stage the 1.9.0 package, Java 25 and the new
application.ymlandsureness.yml, without overwriting the running 1.8.x files yet. - Before changing production data, upgrading a component or replacing a file, back up the configuration and deployment files listed in the next section and record the current version of every component.
- While 1.8.x is still running, complete the actions that must happen in advance: delete push style monitors and their custom templates, review scheduled alert rules, rename hostless monitors where needed, clean up duplicate bulletin names, export templates from the marketplace, and remove conflicting old dependencies from
ext-lib/. - With remote collectors, upgrade them to 1.9.0 one by one first. A 1.9.0 collector works against a 1.8.x manager, so this step usually needs no manager downtime.
- Enter the maintenance window: stop the manager, and pause every sender that writes HertzBeat product logs to that GreptimeDB, directly or indirectly. Stop the collectors too if you want to avoid continuous retries.
- Now that writes have stopped, take consistent backups or snapshots of the metadata database and GreptimeDB.
- With GreptimeDB enabled, follow the staged upgrade path in this guide and rename the old
hertzbeat_logstable. - Replace the whole manager installation, merge the new configuration and authorization rules, then start manager 1.9.0. With external writes still paused, copy the log history back and verify it after the new table has been created.
- Resume one sender in a controlled manner, or send one test log, and work through the post-upgrade checks. Resume all external writers and close the window only after they pass.
The manager is unavailable from step 5 until it starts successfully in step 8. Collectors can be upgraded ahead of the window. Product log writes must stay stopped while GreptimeDB is upgraded, the table is renamed, the manager is switched over and the log history is copied back. The actual duration depends on the staged GreptimeDB upgrade, any volume restore and the history copy. Resume all senders only after a controlled new-log check succeeds.
Backups Before Upgrading
Backups happen in two stages. Before making any change, preserve:
- HertzBeat and collector files:
define/,ext-lib/,config/, plus any externally mountedapplication.yml,sureness.yml, certificates and key files. - Container deployment files:
.env, the compose file you actually use and any local edits to it; for Kubernetes, the ConfigMaps, Secrets, values files and mount declarations. - A record of the current 1.8.x package or image tag, every collector version and the GreptimeDB version, so the same artifacts can still be obtained if you need to roll back.
After entering the maintenance window and stopping the related writes, take these consistent backups:
- The metadata database: the complete
data/directory for H2, or a consistent dump of MySQL / PostgreSQL. - With GreptimeDB enabled: the complete data directory, Docker volume or external storage snapshot. This backup must be restorable together with the GreptimeDB version you were running before the upgrade.
Do not overwrite or delete these backups until the upgrade is complete. Rehearsing both the upgrade and the rollback is strongly recommended if you use GreptimeDB, scheduled alert rules or a customised sureness.yml.
Runtime
Java 25
1.9.0 raises the Java runtime requirement from Java 17 to Java 25. As in 1.8.x, the generic installation package does not bundle a JDK.
- The default Java on the server is already 25: nothing to do.
- The default Java is not 25 (for example 8, 11, 17 or 21) and no other application depends on it: install Java 25 and point the environment variable at it.
- Other applications depend on the older Java: download Java 25, rename the extracted folder to
javaand place it in the HertzBeat installation directory. The startup script prefers it.
The Docker images are now based on eclipse-temurin:25-jdk, so Docker users need no action.
Package layout
The startup scripts now put lib/* and ext-lib/* on the classpath explicitly instead of relying on the jar manifest. Replace the whole installation directory; do not swap only the main jar.
Collector image path
The collector image working directory moved from /opt/apache-hertzbeat-collector-<version>-bin/ to the fixed /opt/hertzbeat-collector/. Update every volume or Kubernetes ConfigMap mount that targets config/, logs/ or ext-lib/.
Manager Configuration: application.yml
JPA provider switched from EclipseLink to Hibernate
This is the easiest change to miss and the most immediate failure. Keeping the 1.8.x application.yml prevents the manager from starting. Replace the whole spring.jpa block according to your database.
1.8.x form (remove it):
spring:
jpa:
show-sql: false
database-platform: org.eclipse.persistence.platform.database.MySQLPlatform
database: h2
properties:
eclipselink:
logging:
level: SEVERE
1.9.0 form, H2:
spring:
jpa:
show-sql: false
database: h2
hibernate:
ddl-auto: update
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect
format_sql: true
MySQL:
spring:
jpa:
show-sql: false
database: mysql
hibernate:
ddl-auto: update
properties:
hibernate:
dialect: org.hibernate.dialect.MySQLDialect
format_sql: true
PostgreSQL:
spring:
jpa:
show-sql: false
database: postgresql
hibernate:
ddl-auto: update
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
hibernate.ddl-auto: update is mandatory. Tables and columns new in 1.9.0 (such as hzb_auth_token and the ntfy fields on notice receivers) are created by Hibernate. Without it the manager starts, but those features fail at runtime with SQL errors.
The Flyway migration new in 1.9.0 runs automatically at startup and needs no manual action. One of its statements has a visible effect:
- It widens
hzb_alert_define.exprto a large-text type (TEXTon PostgreSQL,LONGTEXTon MySQL,CLOBon H2), so expressions binding many monitors are no longer truncated.
Docker Compose overrides the file inside the image with ./conf/application.yml, and docker run setups commonly mount -v $(pwd)/application.yml:/opt/hertzbeat/config/application.yml. Pulling a new image does not update a mounted file, so a 1.8.x copy hits the EclipseLink failure above just the same. Regenerate it from the 1.9.0 version and merge your own changes back in.
Other configuration keys
| 1.8.x | 1.9.0 | Notes |
|---|---|---|
management.endpoints.enabled-by-default: on | management.endpoints.access.default: read_only | Spring Boot 4 removed the old key. Keeping it does not fail, but the actuator falls back to unrestricted mode. |
| none | springdoc.api-docs.enabled: false, springdoc.swagger-ui.enabled: false | The OpenAPI document is disabled by default. Enable it when needed; only the admin role can read it. |
| none | hertzbeat.otlp.grpc.port: 14317 | New OTLP/gRPC listener when GreptimeDB is enabled, see New Version Upgrade. |
| none | hertzbeat.collector.mysql.query-engine: auto | Query engine selection for MySQL-family monitors, see Collectors. |
spring.mail.properties.mail.smtp.ssl.trust | the "verify SSL certificate" toggle in the mail settings page | The yml property is no longer honoured; disable verification in the UI for self-signed SMTP servers. |
Authorization Rules: sureness.yml
1.9.0 tightens the role requirements of many endpoints. The 1.9.0 installation package, the Docker image, the conf/sureness.yml of all five Compose variants and script/sureness.yml all ship the new rules.
- Nothing mounted over
sureness.yml: no action needed. - A
sureness.ymlcopy downloaded or copied on 1.8.x is mounted: replace it with the 1.9.0 file. The authorization rules changed in 1.9.0, and a mounted file overrides the one shipped in the package and the image. - You customised the file: merge your changes onto the 1.9.0 version.
What changed:
- The alert and manager SSE streams (
/api/alert/sse/**,/api/manager/sse/**) require an authenticated user in 1.9.0. The browser-nativeEventSourcecannot send an authorization header; the web UI switched to an authenticated fetch stream and third-party dashboards must do the same. - The following are admin only in 1.9.0: plugin upload
/api/plugin/**, AI and SOP/api/ai/**, API token management/api/account/token*, configuration writes/api/config/**, template writes/api/apps/**(PUT/DELETE),/actuator/**,/api/metrics,/api/warehouse/query, single monitor deletion. Automation calling these endpoints has to switch to admin credentials. - Creating and editing alert definitions (
POST/PUT /api/alert/define,/api/alert/defines/import) and the threshold preview routeGET /api/alert/define/preview/**are admin only in 1.9.0; guest cannot read alert definitions. - Deleting labels,
DELETE /api/label/**, is admin only in 1.9.0. - External alert ingestion
POST /api/v2/alertsaccepts admin and user only in 1.9.0; Alertmanager / Zabbix integrations need credentials with one of those roles. - The system secret configuration can no longer be read through the REST API; such requests return 403 for every role.
- The OpenAPI endpoints
/v3/api-docs/**are admin only in 1.9.0. - CORS no longer returns
Access-Control-Allow-Credentials; cross-origin front ends relying on cookies must switch to anAuthorization: Bearerheader.
If Prometheus scrapes /actuator/prometheus, configure the scrape job with an admin-role API token.
GreptimeDB (only when enabled)
1.9.0 initialises the GreptimeDB tables and the log pipeline at startup and exits the manager when any step fails, instead of degrading as 1.8.x did. Finish this section before starting the manager.
Version requirement
The greptime/greptimedb:v0.14.3 shipped with the 1.8.x Docker Compose does not support the 1.9.0 log pipeline, and Compose 1.9.0 now pins v1.1.3. Do not replace v0.14.3 directly with v1.1.3. The official GreptimeDB upgrade path requires releases earlier than v0.16 to upgrade to v0.16 first, and then to v1.0.
Proceed in stages, following the official instructions applicable to each version. Before continuing, verify at every stage that GreptimeDB starts, the old tables can be queried and their row counts are as expected, then create a new restorable snapshot for the next stage:
v0.14.3→ a compatiblev0.16.xrelease;v0.16.x→v1.0.x;v1.0.x→ the Compose version,v1.1.3.
If verification fails at any stage, restore the snapshot from before that stage. Do not let a later GreptimeDB version continue writing to that data directory.
The configured GreptimeDB account needs permission to create and alter tables and to upload pipelines.
The body column of hertzbeat_logs
1.8.x created body as a JSON column; 1.9.0 writes it as STRING. 1.9.0 does not alter the existing table, so after a plain upgrade GreptimeDB rejects every new log write. The only visible symptom is that logs stop updating, and log-based alerting stops with them.
With the manager and every other log writer stopped, and the GreptimeDB snapshot taken, first check the old table and its size:
SHOW CREATE TABLE hertzbeat_logs;
SELECT COUNT(*) AS row_count, MIN(time_unix_nano) AS min_time, MAX(time_unix_nano) AS max_time
FROM hertzbeat_logs;
Once you have confirmed that body is JSON and the table name is right:
ALTER TABLE hertzbeat_logs RENAME hertzbeat_logs_v18;
Start 1.9.0 so it creates the new table, but keep external log writes paused. Confirm that the new table is empty, then copy the history back:
INSERT INTO hertzbeat_logs (time_unix_nano, observed_time_unix_nano, trace_id, span_id, trace_flags,
severity_text, severity_number, body, attributes, resource, instrumentation_scope, dropped_attributes_count)
SELECT time_unix_nano, observed_time_unix_nano, trace_id, span_id, trace_flags,
severity_text, severity_number, json_to_string(body), attributes, resource, instrumentation_scope, dropped_attributes_count
FROM hertzbeat_logs_v18;
The new table uses append-only mode, so this INSERT ... SELECT is not idempotent and running it twice duplicates logs. Record the old table's row count and earliest and latest timestamp before the copy. Those values should match in both tables after it succeeds. Only then resume one sender in a controlled manner and verify that a new log arrives.
If the client disconnects, times out or cannot tell whether the statement completed, do not rerun it directly. Keep all product-log writers paused and compare the two tables' counts and time ranges. If you cannot prove that the target is still empty or that the copy completed, restore the pre-upgrade snapshot and repeat the whole GreptimeDB upgrade. If the target data must be retained, first design and verify a deduplicating migration in an isolated environment.
Do not assume GreptimeDB DDL rolls back with a transaction the way a relational database would: verify the result of every statement before moving on.
Do not run ALTER TABLE hertzbeat_logs MODIFY COLUMN body STRING. The statement succeeds, but the existing JSON binary content is then read back as garbage strings.
Self-monitoring tables renamed
HertzBeat's self-monitoring log and trace tables were renamed from hzb_logs / hzb_traces to hzb_internal_logs / hzb_internal_traces. There is no automatic migration; see New Version Upgrade for how to approach it.
The product trace table hertzbeat_traces is created fresh: 1.8.x had no trace ingestion route, no trace query API and no traces page, and hzb_traces holds nothing but HertzBeat's own spans, so there is no historical business trace data to migrate.
Collectors
1.9.0 changed the AES cipher format of monitor credentials. The manager never decrypts; it forwards the stored ciphertext to collectors. A 1.8.x collector cannot decrypt passwords sent by a 1.9.0 manager, so monitors that need authentication may fail to collect. The manager rewrites some credentials in the new format on its first start, without any user action.
The handshake carries no version gate, so nothing stops the mismatched pair from connecting. The collector logs an AES decode error and the failure usually surfaces as an authentication error returned by the monitored service, which is easy to mistake for a wrong credential.
Correct order: upgrade every collector to 1.9.0 first (a 1.9.0 collector decrypts the 1.8.x format and works fine against an old manager), then upgrade the manager.
MySQL-family query engine
1.9.0 ships a built-in R2DBC query engine for MySQL / MariaDB / OceanBase / TiDB. query-engine defaults to auto: when mysql-connector-j is present under ext-lib/, JDBC is used and behaviour is unchanged; otherwise the built-in engine is used. If your driver lives outside ext-lib/, set HERTZBEAT_COLLECTOR_MYSQL_QUERY_ENGINE=jdbc explicitly.
The built-in engine is stricter than JDBC:
- Connection options in the
urlparameter (useSSL,serverTimezone, ...) are ignored. - Custom SQL goes through an allowlist: only a single statement starting with
SELECTorSHOWis accepted. - Any comment (
--,#,/* */) is rejected, and so is any statement containinginsert/update/delete/replace/merge/alter/drop/truncate/create/callas a whole word — which also rules outWITH ... SELECT,DESCandSHOW CREATE TABLE.
The built-in R2DBC path prefers TLS. If a TLS handshake or a MySQL authentication plugin compatibility error appears after the upgrade, configure a compatible TLS and authentication method for the monitoring account, or place mysql-connector-j in ext-lib/ and set HERTZBEAT_COLLECTOR_MYSQL_QUERY_ENGINE=jdbc to return to the JDBC path.
JDBC database name validation
The database parameter of JDBC monitors must now match [A-Za-z0-9_$][A-Za-z0-9_$.-]{0,63}. For names containing spaces, non-ASCII characters or more than 64 characters, put the full connection string in the url parameter instead.
Monitors
SFTP monitors
SFTP monitors (FTP template with SSL enabled) reject every server host key after the upgrade. Each SFTP monitor needs a host-key fingerprint, or the temporary skip-verification option. See New Version Upgrade.
Synology template renamed
The Synology template app identifier changed from synology to synology_nas, and existing monitors are not migrated automatically. With the manager stopped and the metadata database backed up, first confirm which rows are affected:
SELECT id, name, app FROM hzb_monitor WHERE app = 'synology';
Once the result looks right, run the update and query again to confirm nothing was missed:
UPDATE hzb_monitor SET app = 'synology_nas' WHERE app = 'synology';
SELECT id, name, app FROM hzb_monitor WHERE app = 'synology';
Alert definitions, notice rules and dashboard filters referencing synology need the same change.