Databases AWS

Apache HBase 2.5.16 on AWS User Guide

| Product: Apache HBase 2.5.16 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using Apache HBase 2.5.16 on Ubuntu 24.04 from cloudimg on Amazon EC2. It is one version of the cloudimg Hardened Apache Cassandra, FerretDB & NoSQL Database AMIs listing on AWS Marketplace; you pick it as the software version when you subscribe and launch. The other databases in the listing are described on the listing's guide hub.

Apache HBase is a wide-column NoSQL database modelled on Google's Bigtable. Rows are sorted by row key, cells are grouped into column families, every cell is versioned by timestamp, and tables split into regions that a cluster spreads across RegionServers. You use it through the HBase shell, the Java client API or the REST and Thrift gateways. This image runs HBase in standalone mode: the HMaster, one RegionServer and an embedded ZooKeeper run together in one Java process on one instance, storing data on the local file system. That is a complete HBase for development, testing, prototyping and single-node workloads, without a Hadoop or HDFS cluster.

HBase has no authentication of its own unless you set up Kerberos, so an upstream install exposes its ports and web pages to anyone who can reach them. The cloudimg image changes that:

  • HBase on localhost only. ZooKeeper (2181), the Master (RPC 16000, web UI 16010) and the RegionServer (RPC 16020, web UI 16030) are bound to 127.0.0.1 and are never reachable from the network. Never open those ports in a security group.
  • HTTPS from the first boot for the status UI. nginx is the only network entry point. It publishes the HBase Master status UI on port 443 with a certificate generated on your instance's first boot; port 80 only redirects to HTTPS.
  • A password unique to your instance. Every page except a static /healthz needs HTTP Basic Auth as the user admin. The password is generated on first boot and written to the root only file /root/hbase-credentials.txt; nginx stores only its bcrypt hash. No password is baked into the image and no two instances share one.
  • A read-only status UI. The table pages show no compact, split or merge actions, and the runtime log level setter and the profiler are refused (HTTP 403) even when you are signed in.
  • Fail closed. nginx refuses to start until first boot has written the certificate and the password, so the UI is never served without a login.
  • A sandboxed, unprivileged HBase. HBase runs as its own hbase user with no capabilities, a read-only system and private temporary files, and it cannot reach the EC2 instance metadata endpoint (neither can nginx).
  • Your data on its own disk. Tables, the write-ahead log and ZooKeeper data live on a dedicated 20 GiB EBS volume mounted at /var/lib/hbase, which you can resize independently of the root volume.
  • Fixed host name and real client addresses. Requests for a host name the instance does not serve are dropped, nginx logs the real client address (client supplied X-Forwarded-For headers are overwritten), and cross site browser requests that change state are refused.
  • Verified download. The Apache release was checked against Apache's published SHA-512 checksum and its OpenPGP signature from the Apache HBase release manager's key. The image uses the Hadoop 3 build of HBase 2.5.16 (Hadoop 3.4.3 client libraries).
  • Key only SSH. Password logins, root logins and X11 forwarding are switched off.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: Apache HBase 2.5.16 (/opt/hbase, the Hadoop 3 build, Apache-2.0), Ubuntu's OpenJDK 11 runtime, 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.
  • A security group that allows TCP 22 (SSH) from your own networks and TCP 443 (the status UI) from the networks you will browse from. TCP 80 only redirects to HTTPS and is optional. Do not open 2181 or 16000 to 16030.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Apache HBase 2.5.16 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. The AMI brings its own 20 GiB data volume for HBase; increase its size on the launch page if you expect more data (Step 8).

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=hbase}]'

Step 2: Connect to your instance

Version SSH user
Apache HBase 2.5.16 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

Step 3: Check the services

First boot generates the certificate and the password, then nginx starts. HBase starts on its own and is ready about half a minute after boot:

