Deployment guide

Apache ZooKeeper

Overview

Apache ZooKeeper is a high-performance coordination service for distributed applications. It exposes a hierarchical key-value store of znodes used for distributed configuration, synchronisation, naming, leader election and service discovery. Apache Kafka, Apache Hadoop, Apache Solr and many other distributed systems use ZooKeeper as their coordination backplane.

This image runs Apache ZooKeeper 3.9 on OpenJDK 17 and bundles a web UI, so you can manage znodes from a browser as well as from the command line.

What you get:

  • Apache ZooKeeper 3.9 running as a standalone single-node ensemble on OpenJDK 17
  • Client port 2181 for your applications, with the admin server and the four-letter-word commands enabled for monitoring
  • ZooNavigator, an open-source znode browser web UI, running in a Docker container. nginx publishes it on port 80 behind HTTP basic authentication.
  • A Web UI password generated on the first boot of every deployed instance, so two instances launched from the same Amazon Machine Image never share a password. The credential is written to /root/zookeeper-credentials.txt with mode 0600, so only the root user can read it.

Prerequisites

Before you deploy this image you need:

  • An Amazon Web Services account where you can launch EC2 instances
  • IAM permissions to launch instances, create security groups, and subscribe to AWS Marketplace products
  • An EC2 key pair in the target Region for SSH access to the instance
  • A VPC and subnet in the target Region, with a security group allowing inbound port 22 from your management network, inbound port 80 from the networks that will reach the web UI, and inbound port 2181 from the application hosts that will use ZooKeeper as a coordination service
  • The AWS CLI (version 2) installed locally if you plan to deploy from the command line
1

Launch the Instance from the AWS Marketplace

Sign in to the AWS Management Console, open the EC2 service, and select Launch instance.

Under Application and OS Images, choose AWS Marketplace AMIs and search for Apache ZooKeeper. Select the SecureAnyCloud listing and choose Select, then choose Continue on the subscription summary.

Pick an instance type of m5.large or larger. Choose your EC2 key pair under Key pair (login). Under Network settings select your VPC and subnet, and either create or select a security group that allows inbound port 22 from your management network, inbound port 80 from the networks that reach the web UI, and inbound port 2181 from your application hosts. Leave the root volume at the default size or larger.

Select Launch instance. First boot initialisation takes approximately one minute after the instance state becomes Running and the status checks pass.

2

Launch the Instance from the AWS CLI

The following block launches an instance from the SecureAnyCloud Apache ZooKeeper Marketplace AMI into an existing subnet and security group. Replace <ami-id> with the AMI ID shown on the Marketplace listing. Replace <key-name> with your EC2 key pair name and <subnet-id> with your subnet ID. Replace <security-group-id> with a security group that opens ports 22, 80 and 2181 as described above.

aws ec2 run-instances \
  --image-id <ami-id> \
  --instance-type m5.large \
  --key-name <key-name> \
  --subnet-id <subnet-id> \
  --security-group-ids <security-group-id> \
  --metadata-options "HttpTokens=required,HttpEndpoint=enabled" \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=apache-zookeeper}]'

The image reads instance metadata using IMDSv2, so launching with HttpTokens=required is supported and recommended.

3

Connect to the Instance over SSH

Connect over SSH using the key pair you selected at launch. The login user depends on the operating system variant of the AMI you launched.

Connecting to your instance

  • Ubuntu 24.04: the SSH login user is ubuntu

For the Ubuntu 24.04 variant, run:

ssh -i /path/to/your-key.pem ubuntu@<instance-public-ip>
4

Retrieve the Per-Instance Credentials

On first boot the instance generates a fresh Web UI password and writes it, together with the connection details, to /root/zookeeper-credentials.txt. Read the file with sudo:

sudo cat /root/zookeeper-credentials.txt

The file looks like this:

# Apache ZooKeeper 3.9 — Per-Instance Credentials
# Generated: Fri May 22 12:12:14 UTC 2026 ZK_CONNECT=<instance-public-ip>:2181 ZK_ADMIN_USER=sac ZK_ADMIN_PASSWORD=<set-at-first-boot> ZK_AUTH_PROVIDER=digest ZOONAVIGATOR_URL=http://<instance-public-ip>/ ZOONAVIGATOR_USER=sac ZOONAVIGATOR_PASSWORD=<set-at-first-boot>

