Apache ZooKeeper 3.9
Overview
Apache ZooKeeper is the industry-standard distributed coordination service. It provides a service registry, leader election, distributed locks and a configuration store. It is also the backing store for systems like Kafka (legacy mode), HBase, NiFi, Druid and SolrCloud.
This image installs ZooKeeper 3.9.5 from the official Apache CDN with SHA-512 verification, alongside OpenJDK 17. It runs as a single-node standalone ensemble. Per-VM SecureAnyCloud credentials are generated at first boot. They are used for ZooKeeper's native digest ACL scheme and for the ZooNavigator 2.0.0 web UI, which is bundled in a Docker container behind an nginx HTTP basic auth wall on TCP 80.
What you get:
- Apache ZooKeeper 3.9.5 at
/opt/zookeeper - OpenJDK 17 JRE headless from Ubuntu 24.04 noble main
- Standalone single-node ensemble:
zookeeper.service(one JVM, with both server and client roles) - ZooKeeper client port 2181 (TCP) and admin server 127.0.0.1:8080 (loopback only)
- Four-letter-word (4LW) commands enabled (stat, ruok, conf, mntr, srvr and others)
- ZooNavigator 2.0.0 Docker container bound to 127.0.0.1:9000
- nginx reverse proxy on TCP 80 with HTTP basic auth. A single per-VM SecureAnyCloud credential covers nginx auth, ZooNavigator container auth and ZooKeeper digest ACLs.
- Heap tuned for Standard_B2s (256m to 512m JVM)
- Credentials at
/stage/scripts/zookeeper-credentials.log - 24/7 SecureAnyCloud support
Prerequisites
- An active Azure subscription, an SSH key, and a VNet and subnet.
- Standard_B2s (4 GB RAM) is suitable for dev, test, single-tenant production coordination, an embedded service registry and PoC workloads.
- NSG inbound rules that allow
22/tcpfrom your management CIDR,2181/tcpfrom your ZooKeeper client CIDR (the application tier), and80/tcpfrom your operator CIDR (ZooNavigator web UI).
The security model does not use ZooKeeper's sessionRequireClientSASLAuth=true, because Java 17+ has deprecated DIGEST-MD5 SASL. Instead, the image relies on three layers:
- NSG perimeter as the primary access control on
:2181 - ZooKeeper digest ACLs for per-znode authorization once a client has connected
- nginx HTTP basic auth on
:80for the ZooNavigator web UI
Restrict NSG :2181 inbound to your application tier IPs.
Step 1-3: Deploy and connect over SSH
Deploy the VM from the Azure Marketplace or the Azure CLI, then connect over SSH:
ssh azureuser@<vm-ip>Service status and version
Check the service and the ZooKeeper version:
sudo systemctl is-active zookeeper.service zoonavigator.service nginx.service /opt/zookeeper/bin/zkServer.sh version 2>&1 | tail -1Read per-VM credentials
Read the credentials file:
sudo cat /stage/scripts/zookeeper-credentials.logNote ZK_CONNECT, ZK_ADMIN_USER and ZK_ADMIN_PASSWORD. The same password is used for the ZooNavigator web UI basic auth.
4LW health check and auth roundtrip
Run a health check and an authenticated request:
echo "stat" | nc -q 2 127.0.0.1 2181 | head -10
PASS=$(sudo grep '^ZK_ADMIN_PASSWORD=' /stage/scripts/zookeeper-credentials.log | cut -d= -f2-) printf "addauth digest sac:%s\nls /\n" "$PASS" | /opt/zookeeper/bin/zkCli.sh -server 127.0.0.1:2181 2>&1 | tail -5The first call uses ZooKeeper's "stat" four-letter-word command to confirm Mode: standalone. The second call authenticates as SecureAnyCloud via digest ACL and lists the root znodes.
ZooNavigator web UI: connect
Browse to http://<vm-ip>/ and authenticate as SecureAnyCloud with the password from Step 5. ZooNavigator auto-connects to the local ZooKeeper ensemble and lands on the znode browser.
Create a znode from the UI
Click Create node in the ZooNavigator left pane. Name the node /myapp and set the mode to Persistent. The new znode appears in the tree.
Inspect a znode
Click any znode, for example the newly created /myapp, to see its data, ACL list, stat (czxid, mzxid, version and so on) and any children. The Edit tab lets operators set data inline.
Create and protect a znode from the CLI
Create a protected znode with the ZooKeeper CLI:
PASS=$(sudo grep '^ZK_ADMIN_PASSWORD=' /stage/scripts/zookeeper-credentials.log | cut -d= -f2-) printf "addauth digest sac:%s\ncreate /sac/data hello digest:sac:cdrwa\nls /sac\nget /sac/data\n" "$PASS" | /opt/zookeeper/bin/zkCli.sh -server 127.0.0.1:2181 2>&1 | tail -10This creates /sac/data with an ACL that grants the SecureAnyCloud digest user full CRUD permissions, then reads it back. For public-readable znodes, use the ACL world:anyone:r instead.
Tune the heap
Edit /etc/zookeeper/conf/java.env and restart the service:
sudo systemctl restart zookeeper.serviceDefaults on Standard_B2s:
- JVMFLAGS: -Xms256m -Xmx512m
- Snapshot retention: 5 snapshots, autopurge every 24 hours
- Data dir:
/var/lib/zookeeper/data - Transaction log dir:
/var/lib/zookeeper/log
For production with a busy ensemble, raise -Xmx to 1024m–2048m and place transaction logs on a separate fast disk (dataLogDir in /etc/zookeeper/conf/zoo.cfg).
Add a second ZooNavigator user
Replace <new-password> with the operator's chosen password:
sudo htpasswd -bB /etc/nginx/zoonavigator.htpasswd alice <new-password>
sudo systemctl reload nginxEach operator authenticates with their own basic-auth credential against the same nginx vhost. The underlying ZooKeeper digest ACL is still SecureAnyCloud.
Add a second ZooKeeper digest user
Generate the digest hash and create a znode owned by the second user:
PASS=$(sudo grep '^ZK_ADMIN_PASSWORD=' /stage/scripts/zookeeper-credentials.log | cut -d= -f2-) NEWUSER=alice NEWPASS=<new-password>
HASH=$(echo -n "$NEWUSER:$NEWPASS" | openssl dgst -binary -sha1 | base64) printf "addauth digest sac:%s\nsetAcl / digest:sac:cdrwa,digest:%s:%s:cdrwa\nls /\n" "$PASS" "$NEWUSER" "$HASH" | /opt/zookeeper/bin/zkCli.sh -server 127.0.0.1:2181 2>&1 | tail -5Backups
ZooKeeper writes snapshots to /var/lib/zookeeper/data/version-2 and transaction logs to /var/lib/zookeeper/log/version-2. Take a consistent backup while the service is running by copying the latest snapshot and all transaction logs since:
sudo tar czf /var/backups/zookeeper-$(date +%F).tgz -C /var/lib zookeeperPeriodically copy /var/backups to Azure Blob Storage (az storage blob upload-batch) for off-VM retention.
To restore, stop ZooKeeper, replace /var/lib/zookeeper and restart the service.
Logs and troubleshooting
Use these commands to inspect the logs:
sudo journalctl -u zookeeper.service --no-pager -n 80
sudo journalctl -u zoonavigator.service --no-pager -n 30
sudo journalctl -u nginx.service --no-pager -n 30
sudo ls /var/lib/zookeeper/log/ZooKeeper writes its main log to /var/lib/zookeeper/log/zookeeper-zookeeper-server-*.out and its data log to /var/lib/zookeeper/log/version-2/. ZooNavigator's stdout goes to the systemd journal. The nginx access log is at /var/log/nginx/access.log.
Security
- ZooKeeper client port
:2181is not auth-walled at the network layer, because SASL DIGEST-MD5 is deprecated in Java 17+. Restrict NSG inbound to your application tier IPs. - ZooKeeper digest ACLs gate per-znode access for connected clients. Use them to isolate workloads.
- The admin server is bound to
127.0.0.1:8080(loopback only) and is not exposed to the network. - The ZooNavigator web UI is bound to
127.0.0.1:9000and is not exposed directly. - The nginx auth wall on
:80enforces HTTP basic authentication for the ZooNavigator web UI. - One per-VM SecureAnyCloud password is used for nginx, the ZooNavigator container and the default znode digest ACL, so a single rotation covers all three.
- Terminate TLS at an upstream Application Gateway or Front Door, or enable ZooKeeper's own SSL listener (zoo.cfg
secureClientPort=2281).
Licensing: ZooNavigator (AGPL-3.0)
Apache ZooKeeper itself is Apache License 2.0 (permissive). The ZooNavigator web UI bundled in this image is AGPL-3.0-or-later, which has a network-copyleft clause. If you modify ZooNavigator and run the modified version on a network where users can interact with it, you must offer those users the source code of your modified version.
Unmodified deployments satisfy AGPL section 13, because the upstream source is publicly available. This is the SecureAnyCloud default: we ship elkozmon/zoonavigator:2.0.0 from Docker Hub without modification. The upstream locations are:
- Source: https://github.com/elkozmon/zoonavigator
- Image: https://hub.docker.com/r/elkozmon/zoonavigator
- License: https://github.com/elkozmon/zoonavigator/blob/master/LICENSE.txt
If you modify the ZooNavigator container in your own deployment, for example with custom theming or additional plugins, AGPL section 13 requires you to provide your modified source to your users.
The SecureAnyCloud packaging, support and CLI scripts around ZooNavigator are not modifications to ZooNavigator and remain under SecureAnyCloud's commercial terms.
If you prefer a strictly Apache-licensed image without ZooNavigator, contact sales@secureanycloud.com and we will build a CLI-only ZooKeeper variant.
Support
SecureAnyCloud provides 24/7/365 expert technical support, with a guaranteed response within 24 hours and a one-hour average for critical issues. Contact sales@secureanycloud.com.
Deploy on Azure
Launch Apache ZooKeeper 3.9 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com