systemctl is-active hbase nginx apache-hbase-firstboot
sudo test -f /var/lib/cloudimg/apache-hbase-firstboot.done && echo FIRSTBOOT_DONE
/opt/hbase/bin/hbase version 2>/dev/null | head -1
java -version 2>&1 | head -1
active
active
active
FIRSTBOOT_DONE
HBase 2.5.16-hadoop3
openjdk version "11.0.32.1" 2026-08-18

Only SSH and nginx listen on public addresses; every HBase port is on loopback:

sudo ss -Hlntp | awk '{print $4, $6}' | grep -v '127.0.0.5' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort
0.0.0.0:22 users:(("sshd"),("systemd"))
0.0.0.0:443 users:(("nginx"),("nginx"),("nginx"))
0.0.0.0:80 users:(("nginx"),("nginx"),("nginx"))
127.0.0.1:16000 users:(("java"))
127.0.0.1:16010 users:(("java"))
127.0.0.1:16020 users:(("java"))
127.0.0.1:16030 users:(("java"))
127.0.0.1:2181 users:(("java"))
[::]:22 users:(("sshd"),("systemd"))
[::]:443 users:(("nginx"),("nginx"),("nginx"))
[::]:80 users:(("nginx"),("nginx"),("nginx"))

Step 4: Use the HBase shell

The HBase shell is on every login's PATH, so after you connect you can type hbase shell for an interactive session. It talks to HBase over loopback, so it works for any user logged in to the instance. The example below creates a table with one column family, writes two rows, then reads them back with scan, get and count (the -n flag runs the commands non-interactively):

/opt/hbase/bin/hbase shell -n <<'EOF' 2>&1 | grep -v '^Took '
create 'guide_users', 'info'
put 'guide_users', 'user1', 'info:name', 'Ada Lovelace'
put 'guide_users', 'user1', 'info:city', 'London'
put 'guide_users', 'user2', 'info:name', 'Alan Turing'
put 'guide_users', 'user2', 'info:city', 'Manchester'
scan 'guide_users'
get 'guide_users', 'user2', 'info:city'
count 'guide_users'
EOF
hbase:001:0> create 'guide_users', 'info'
Created table guide_users
=> Hbase::Table - guide_users
hbase:002:0> put 'guide_users', 'user1', 'info:name', 'Ada Lovelace'
hbase:003:0> put 'guide_users', 'user1', 'info:city', 'London'
hbase:004:0> put 'guide_users', 'user2', 'info:name', 'Alan Turing'
hbase:005:0> put 'guide_users', 'user2', 'info:city', 'Manchester'
hbase:006:0> scan 'guide_users'
ROW  COLUMN+CELL
 user1 column=info:city, timestamp=2026-10-22T07:39:42.265, value=London
 user1 column=info:name, timestamp=2026-10-22T07:39:42.219, value=Ada Lovelace
 user2 column=info:city, timestamp=2026-10-22T07:39:42.339, value=Manchester
 user2 column=info:name, timestamp=2026-10-22T07:39:42.311, value=Alan Turing
2 row(s)
hbase:007:0> get 'guide_users', 'user2', 'info:city'
COLUMN  CELL
 info:city timestamp=2026-10-22T07:39:42.339, value=Manchester
1 row(s)
hbase:008:0> count 'guide_users'
2 row(s)
=> 2
hbase:009:0> 

Rows come back sorted by row key, and each cell shows its column (family:qualifier), its timestamp and its value. Leave the table in place for now to see it in the web UI (Step 6); remove it at the end of this guide.

Step 5: Get your URL and password

The URL and the user name are in the root only credentials file:

sudo grep -E '^HBASE_(UI_URL|ADMIN_USER)=' /root/hbase-credentials.txt
HBASE_UI_URL=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/master.jsp
HBASE_ADMIN_USER=admin

The password is the HBASE_ADMIN_PASSWORD line of the same file: sudo grep '^HBASE_ADMIN_PASSWORD=' /root/hbase-credentials.txt. Store it in your password manager; it is unique to this instance.

The URL uses the instance's public DNS name from EC2 instance metadata (IMDSv2), or its public or private IPv4 address when it has no public name. The certificate names that host plus the instance's public and private IP addresses:

openssl x509 -in /etc/ssl/hbase/hbase.crt -noout -subject -ext subjectAltName -enddate
subject=CN = ec2-<public-ip-dashed>.compute-1.amazonaws.com, O = cloudimg
X509v3 Subject Alternative Name: 
    DNS:ec2-<public-ip-dashed>.compute-1.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhost
notAfter=<date about 27 months after first boot>

Without the password every page is refused, a wrong password is refused, the log level setter is refused even with the password, and port 80 redirects to HTTPS. The commands below read the password from the credentials file into a shell variable and pass it to curl through a file descriptor, so it never appears in the process list or your shell history:

H=$(sudo sed -n 's#^HBASE_UI_URL=https://\([^/]*\)/.*#\1#p' /root/hbase-credentials.txt)
PW=$(sudo sed -n 's/^HBASE_ADMIN_PASSWORD=//p' /root/hbase-credentials.txt)
CA=/etc/ssl/hbase/hbase.crt
curl -s -o /dev/null -w 'http://  -> %{http_code} %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/master.jsp"
curl -s -o /dev/null -w 'no login -> HTTP %{http_code}\n' --cacert $CA --resolve "$H:443:127.0.0.1" "https://$H/master.jsp"
curl -s -o /dev/null -w 'wrong password -> HTTP %{http_code}\n' --cacert $CA --resolve "$H:443:127.0.0.1" -u admin:not-the-password "https://$H/master.jsp"
curl -s -o /dev/null -w 'admin + password -> HTTP %{http_code}\n' --cacert $CA --resolve "$H:443:127.0.0.1" -K <(printf 'user = "admin:%s"\n' "$PW") "https://$H/master.jsp"
curl -s -o /dev/null -w 'log level setter (signed in) -> HTTP %{http_code}\n' --cacert $CA --resolve "$H:443:127.0.0.1" -K <(printf 'user = "admin:%s"\n' "$PW") "https://$H/logLevel"
curl -s -w ' <- /healthz (no login) HTTP %{http_code}\n' --cacert $CA --resolve "$H:443:127.0.0.1" "https://$H/healthz"
http://  -> 301 https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/master.jsp
no login -> HTTP 401
wrong password -> HTTP 401
admin + password -> HTTP 200
log level setter (signed in) -> HTTP 403
ok
 <- /healthz (no login) HTTP 200

--resolve sends the request to this instance while keeping the real host name, so the certificate check passes. From another machine, drop --resolve and use the URL directly.

Step 6: Browse the Master status UI

Open the HBASE_UI_URL from Step 5 (https://<your-host>/master.jsp) in a browser. The certificate is self signed, so the browser warns once; accept it, or install your own certificate (Step 9). Sign in as admin with the password from the credentials file.

The home page shows the active Master, the RegionServer with its request rate and region count, and your tables:

The HBase 2.5.16 Master status page served by the cloudimg image, showing the active master localhost, the RegionServer localhost,16020 running 2.5.16-hadoop3 with three regions, the empty Backup Masters table and the start of the Tables section

Click a table name (or Table Details) to see its attributes and the settings of each column family: Bloom filter, versions kept, time to live, compression and block size. The page is read only on this image:

The HBase table page for guide_users, showing that the table is enabled and not compacting, and the schema of the info column family

The Debug Dump menu and the ZooKeeper page (/zk.jsp) show what HBase keeps in its embedded ZooKeeper: the active master, the server holding hbase:meta, the RegionServers and the client connections, all on localhost:

The HBase ZooKeeper dump page showing HBase rooted at /hbase, the active master and RegionServer on localhost, and ZooKeeper 3.8.6 running in standalone mode on localhost:2181

The Log Level and Profiler menu entries are refused on this image (HTTP 403), so nobody can change HBase's logging or start a profiler from a browser. The RegionServer's own status page (port 16030) is not published; reach it through an SSH tunnel (ssh -L 16030:127.0.0.1:16030 ...) if you need it.

Step 7: Connect applications

HBase in standalone mode is meant to be used from the instance itself: run your application, the HBase shell, or hbase tools here, and point the client at ZooKeeper on localhost:2181 (the default in /opt/hbase/conf/hbase-site.xml, so a client that reads that file needs no extra settings). The Java client needs the HBase client libraries (/opt/hbase/lib, or the hbase-shaded-client 2.5.16 artifact from Maven Central) and this configuration:

Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "localhost");
conf.set("hbase.zookeeper.property.clientPort", "2181");
try (Connection connection = ConnectionFactory.createConnection(conf);
     Table table = connection.getTable(TableName.valueOf("guide_users"))) {
    Result r = table.get(new Get(Bytes.toBytes("user1")));
    System.out.println(Bytes.toString(r.getValue(Bytes.toBytes("info"), Bytes.toBytes("name"))));
}

From a workstation, forward the three client ports over SSH and run the client against localhost (the Master and RegionServer advertise themselves as localhost, so the tunnel carries all the traffic):

ssh -i <your-key.pem> -N -L 2181:127.0.0.1:2181 -L 16000:127.0.0.1:16000 -L 16020:127.0.0.1:16020 ubuntu@<public-ip>

Never open 2181 or 16000 to 16030 in a security group: HBase has no authentication of its own, so anyone who can reach those ports can read, change and delete every table. For several application servers or a multi-node cluster, run HBase in distributed mode on HDFS with Kerberos authentication, as described in the Apache HBase reference guide.

Step 8: Storage, memory and logs

HBase keeps its data (hbase.rootdir), its write-ahead log and the ZooKeeper data on the dedicated EBS volume mounted at /var/lib/hbase, and runs with a 2 GB Java heap:

df -h /var/lib/hbase | tail -1 | awk '{print $2" total, "$4" free on "$6}'
grep -oE '<name>hbase\.(rootdir|wal\.dir|zookeeper\.property\.dataDir)</name><value>[^<]*' /opt/hbase/conf/hbase-site.xml | sed 's#</name><value># = #; s#<name>##'
grep -E '^export HBASE_HEAPSIZE' /opt/hbase/conf/hbase-env.sh
20G total, 19G free on /var/lib/hbase
hbase.rootdir = file:///var/lib/hbase/data
hbase.wal.dir = file:///var/lib/hbase/wal
hbase.zookeeper.property.dataDir = /var/lib/hbase/zookeeper
export HBASE_HEAPSIZE=2G
  • Heap. On a larger instance raise HBASE_HEAPSIZE in /opt/hbase/conf/hbase-env.sh (about half of the instance's memory is a good start), then sudo systemctl restart hbase.
  • Disk. To grow the volume, modify it in the EC2 console (Volumes, Modify volume) and extend the file system online: sudo resize2fs "$(findmnt -no SOURCE /var/lib/hbase)".
  • Backups. Take an HBase snapshot of a table with snapshot 'guide_users', 'guide_users-snap' in the shell, or stop HBase (sudo systemctl stop hbase) and take an EBS snapshot of the data volume.
  • Logs. HBase writes /var/log/hbase/hbase-standalone.log (rolled at 64 MB, ten files kept), also visible under Local Logs in the UI. journalctl -u apache-hbase-firstboot shows what first boot did.

Step 9: Use your own DNS name or certificate

The served host name is fixed by configuration, never taken from a request. To serve the UI under your own DNS name, point the name at the instance and run the helper, which records the name, regenerates the self signed certificate for it, updates the credentials file and reloads nginx:

sudo hbase-set-hostname <your-dns-name>

Run the same command after a stop and start if the instance's public DNS name changed (an Elastic IP avoids that), because a request for a name the instance does not serve is dropped.

To use a certificate from your own CA or a public CA instead, replace /etc/ssl/hbase/hbase.crt (full chain) and /etc/ssl/hbase/hbase.key (mode 0600, owner root), then run sudo nginx -t && sudo systemctl reload nginx.

Step 10: Security notes

HBase runs as its own unprivileged user with a read-only system and private temporary files, and neither HBase nor nginx can reach the instance metadata endpoint. In standalone mode the HMaster, the RegionServer and ZooKeeper share one process, so the block on the hbase service covers all three:

systemctl show hbase -p User -p NoNewPrivileges -p ProtectSystem -p PrivateTmp -p IPAddressDeny
systemctl show nginx -p IPAddressDeny
IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32
User=hbase
PrivateTmp=yes
ProtectSystem=strict
NoNewPrivileges=yes
IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32

SSH accepts keys only: passwords, root logins and X11 forwarding are switched off in /etc/ssh/sshd_config.d/00-cloudimg.conf, which sorts before every other SSH setting. Check the effective values:

sudo sshd -T | grep -E '^(passwordauthentication|kbdinteractiveauthentication|permitrootlogin|x11forwarding|pubkeyauthentication) '
permitrootlogin no
pubkeyauthentication yes
passwordauthentication no
kbdinteractiveauthentication no
x11forwarding no

Other points:

  • HBase itself has no users or passwords: anyone who can log in to the instance can use the HBase shell. Give SSH access only to people who may read and change every table.
  • nginx logs the real client address of every request; X-Forwarded-For headers sent by clients are ignored.
  • The AMI requires IMDSv2. Restrict port 443 to the networks you browse from.
  • To rotate the UI password, write a new bcrypt hash with sudo htpasswd -iB /etc/nginx/.hbase.htpasswd admin (it reads the new password from standard input), update /root/hbase-credentials.txt and reload nginx.
  • Bundled libraries. HBase 2.5.16 ships Netty 4.1.131, which a scanner reports as affected by CVE-2026-75595 (critical) and several high-severity Netty codec advisories. The critical issue is in Netty's SNI-based TLS routing, which HBase does not use; HBase's RPC runs without TLS and listens on localhost only, and the affected HTTP/2, SPDY, Redis, STOMP, HAProxy, XML and DNS codecs are not exposed. The same reasoning covers the high-severity Jetty 9.4.57 advisories (the status UI is reached only through nginx, and HBase uses no DIGEST authentication), the Jackson 2.21.1 polymorphic-typing advisories (HBase enables no default typing on untrusted JSON) and the lz4-java 1.11.1 temporary-file issue (HBase uses a private Java temporary directory on this image). 2.5.16 is the newest HBase 2.5 release; cloudimg updates the image when a newer 2.5 release ships.

When you have finished with the example table from Step 4, remove it (a table must be disabled before it can be dropped):

/opt/hbase/bin/hbase shell -n <<'EOF' 2>&1 | grep -v '^Took '
disable 'guide_users'
drop 'guide_users'
list
EOF
hbase:001:0> disable 'guide_users'
hbase:002:0> drop 'guide_users'
hbase:003:0> list
TABLE
0 row(s)
=> []
hbase:004:0> 

Support and licensing

cloudimg supports this image by email at support@cloudimg.co.uk. Include the instance ID, the version (Apache HBase 2.5.16 on Ubuntu 24.04) and what you were doing.

Apache HBase is open source software under the Apache License 2.0; the licence and notices are on the instance at /opt/hbase/LICENSE.txt and /opt/hbase/NOTICE.txt, and there is no per user or per CPU licence fee. The AMI price covers cloudimg's hardening, first boot setup and support.

Apache HBase, HBase, Apache Hadoop, Apache ZooKeeper and Apache are trademarks of the Apache Software Foundation. Ubuntu is a trademark of Canonical Ltd. Java is a trademark of Oracle and/or its affiliates. cloudimg is not affiliated with or endorsed by these organisations.