Application Servers AWS

Apache ZooKeeper on AWS User Guide

| Product: Apache ZooKeeper on AWS

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 as a standalone single node ensemble on OpenJDK 17. The client port 2181 serves applications, and the admin server plus the four letter word commands are enabled for monitoring. Because ZooKeeper itself is headless, the image also bundles ZooNavigator, an open source znode browser web UI, so you have a visual management surface. ZooNavigator runs in a Docker container and nginx publishes it on port 80 behind HTTP basic authentication.

The Web UI password is generated on the first boot of every deployed instance. 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 that 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

Step 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 cloudimg listing and choose Select, then 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.

Step 2: Launch the Instance from the AWS CLI

The following block launches an instance from the cloudimg Apache ZooKeeper Marketplace AMI into an existing subnet and security group. Replace <ami-id> with the AMI ID shown on the Marketplace listing, <key-name> with your EC2 key pair name, <subnet-id> with your subnet ID, and <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.

Step 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

OS variant SSH login user
Ubuntu 24.04 ubuntu

For the Ubuntu 24.04 variant:

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

Step 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=cloudimg
ZK_ADMIN_PASSWORD=ff06c45be245de8afc1e85e7f16d9a93
ZK_AUTH_PROVIDER=digest
ZOONAVIGATOR_URL=http://<instance-public-ip>/
ZOONAVIGATOR_USER=cloudimg
ZOONAVIGATOR_PASSWORD=ff06c45be245de8afc1e85e7f16d9a93

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.

Step 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 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

Step 6: Explore the znode Tree with ZooNavigator

Open a web browser and navigate to http://<instance-public-ip>/. The nginx authentication wall prompts for credentials — sign in as the cloudimg 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 cloudimg parent znode that the image pre creates as a convenient starting point for your own application paths.

ZooNavigator showing the ZooKeeper znode tree at the root

Select any znode to inspect it. The Data tab shows the data payload stored at that path and lets you edit and save it.

Browsing the data payload of a znode in the ZooNavigator Data tab

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 ZooNavigator Meta tab showing a znode stat structure

The ACL tab shows the access control list applied to the znode and lets you add, edit or remove ACL entries, optionally applying changes recursively to all descendant znodes.

Reviewing and editing a znode access control list in the ZooNavigator ACL tab

Step 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] [cloudimg, 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"

Step 8: Connect a Client Application

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

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.

Step 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

Step 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 readable and writable only by a digest principal:

[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 SHA1 of user:password, base64 encoded:

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 shown in Step 6.

Step 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.

Step 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 updating 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

ZooNavigator znode tree

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

Browsing znode data

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

znode metadata

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

znode ACL editor

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


Support

This image is published by cloudimg with 24/7 technical support by email and chat. cloudimg 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 Ľuboš 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.