ZK_ADMIN_PASSWORD and ZOONAVIGATOR_PASSWORD are the same value. That single credential protects the ZooNavigator web UI login and the nginx authentication wall in front of it. The value shown above is from the build instance. Your deployed instance has its own unique password.

5

Verify the Services

The image runs three systemd units: zookeeper.service (the ZooKeeper server), zoonavigator.service (the ZooNavigator web UI container) and nginx.service (the reverse proxy and authentication wall). Confirm that all three are active:

systemctl is-active zookeeper.service zoonavigator.service nginx.service

All three report active:

active active active

Check which ports are listening. ZooKeeper serves clients on 2181. The ZooNavigator container listens on 9000, nginx publishes the web UI on 80, and the ZooKeeper admin server is bound to loopback on 8080:

ss -tlnp 2>/dev/null | grep -E ':2181|:80 |:8080|:9000' | awk '{print $1, $4}'
LISTEN 0.0.0.0:80 LISTEN *:9000 LISTEN *:2181 LISTEN [::ffff:127.0.0.1]:8080 LISTEN [::]:80
6

Explore the znode Tree with ZooNavigator

Open a web browser and go to http://<instance-public-ip>/. The nginx authentication wall prompts for credentials. Sign in as the SecureAnyCloud user with the ZOONAVIGATOR_PASSWORD from /root/zookeeper-credentials.txt.

ZooNavigator connects to the local ZooKeeper server automatically and shows the znode tree. At the root, you see the zookeeper system znode and a SecureAnyCloud parent znode. The image pre-creates that parent znode as a convenient starting point for your own application paths.

Select any znode to inspect it:

  • The Data tab shows the data payload stored at that path and lets you edit and save it.
  • The Meta tab shows the znode stat structure: the creation and modification transaction IDs and timestamps, the data and ACL versions, the number of child znodes and the ephemeral owner session.
  • The ACL tab shows the access control list applied to the znode and lets you add, edit or remove ACL entries. You can optionally apply changes recursively to all descendant znodes.
7

Use the Command-Line ZooKeeper Client

The bundled zkCli.sh client connects to the local ZooKeeper server for scripting and quick checks. Create a znode, read it back, and list the root:

printf 'create /myapp config-data\nget /myapp\nls /\nquit\n' | /opt/zookeeper/bin/zkCli.sh -server 127.0.0.1:2181 2>/dev/null | grep -E 'Created|config-data|^\['
[zk: 127.0.0.1:2181(CONNECTED) 1] config-data [zk: 127.0.0.1:2181(CONNECTED) 2] [sac, myapp, zookeeper]

Remove the znode again when you have finished:

printf 'delete /myapp\nquit\n' | /opt/zookeeper/bin/zkCli.sh -server 127.0.0.1:2181 2>/dev/null >/dev/null && echo "deleted /myapp"
8

Connect a Client Application

Point your distributed application's ZooKeeper client at the instance address on port 2181. This is the ZK_CONNECT value from the credentials file. From an application host inside your VPC, a connection string looks like <instance-private-ip>:2181. For clients outside the VPC where port 2181 is reachable, use <instance-public-ip>:2181.

ZooKeeper client libraries exist for Java, Python (kazoo), Go, Node.js and most other languages. A standalone single-node ensemble suits development, test, single-tenant production coordination and proof-of-concept workloads. For a highly available production ensemble, run an odd number of ZooKeeper nodes (three or five) and list every member in each node's zoo.cfg.

9

Monitor with the Four-Letter-Word Commands

ZooKeeper exposes lightweight diagnostic commands known as four-letter words. The image enables a safe whitelist of them. Send stat to see server health, connected clients and the standalone mode:

echo stat | nc -q 2 127.0.0.1 2181
Zookeeper version: 3.9.5-293c895a8d966a3ecb92872be4a1daf87d725da2, built on 2026-02-11 20:18 UTC Clients: /127.0.0.1:53904[0](queued=0,recved=1,sent=0) Latency min/avg/max: 0/1.7667/10 Received: 31 Sent: 30 Connections: 1 Outstanding: 0 Zxid: 0x13 Mode: standalone Node count: 6

Send mntr for a flat list of metrics suitable for scraping into a monitoring system:

echo mntr | nc -q 2 127.0.0.1 2181 | head -12
zk_version 3.9.5-293c895a8d966a3ecb92872be4a1daf87d725da2, built on 2026-02-11 20:18 UTC zk_server_state standalone zk_ephemerals_count 0 zk_num_alive_connections 1 zk_avg_latency 1.7667 zk_outstanding_requests 0 zk_znode_count 6 zk_global_sessions 0 zk_non_mtls_remote_conn_count 0 zk_last_client_response_size 16 zk_packets_sent 31 zk_packets_received 32

The ruok command is a simple health probe. The server replies imok when it is running:

echo ruok | nc -q 2 127.0.0.1 2181; echo
10

Protect znodes with ACLs

ZooKeeper applies access control lists per znode. By default, a newly created znode is world-readable and writable. To restrict a znode, register a digest identity and apply an ACL. The image registers the DigestAuthenticationProvider, so you can use the digest scheme. The following session, run inside zkCli.sh, creates a protected znode that only a digest principal can read and write:

[zk: 127.0.0.1:2181(CONNECTED) 0] addauth digest appuser:appsecret [zk: 127.0.0.1:2181(CONNECTED) 1] create /secure-config sensitive-value [zk: 127.0.0.1:2181(CONNECTED) 2] setAcl /secure-config digest:appuser:<generated-hash>:cdrwa [zk: 127.0.0.1:2181(CONNECTED) 3] getAcl /secure-config

Generate the digest hash with the bundled tool. The hash is the base64-encoded SHA1 of user:password:

java -cp /opt/zookeeper/lib/*:/opt/zookeeper/bin/../zookeeper-*.jar \
  org.apache.zookeeper.server.auth.DigestAuthenticationProvider appuser:appsecret

You can also review and edit ACLs visually in the ZooNavigator ACL tab described in Step 6.

11

Enable TLS on the Client Port (Optional)

By default, ZooKeeper serves clients on port 2181 without encryption. For production deployments where ZooKeeper traffic crosses untrusted networks, terminate TLS at an upstream Application Load Balancer, or enable ZooKeeper's own TLS listener.

To enable the native TLS listener, create a JKS keystore and truststore, then add a secure client port and the SSL properties to /etc/zookeeper/conf/zoo.cfg:

secureClientPort=2281 serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory ssl.keyStore.location=/etc/zookeeper/conf/zookeeper.keystore.jks ssl.keyStore.password=<keystore-password> ssl.trustStore.location=/etc/zookeeper/conf/zookeeper.truststore.jks ssl.trustStore.password=<truststore-password>

Restart zookeeper.service and update your security group to allow inbound port 2281. Clients then connect with TLS enabled and the truststore configured.

12

Back Up and Maintain

ZooKeeper writes point-in-time snapshots to /var/lib/zookeeper/data and a transaction log to /var/lib/zookeeper/log. The image enables automatic purging. autopurge.purgeInterval=24 keeps the five most recent snapshots and prunes the rest every 24 hours.

To take a manual backup, stop writes briefly or accept a slightly stale copy, then archive the data and log directories:

sudo tar czf /tmp/zookeeper-backup.tar.gz -C /var/lib/zookeeper data log && ls -la /tmp/zookeeper-backup.tar.gz

Apply operating system updates regularly with sudo apt-get update && sudo apt-get upgrade. After you update the kernel, reboot the instance during a maintenance window. The systemd units are enabled, so ZooKeeper, ZooNavigator and nginx all start automatically after a reboot.

Screenshots

The ZooNavigator znode-browser web UI showing the ZooKeeper znode tree, served behind an nginx authentication wall on first boot.

Inspecting and editing the data payload of a znode in the ZooNavigator Data tab.

The ZooNavigator Meta tab showing a znode's stat structure: versions, timestamps and child counts.

Reviewing and editing the access control list on a znode from the ZooNavigator ACL tab.

Support

This image is published by SecureAnyCloud with 24/7 technical support by email and chat. SecureAnyCloud can help with ZooKeeper deployment, ensemble configuration, ACL design, TLS setup, performance tuning and monitoring. Contact details are on the AWS Marketplace listing.

ZooNavigator is an open-source project (AGPL-3.0) by Lubos Kozmon and is bundled unmodified as the web UI.

All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.

Deploy on AWS

Launch Apache ZooKeeper on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS