Apache HertzBeat 1.9.0 on AWS User Guide
Overview
This guide covers launching and using the Apache HertzBeat 1.9.0 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg Zabbix and Infrastructure Monitoring listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
Apache HertzBeat is an open source, real time monitoring and alerting system from the Apache Software Foundation. It monitors web sites, APIs, TLS certificates, servers, databases, middleware and network services without installing an agent on the target, evaluates threshold rules, and sends alerts through email, webhooks, Slack, Discord, Telegram and many other channels. This image runs the official Apache HertzBeat 1.9.0 package on OpenJDK 25 as a self contained single node: an embedded H2 database holds the configuration and an embedded DuckDB store holds the metric history, so there is no external database to provision.
The cloudimg image adds:
- HTTPS from the first boot. nginx serves the console on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
- Secrets unique to your instance. First boot generates the
adminpassword, the key that signs HertzBeat's login tokens (JWT), the key that encrypts the credentials you store in monitors, and the embedded database password. The admin password is stored only as a salted hash; the password itself is written to/root/hertzbeat-credentials.txt. HertzBeat's well known defaults (admin/hertzbeatand the published JWT key) are refused. Nothing is baked into the image. - HertzBeat 1.9.0 security fixes and role rules. 1.9.0 is the release that stops serving the signing and encryption keys over the configuration API, requires sign in for the live alert, manager and log streams, and limits plugin upload, actuator and warehouse queries to administrators. The image ships exactly one account,
admin. - Fail closed. HertzBeat refuses to start until first boot has generated every secret, and nginx refuses to start until first boot has finished.
- HertzBeat on loopback only. HertzBeat listens on
127.0.0.1:1157behind nginx; the remote collector port (1158) is switched off and the OpenTelemetry gRPC receiver is bound to loopback. The only open ports are 22, 80 and 443. HertzBeat's unauthenticated Prometheus push endpoint is refused at nginx (Step 11 shows how to open it), and sign in attempts are rate limited. - Metadata access blocked for the monitoring engine. HertzBeat probes any host or URL you give it, so the HertzBeat and nginx services are blocked from the instance metadata service; a monitor pointed at
169.254.169.254simply times out. The AMI requires IMDSv2. - Hardened defaults. HertzBeat runs as its own unprivileged user in a systemd sandbox that can write only its data, its logs and the plugin directory; the program and its configuration belong to root. The console's address comes from your configuration, never from a request, and requests for a host name the instance does not serve are dropped.
- Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on (they also keep OpenJDK and nginx patched).
What is on the image: Apache HertzBeat 1.9.0 (the official apache-hertzbeat-1.9.0-bin.tar.gz, checksum verified), OpenJDK 25 from Ubuntu, nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route you use for management).
- A security group that allows TCP 22 (SSH), TCP 443 (the console) and TCP 80 (redirects to HTTPS) from your own networks only.
- Outbound access from the instance to whatever you want to monitor.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Apache HertzBeat 1.9.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, your key pair and a security group as above. HertzBeat's data lives under /var/lib/hertzbeat on the root volume.
With the AWS CLI (replace the values in angle brackets):
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=hertzbeat}]'
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| Apache HertzBeat 1.9.0 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services
First boot takes a few seconds, and HertzBeat needs about a minute more to start. Then the three units are active:
systemctl is-active hertzbeat-firstboot hertzbeat nginx
active
active
active
HertzBeat (the java process) listens on loopback only; nginx serves ports 80 and 443:
sudo ss -tlnp | awk 'NR==1 || /java|nginx|sshd/' | awk '{print $1, $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g'
State Local Peer
LISTEN 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx"))
LISTEN 0.0.0.0:22 users:(("sshd"),("systemd"))
LISTEN 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx"))
LISTEN 127.0.0.1:1157 users:(("java"))
LISTEN [::]:443 users:(("nginx"),("nginx"),("nginx"))
LISTEN [::]:22 users:(("sshd"),("systemd"))
LISTEN [::]:80 users:(("nginx"),("nginx"),("nginx"))
The versions on the image:
head -2 /usr/share/doc/hertzbeat/RELEASE
java -version 2>&1 | head -1
Apache HertzBeat 1.9.0
package apache-hertzbeat-1.9.0-bin.tar.gz
openjdk version "25.0.4.1" 2026-08-18
The console footer also shows Apache HertzBeat v1.9.0. (The Java libraries inside the 1.9.0 package are named *-2.0-SNAPSHOT.jar; that is how Apache built the 1.9.0 release, not a different version.)
Step 4: Get your URL and password
sudo cat /root/hertzbeat-credentials.txt
The file holds the address the console is served as (your instance's public DNS name by default), the administrator user and the password generated on this instance:
HERTZBEAT_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/
HERTZBEAT_ADMIN_USER=admin
HERTZBEAT_ADMIN_PASSWORD=<24 letters and digits>
Step 5: Sign in from your browser
Open HERTZBEAT_URL in your browser. The certificate is self signed (generated on your instance), so accept the browser warning once, or install your own certificate (Step 9). Sign in with admin and the password from Step 4.

On the first sign in HertzBeat shows a welcome box; tick Do not show this popup on next login and close it. The dashboard summarises your monitors by state and type and lists recent alarms:

Step 6: Add your first monitor
Choose Monitors, then New Monitor, and pick a monitor type, for example Website under Service. Enter the target host, a task name, the port and whether to use HTTPS, then choose Detect to test the connection and OK to save. Every monitor runs on the built in collector at the interval you set (60 seconds by default).

The Monitors list shows each monitor's state:

Open a monitor to see its live metrics, and the Monitor Historical Chart Detail tab for the history kept in the embedded store (90 days by default):

To be told when something breaks, add a rule under Alerting, Threshold, and a receiver (email, webhook, Slack, Telegram and others) under Alerting, Notification. Outgoing mail needs your own SMTP server, set under Settings, Message Server Setting.
Step 7: Security checks
Port 80 sends browsers to HTTPS, the console answers over HTTPS, the live alert stream needs a sign in, the push endpoint is refused, nginx closes the connection for a host name the instance does not serve (000 means no answer), and HertzBeat's default password is refused:
H=$(sudo sed -n 's#^HERTZBEAT_URL=https://\([^/]*\)/.*#\1#p' /root/hertzbeat-credentials.txt)
curl -s -o /dev/null -w 'port 80: %{http_code} -> %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
curl -sk -o /dev/null -w 'console: %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/"
curl -sk -o /dev/null -w 'alert stream without sign-in: %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/api/alert/sse/subscribe"
curl -sk -o /dev/null -w 'push endpoint: %{http_code}\n' --resolve "$H:443:127.0.0.1" -X POST "https://$H/api/push/prometheus/test"
curl -s -o /dev/null -w 'unknown host name: %{http_code}\n' -H 'Host: example.com' http://127.0.0.1/ || true
curl -sk --resolve "$H:443:127.0.0.1" -H 'Content-Type: application/json' -d '{"identifier":"admin","credential":"hertzbeat"}' "https://$H/api/account/auth/form"; echo
port 80: 301 -> https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/
console: 200
alert stream without sign-in: 401
push endpoint: 403
unknown host name: 000
{"code":5,"data":null,"msg":"Incorrect Account or Password"}
Step 8: Use the REST API
Everything in the console is available over HertzBeat's REST API. Sign in with the form endpoint to get a token (the password is read from the credentials file and passed in a temporary file, so it never appears on a command line), then send it as a bearer token. The signing and encryption keys can never be read back over the API, whatever account asks:
H=$(sudo sed -n 's#^HERTZBEAT_URL=https://\([^/]*\)/.*#\1#p' /root/hertzbeat-credentials.txt)
B=$(mktemp); trap 'rm -f "$B"' EXIT
{ printf '{"identifier":"admin","credential":"'; sudo sed -n 's/^HERTZBEAT_ADMIN_PASSWORD=//p' /root/hertzbeat-credentials.txt | tr -d '\n'; printf '"}'; } > "$B"
TOKEN=$(curl -sk --resolve "$H:443:127.0.0.1" -H 'Content-Type: application/json' --data-binary @"$B" "https://$H/api/account/auth/form" | jq -r .data.token)
curl -sk --resolve "$H:443:127.0.0.1" -H "Authorization: Bearer $TOKEN" "https://$H/api/monitors?pageSize=10" | jq -r '.data.content[] | "\(.name) \(.app) \(.instance) status=\(.status)"'
curl -sk --resolve "$H:443:127.0.0.1" -H "Authorization: Bearer $TOKEN" -o /dev/null -w 'secret config: %{http_code}\n' "https://$H/api/config/secret"
cloudimg website website www.cloudimg.co.uk:443 status=1
secret config: 403
(status=1 means up. The list is empty until you add a monitor.) For scripts that run unattended, create a long lived API token in the console under Settings, API Token Management instead of signing in each time.
Step 9: Use your own domain name and certificate
By default the console is served as your instance's public DNS name with a self signed certificate. To serve it as your own name, point a DNS record at the instance and run:
sudo hertzbeat-set-hostname monitoring.example.com
This records the name in /etc/cloudimg/hertzbeat-hostname, generates a new self signed certificate for it, pins it in nginx (other names get no answer), sets it as the base of the links in HertzBeat's alert messages, reloads nginx and restarts HertzBeat.
To use a certificate from your own certificate authority, replace /etc/nginx/ssl/hertzbeat.crt and /etc/nginx/ssl/hertzbeat.key (keep the key mode 0600) and run sudo systemctl reload nginx. With Let's Encrypt, install certbot and point it at the same name; HertzBeat's nginx site is /etc/nginx/sites-available/cloudimg-hertzbeat.
Step 10: Change the admin password
sudo hertzbeat-set-password
printf '%s\n' '<your-new-password>' | sudo hertzbeat-set-password --stdin
The first form generates a new random password; the second sets your own (24 to 64 letters and digits). Both update /root/hertzbeat-credentials.txt, store only a salted hash in /etc/hertzbeat/sureness.yml and restart HertzBeat.
More accounts. HertzBeat's accounts and their roles (admin, user, guest) are defined in /etc/hertzbeat/sureness.yml; the role rules in the same file are the ones shipped with HertzBeat 1.9.0. The image ships only admin. The startup check refuses to start HertzBeat with more than one account, so to add accounts, read the Apache HertzBeat account documentation first and remove the check from /usr/local/lib/cloudimg/hertzbeat-config.py (guard) knowingly.
Step 11: Optional features that are off by default
- Prometheus push (
/api/push/): HertzBeat accepts pushed metrics there without a sign in and can create monitors from them, so nginx refuses it. To use it, remove thelocation ^~ /api/push/line from/etc/nginx/sites-available/cloudimg-hertzbeat, runsudo systemctl reload nginx, and allow the port only from the systems that push. - Remote collectors (port 1158): HertzBeat can hand work to collectors on other hosts. The image switches the collector server off (
scheduler.server.enabled: falsein/etc/hertzbeat/application.yml); the built in collector runs every monitor. If you turn it on, the port listens on every interface, so open it in the security group only to your collectors' addresses. - OpenTelemetry gRPC (port 14317): bound to
127.0.0.1by theHERTZBEAT_OTLP_GRPC_HOSTsetting in/etc/systemd/system/hertzbeat.service. - AI assistant and MCP server (the Ask me anything box and Advanced, MCP Server): administrator only in 1.9.0; the assistant does nothing until you configure a model provider for it.
Step 12: Metadata access is blocked for HertzBeat
HertzBeat connects to any host or URL a monitor names, so the HertzBeat and nginx services cannot reach the EC2 instance metadata service or the cloud wire server; a monitor pointed at 169.254.169.254 times out instead of returning the instance's credentials:
systemctl show -p IPAddressDeny hertzbeat nginx
IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
The block is in /etc/systemd/system/hertzbeat.service.d/50-cloudimg-imds-deny.conf (and the nginx equivalent). The rest of the instance, including the SSM agent and first boot, uses IMDSv2 as usual.
Step 13: Your data and backups
HertzBeat keeps its configuration and monitors in an embedded H2 database and the metric history in an embedded DuckDB store:
sudo ls /var/lib/hertzbeat /var/lib/hertzbeat/db
/var/lib/hertzbeat:
db
history.duckdb
history.duckdb.wal
logs
/var/lib/hertzbeat/db:
hertzbeat.mv.db
Back up /var/lib/hertzbeat together with /etc/hertzbeat (or snapshot the root volume): the database password and the key that encrypts the credentials stored in your monitors are in /etc/hertzbeat/application.yml, and a copy of the database is not readable without them. Stop HertzBeat (sudo systemctl stop hertzbeat) for a consistent file copy.
The image ships the HertzBeat release named in the listing. Ubuntu's unattended upgrades keep the operating system, nginx and OpenJDK patched; to move to a newer HertzBeat release later, snapshot the instance first and follow the Apache HertzBeat upgrade guide for that release, keeping /var/lib/hertzbeat and /etc/hertzbeat.
Step 14: Server components
| Component | Path |
|---|---|
| HertzBeat program | /opt/hertzbeat (release record in /usr/share/doc/hertzbeat/RELEASE) |
| Settings | /etc/hertzbeat/application.yml (linked as /opt/hertzbeat/config) |
| Accounts and role rules | /etc/hertzbeat/sureness.yml |
| Data (H2 database, DuckDB history) | /var/lib/hertzbeat |
| Uploaded plugins | /opt/hertzbeat/lib/plugin-lib |
| Logs | /var/log/hertzbeat (also journalctl -u hertzbeat) |
| Service units | /etc/systemd/system/hertzbeat.service, /etc/systemd/system/hertzbeat-firstboot.service |
| nginx site | /etc/nginx/sites-available/cloudimg-hertzbeat |
| Certificate and key | /etc/nginx/ssl/ |
| Host name and password helpers | /usr/local/sbin/hertzbeat-set-hostname, /usr/local/sbin/hertzbeat-set-password |
| Credentials | /root/hertzbeat-credentials.txt (mode 0600) |
Step 15: Security recommendations
- Allow ports 22, 80 and 443 in the security group from your own networks only.
- Keep the administrator password secret, and rotate it if it may have been shared (Step 10).
- Use your own DNS name and a certificate your browsers trust (Step 9).
- Monitors store target credentials (database and SSH passwords) encrypted with this instance's key; give HertzBeat least privilege accounts on the systems it watches.
- Leave the push endpoint, remote collector port and metadata block as shipped unless you need them (Step 11).
- Keep the operating system patched; unattended security upgrades are on, and
sudo apt-get update && sudo apt-get upgradeapplies everything else. - Back up
/var/lib/hertzbeatand/etc/hertzbeatregularly (Step 13).
Support and licensing
Apache HertzBeat is open source under the Apache License 2.0 (its licence and notice are at /usr/share/doc/hertzbeat/); there is no per user or per CPU licence fee. Apache HertzBeat, HertzBeat and Apache are trademarks of The Apache Software Foundation; cloudimg is not affiliated with or endorsed by it. The AMI price covers cloudimg's hardening, first boot setup and support.
- Email: support@cloudimg.co.uk
- Website: https://www.cloudimg.co.uk
- Support: 24/7, with a 24 hour response SLA