AKHQ
Overview
AKHQ is an open source web UI for Apache Kafka. From one browser tab you can browse topics and their configuration, read and search messages with a live tail, and produce test messages. You can also watch consumer groups and their lag, reset offsets, inspect brokers and their configuration, and manage schema registry subjects, Kafka Connect connectors, ksqlDB and ACLs. AKHQ talks to any Kafka cluster it can reach: Azure Event Hubs (Kafka endpoint), Confluent Cloud, Redpanda or your own brokers.
This guide covers deploying and using AKHQ for Apache Kafka on Ubuntu 24.04 LTS from SecureAnyCloud on Microsoft Azure. The image adds a secured, ready-to-use setup around AKHQ.
What you get:
- No default login, no shared secret. The admin password and the secret that signs AKHQ's login sessions are generated on your VM at first boot. They are written to
/etc/akhq/secrets.yml(root and theakhqgroup only) and/root/akhq-credentials.txt(root only). The image ships neither file, and AKHQ's built-in default session secret is never used. - Login required for everything. AKHQ security is switched on. An anonymous visitor gets no role at all, HTTP Basic authentication is off, and the only way in is the login form, which nginx rate limits per client address.
- HTTPS from the first boot. nginx serves AKHQ on port 443 with a certificate generated on your VM. Port 80 redirects to HTTPS, and requests for host names the VM does not serve are dropped. nginx refuses a request that carries another site's
Origin, every WebSocket upgrade (AKHQ uses Server-Sent Events for its live tail), and any cross-site link to a page that changes state, such as signing out, restarting, pausing or resuming a Kafka Connect connector. - AKHQ and Kafka on localhost only. AKHQ listens on
127.0.0.1:8080and its health endpoint on127.0.0.1:28081. Its service refuses to stay up if it ever finds itself on another address. The bundled demo broker listens on127.0.0.1:9092. - Fail closed. Kafka, AKHQ and nginx start only after the boot service has written your secrets. Before AKHQ starts, a configuration check refuses to run it with a missing or default secret, with HTTP Basic or anonymous access switched on, or on a public address.
- Hardened defaults. AKHQ, Kafka, nginx and the boot service are blocked from the Azure Instance Metadata Service and the Azure WireServer. AKHQ connects to the addresses you configure, so a mistake or a malicious setting can never reach the VM's metadata or a managed identity token. nginx hands AKHQ the real client address and the configured host name, never values a client sends. Both Java services run under systemd sandboxing as unprivileged users. The Azure agent keeps its own access.
- Ready to explore. A single-node Apache Kafka 4.4.0 broker (KRaft) is preloaded with three demo topics, sample messages and a consumer group with lag, so every AKHQ screen shows real data on first login. Point AKHQ at your own cluster when you are ready (Step 7).
- Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
- On the image: AKHQ 0.28.0 (the upstream release jar, verified against its published sha256 digest) on Eclipse Temurin 25.0.5 JRE, Apache Kafka 4.4.0 on OpenJDK 21 (from Ubuntu 24.04), nginx 1.24 (from Ubuntu 24.04), cloud-init and the Azure Linux agent.
- Default user:
azureuser - Architecture: x64 (Gen2)
- Recommended VM size: Standard_B2s (2 vCPU, 4 GiB)
Prerequisites
- An Azure subscription, an SSH key pair, and a virtual network and subnet in the target region.
- A network security group that allows TCP 22 (SSH) from your admin network and TCP 443 (the AKHQ web UI) from the networks your users browse from. Also allow TCP 80 if you want plain HTTP requests redirected to HTTPS.
- No rules for ports 8080, 28081, 9092 or 9093. These ports listen on the loopback address only, so never open them.
- To manage your own Kafka cluster, network access from the VM to your brokers. For Azure Event Hubs, this means outbound TCP 9093 to
<namespace>.servicebus.windows.net.
Deploy the VM
In the Azure portal, open Create a resource, search for AKHQ for Apache Kafka on Ubuntu 24.04 LTS by SecureAnyCloud, and select Create. On the Basics tab, choose Standard_B2s and SSH public key authentication with azureuser as the user name. On the Networking tab, allow ports 22, 80 and 443 as described above.
To deploy with the Azure CLI instead, replace the values in angle brackets. The image URN is shown on the offer's Usage information + support tab:
az vm create --resource-group <resource-group> --name akhq --image <image-urn-from-the-offer> \
--size Standard_B2s --admin-username azureuser --ssh-key-values ~/.ssh/id_rsa.pub \
--public-ip-sku Standard --nsg-rule SSH
az network nsg rule create --resource-group <resource-group> --nsg-name akhqNSG --name akhq-https \
--priority 1010 --access Allow --protocol Tcp --destination-port-ranges 80 443 --source-address-prefixes <your-users-cidr>First boot takes about a minute after the VM is running. The boot service generates the secrets and the certificate and formats the demo broker's storage. Then Kafka loads the demo data and AKHQ starts.
Connect to your VM
ssh azureuser@<public-ip>Check the services
systemctl is-active akhq-address akhq-firstboot kafka akhq nginx
sudo test -f /var/lib/sac/data/akhq-firstboot.done && echo FIRSTBOOT_DONEAll five units report active, and the first-boot marker is present:
active active active active active FIRSTBOOT_DONEOnly SSH and nginx listen on network ports. AKHQ (8080, 28081) and Kafka (9092, 9093) listen on the loopback address:
sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v '127.0.0.5' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort0.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")) [::]:22 users:(("sshd"),("systemd")) [::]:443 users:(("nginx"),("nginx"),("nginx")) [::]:80 users:(("nginx"),("nginx"),("nginx")) [::ffff:127.0.0.1]:28081 users:(("java")) [::ffff:127.0.0.1]:8080 users:(("java")) [::ffff:127.0.0.1]:9092 users:(("java")) [::ffff:127.0.0.1]:9093 users:(("java"))Get your URL and admin password
sudo cat /root/akhq-credentials.txtThe file is readable by root only. It looks like this:
# AKHQ for Apache Kafka 0.28.0 on Ubuntu 24.04 LTS - generated on first boot by akhq-firstboot.service.
# The admin password below is unique to this VM; none was baked into the image. Keep this file secret.
# Open AKHQ_URL in a browser and log in as AKHQ_ADMIN_USER. The certificate is self signed (minted on this
# VM); install your own in /etc/nginx/ssl. Change the served name:
sudo akhq-set-hostname <dns-name> AKHQ_URL=https://<public-ip>/ AKHQ_ADMIN_USER=admin AKHQ_ADMIN_PASSWORD=<AKHQ_ADMIN_PASSWORD>
# Re-verify the appliance:
sudo akhq-selfcheck Connect your own Kafka cluster: /etc/akhq/application.yml
# User guide: https://secureanycloud.com/stacks/akhq/The URL uses the VM's public IP address as the Azure Instance Metadata Service reports it, or the private address when the VM has no public IP. The certificate also names the private IP address.
If the public IP changes, for example a dynamic address after the VM is deallocated, the boot service picks up the new address at the next boot and issues a new certificate for it. To serve AKHQ under your own DNS name, see Step 9.
The AKHQ web UI
Open https://<public-ip>/ in your browser, accept the self-signed certificate and log in as admin with AKHQ_ADMIN_PASSWORD.
The cluster named SecureAnyCloud is the preloaded demo broker. The Topics view lists every topic with its message count, size, partitions and the consumer groups reading it.
Open a topic to read its messages. AKHQ shows the key, timestamp, partition, offset and decoded value. You can sort, filter by partition or time, search, download results, produce a message or start a Live Tail:
Consumer Groups shows each group's state, coordinator, members and lag per topic. The demo group demo-consumer has read the first 20 orders and lags behind the 8 produced after it:
Nodes lists the brokers with their partition share. Open a node to see its full configuration:
Use the AKHQ API
Everything in the UI is also available as a JSON API under /api. Without a session, the API answers 401:
curl -sk -m 10 -o /dev/null -w '%{http_code}\n' -H 'Accept: application/json' https://127.0.0.1/api/sac/topic401Log in through the same form the browser uses, keep the session cookie, and call the API. On the VM, the commands use https://127.0.0.1/, because the VM's own names are all served. From elsewhere, use your AKHQ_URL. The password goes to curl on standard input, never on the command line, and the cookie jar is removed at the end:
AKHQ_URL=https://127.0.0.1/
JAR=$(mktemp)
sudo sed -n 's/^AKHQ_ADMIN_PASSWORD=//p' /root/akhq-credentials.txt | tr -d '\n' \
| curl -sk -m 20 -c "$JAR" -o /dev/null -w '%{http_code} %{redirect_url}\n' --data-urlencode username=admin --data-urlencode password@- "${AKHQ_URL}login" curl -sk -m 20 -b "$JAR" -H 'Accept: application/json' "${AKHQ_URL}api/sac/topic" | jq -r '.results[] | "\(.name) partitions=\(.partitions | length)"' curl -sk -m 20 -b "$JAR" -H 'Accept: application/json' "${AKHQ_URL}api/sac/group" | jq -c '.results[] | {id, state}' curl -sk -m 20 -b "$JAR" -H 'Accept: application/json' "${AKHQ_URL}api/sac/topic/demo-orders/data" | jq -r '.results[:3][] | "\(.key) \(.value)"' rm -f "$JAR"A successful login answers 303 back to the UI with the session cookie set. A wrong password answers 303 to the login page with no cookie:
303 https://127.0.0.1/ui demo-orders partitions=3 demo-payments partitions=1 demo-users partitions=1 {"id":"demo-consumer","state":"Empty"} order-001 {"orderId":1001,"customer":"cust-02","item":"SKU-0037","qty":2,"amount":22.99,"status":"PLACED"} order-006 {"orderId":1006,"customer":"cust-07","item":"SKU-0222","qty":2,"amount":87.99,"status":"PLACED"} order-002 {"orderId":1002,"customer":"cust-03","item":"SKU-0074","qty":3,"amount":35.99,"status":"PLACED"}Sessions last one hour, so log in again after that.
Some API routes change state with a GET: /logout and the Kafka Connect restart, pause, resume and task restart routes. A script that calls them must send -H 'Sec-Fetch-Site: same-origin'. nginx refuses these routes to any request that does not prove it comes from the AKHQ page itself (Step 11).
Connect your own Kafka cluster
AKHQ reads its clusters from /etc/akhq/application.yml, which is owned by root and readable by the akhq group. Open it with sudo nano /etc/akhq/application.yml and add a connection under akhq.connections, next to the demo SecureAnyCloud entry.
For Azure Event Hubs, use the Standard tier or above with the Kafka endpoint on port 9093. The password is the namespace's connection string from Shared access policies:
akhq: connections: sac: properties: bootstrap.servers: "127.0.0.1:9092" my-event-hubs: properties: bootstrap.servers: "<namespace>.servicebus.windows.net:9093" security.protocol: SASL_SSL sasl.mechanism: PLAIN sasl.jaas.config: org.apache.kafka.common.security.plain.PlainLoginModule required username="$ConnectionString" password="<connection-string>";For SASL/SCRAM on your own brokers or Confluent Platform:
my-cluster: properties: bootstrap.servers: "broker-1.example.internal:9096,broker-2.example.internal:9096" security.protocol: SASL_SSL sasl.mechanism: SCRAM-SHA-512 sasl.jaas.config: org.apache.kafka.common.security.scram.ScramLoginModule required username="<kafka-user>" password="<kafka-password>";For mutual TLS, copy the keystore and truststore into /etc/akhq/ with owner root:akhq and mode 0640. Then use security.protocol: SSL with ssl.truststore.location, ssl.keystore.location and their passwords.
A schema registry or a Kafka Connect cluster joins the same connection, using schema-registry: {url: ...} and connect: [{name: ..., url: ...}]. See the AKHQ configuration documentation for details. Then restart AKHQ and watch it come up:
sudo systemctl restart akhq && sudo journalctl -u akhq -n 20 --no-pagerKeep these points in mind:
- Logins that need the VM's managed identity are not available from this image. Azure AD (OAuth) authentication to Event Hubs from the VM needs a token from the Azure Instance Metadata Service. This image blocks that service for AKHQ on purpose, because the block keeps a malicious or mistaken connection setting from reaching the VM's identity. Use the connection string (SAS) login above, SASL/SCRAM or mutual TLS instead.
- Keep
micronaut.server.hostat127.0.0.1and do not enablemicronaut.server.ssl. AKHQ's configuration check refuses to start otherwise, and nginx already provides HTTPS. /etc/akhq/application.ymlholds your cluster passwords. It is not world-readable, and it is the file to back up (Step 13).
The demo broker
The demo broker is a single-node Apache Kafka 4.4.0 (KRaft) on 127.0.0.1:9092, with its data in /var/lib/kafka/data and its settings in /etc/kafka/server.properties. Every VM formats its own fresh cluster at first boot. Kafka's command-line tools are in /opt/kafka/bin:
LOG_DIR=/tmp/kafka-cli KAFKA_JMX_OPTS=-Dcli=1 /opt/kafka/bin/kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --list LOG_DIR=/tmp/kafka-cli KAFKA_JMX_OPTS=-Dcli=1 /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server 127.0.0.1:9092 --describe --group demo-consumer__consumer_offsets demo-orders demo-payments demo-users Consumer group 'demo-consumer' has no active members. GROUP TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG CONSUMER-ID HOST CLIENT-ID demo-consumer demo-orders 0 6 9 3 - - - demo-consumer demo-orders 1 6 9 3 - - - demo-consumer demo-orders 2 8 10 2 - - -The broker accepts connections from this VM only and has no authentication, so it is a sandbox, not a production cluster. Delete the demo-* topics and the demo-consumer group whenever you like.
Once AKHQ points at your own cluster, you can remove the SecureAnyCloud connection from /etc/akhq/application.yml and switch the broker off. AKHQ does not depend on it:
sudo systemctl disable --now kafka && sudo systemctl restart akhqYour own DNS name and certificate
Point a DNS record at the VM, for example an Azure DNS A record or the public IP's own DNS name label <label>.<region>.cloudapp.azure.com. Then tell AKHQ to serve that name. The command updates nginx, issues a new self-signed certificate that names it, accepts https://<name> as an allowed origin and updates the URL in the credentials file:
sudo akhq-set-hostname akhq.<your-domain>The name is saved in /etc/sac/akhq-hostname and survives reboots.
To use a certificate from your own CA or from Let's Encrypt, replace /etc/nginx/ssl/akhq.crt and /etc/nginx/ssl/akhq.key, keeping the key at mode 0600. Then run sudo systemctl reload nginx.
Users, passwords and the session secret
/etc/akhq/secrets.yml holds the session-signing secret and the users. Passwords are stored as SHA-256 hashes (passwordHash: SHA256), and AKHQ also accepts BCRYPT.
Groups map users to AKHQ roles. The admin group has full access. For a read-only user, add a reader group to akhq.security.groups in application.yml (see the AKHQ security documentation) and give the user that group.
To change the admin password, run the block below. It reads the new password without echoing it, writes its hash into secrets.yml and writes the password into the credentials file through standard input, never the command line. It then restarts AKHQ:
read -rsp 'New AKHQ admin password: ' P; echo printf '%s' "$P" | sudo python3 -I -c ' import hashlib, re, sys p = sys.stdin.read() h = hashlib.sha256(p.encode()).hexdigest() s = open("/etc/akhq/secrets.yml").read() s = re.sub(r"(password: \")[0-9a-f]{64}(\")", lambda m: m.group(1) + h + m.group(2), s, count=1) open("/etc/akhq/secrets.yml", "w").write(s) c = open("/root/akhq-credentials.txt").read() c = re.sub(r"(?m)^AKHQ_ADMIN_PASSWORD=.*$", lambda m: "AKHQ_ADMIN_PASSWORD=" + p, c) open("/root/akhq-credentials.txt", "w").write(c) ' unset P;
sudo systemctl restart akhqTo rotate the session-signing secret, run the commands below. Every user is logged out:
sudo python3 -I -c ' import re, secrets s = open("/etc/akhq/secrets.yml").read() s = re.sub(r"(secret: \")[0-9a-f]{64}(\")", lambda m: m.group(1) + secrets.token_hex(32) + m.group(2), s, count=1) open("/etc/akhq/secrets.yml", "w").write(s) ' && sudo systemctl restart akhqThe secret must stay 64 hexadecimal characters. AKHQ's configuration check refuses anything else, including AKHQ's well-known built-in default.
Security checks
akhq-selfcheck re-proves the whole appliance with this VM's credentials. It checks HTTPS, the refusal of anonymous and HTTP Basic requests, and the login with the right and a wrong password. It also checks the topic and consumer group APIs, and that the session token is signed with this VM's own secret while a token signed with AKHQ's built-in default secret is refused. Finally, it checks the redirect, the refusal of other host names, cross-origin requests, WebSocket upgrades and a cross-site sign-out link, the loopback-only ports, and the metadata block inside every service:
sudo akhq-selfcheckAKHQ appliance self-check (https://<public-ip>/) [ok] HTTPS AKHQ login page -> 200 [ok] topic API without a session -> 401; HTTP Basic (even with the right password) -> 401 [ok] wrong password -> 303 back to the login page, no session cookie [ok] admin login with the per-instance password -> session; topic API -> 200 and lists demo-orders [ok] consumer-group API -> 200 and lists demo-consumer [ok] session token is HS256-signed with this instance's own secret (re-signed copy -> 200) [ok] tokens signed with AKHQ's built-in default secret or a random secret -> 401 [ok] port 80 -> 301 https://<public-ip>/; unknown Host -> 444; CONNECT refused; cross-origin POST -> 403; WebSocket upgrade -> 403; cross-site GET /logout -> 403 [ok] AKHQ (8080/28081) and Kafka (9092/9093) refuse the private address; AKHQ speaks no TLS (no SNI) itself [ok] Azure IMDS and the WireServer refused inside the akhq, kafka and nginx cgroups (loopback control 200) SELFCHECK OK - HTTPS, per-VM login and session secret, origin and cross-site guards, loopback-only AKHQ and Kafka, metadata block verified.The self-check reads the demo broker's topics, so once you have switched the broker off (Step 8), it stops at that check.
The metadata block is a systemd IPAddressDeny rule on each AKHQ service. It covers the Azure Instance Metadata Service (169.254.169.254) and the Azure WireServer (168.63.129.16). Two components keep their access: akhq-address, which only reads this VM's IP addresses from the metadata service before anything else starts, and the Azure agent:
for u in akhq kafka nginx akhq-firstboot akhq-address walinuxagent; do printf '%s: ' $u; systemctl show -p IPAddressDeny --value $u.service | tr ' ' '\n' | sort | tr '\n' ' '; echo; doneakhq: 168.63.129.16/32 169.254.169.254/32 ::ffff:168.63.129.16/128 ::ffff:169.254.169.254/128 fd00:ec2::254/128 kafka: 168.63.129.16/32 169.254.169.254/32 ::ffff:168.63.129.16/128 ::ffff:169.254.169.254/128 fd00:ec2::254/128 nginx: 168.63.129.16/32 169.254.169.254/32 ::ffff:168.63.129.16/128 ::ffff:169.254.169.254/128 fd00:ec2::254/128 akhq-firstboot: 168.63.129.16/32 169.254.169.254/32 ::ffff:168.63.129.16/128 ::ffff:169.254.169.254/128 fd00:ec2::254/128 akhq-address: walinuxagent:The cross-site guard in nginx refuses the routes that change state with a GET, unless the browser proves the request comes from the AKHQ page itself. The proof is Sec-Fetch-Site: same-origin, or an Origin / Referer naming this VM. A link on another site, or a request with no such proof, gets 403:
curl -sk -m 10 -o /dev/null -w '%{http_code}\n' -H 'Sec-Fetch-Site: cross-site' https://127.0.0.1/logout403Versions, provenance and file locations
sed -n '1,6p' /usr/share/doc/akhq/BUILD-PROVENANCE.txt /opt/akhq/jre/bin/java -version 2>&1 | sed -n 1p ls /opt/kafka/libs/kafka-clients-*.jarAKHQ 0.28.0 - official upstream release jar (not rebuilt) source https://github.com/tchiotludo/akhq/releases/download/0.28.0/akhq-0.28.0-all.jar sha256 bb3c5f8ff52e67fa8b3324a6597664faa467dbe3b60c053e6d97304a6079b5ca (GitHub release asset digest) licence Apache License 2.0 (LICENSE from the 0.28.0 tag, sha256 55b6cb57c1485b07a544fec6cce494e916682f36977e8dbb1620de91d6eeca86) runtime Eclipse Temurin 25.0.5+8 JRE (6fac98f0c8202efc3135a9e9e42aa61963ec42d1c5cbb40caf231e7b14a24ef6), /opt/akhq/jre, used by AKHQ only netty-handler 4.2.13.Final (inside the jar) openjdk version "25.0.5" 2026-10-20 LTS /opt/kafka/libs/kafka-clients-4.4.0.jarKnown scanner finding. Vulnerability scanners report CVE-2026-75595 (GHSA-c4c3-7fpv-j4q5, a server name indication routing bypass in netty-handler) against the AKHQ 0.28.0 jar, which bundles netty-handler 4.2.13. The flaw cannot be reached on this image. It lies in netty's TLS server name handling, and AKHQ here has no TLS listener at all.
AKHQ serves plain HTTP on 127.0.0.1 only, and nginx terminates TLS in front of it. AKHQ's configuration check refuses to start if TLS is ever switched on inside AKHQ. The fix is merged upstream (AKHQ pull request #3351) and arrives with the next AKHQ release, which SecureAnyCloud will publish as a new image version.
Path What it is
/etc/akhq/application.yml AKHQ settings and your cluster connections
/etc/akhq/secrets.yml session-signing secret and users (written at first boot)
/root/akhq-credentials.txt URL and admin password (root only)
/etc/kafka/server.properties, /var/lib/kafka/data demo broker settings and data
/etc/nginx/sites-available/sac-akhq the HTTPS front door; certificate and key in /etc/nginx/ssl/
/run/sac-akhq/address.env this VM's public and private IP addresses, read from Azure IMDS at each boot
/usr/share/doc/akhq/ LICENSE and BUILD-PROVENANCE.txt
journalctl -u akhq AKHQ's log (also kafka, nginx, akhq-firstboot, akhq-address)
Backup and maintenance
- Backups. AKHQ keeps no data of its own. Back up
/etc/akhq(your connections, users and secret),/etc/sacand/etc/nginx/ssl. The demo broker's data is disposable. Azure Backup of the VM's OS disk covers all of them. - Updates. Ubuntu security updates install automatically (unattended-upgrades), including the OpenJDK runtime the demo broker uses. AKHQ and its Java runtime live in
/opt/akhq. To upgrade, deploy the newer image version and copy/etc/akhqacross. - Memory. AKHQ and the demo broker each run with a bounded Java heap (768 MB and 1 GB), which fits a Standard_B2s. For large clusters with many topics, give AKHQ more heap in
/etc/default/akhq(JAVA_OPTS) and use a larger VM size.
Security recommendations
- Limit TCP 443 in the network security group to the networks your users browse from, and TCP 22 to your admin network. AKHQ can change and delete topics, offsets and data on the clusters it manages. Treat it as an administrative console, ideally reachable only over a VPN, Azure Bastion or a private endpoint.
- Serve AKHQ under your own DNS name with a certificate from a trusted CA (Step 9).
- Change the admin password if you share the credentials file. Give other people their own users, and use read-only groups where you can (Step 10).
- Connect to your clusters over TLS with a dedicated Kafka user whose ACLs match what AKHQ users should be able to do (Step 7).
- Keep AKHQ on
127.0.0.1and publish nothing but nginx.
Support and licensing
SecureAnyCloud provides 24/7 technical support for this image by email and live chat at sales@secureanycloud.com.
AKHQ is licensed under the Apache License 2.0 (its LICENSE file is on the VM in /usr/share/doc/akhq), Apache Kafka under the Apache License 2.0, and Eclipse Temurin and OpenJDK under the GNU General Public License version 2 with the Classpath Exception. Apache Kafka is a trademark of the Apache Software Foundation, and AKHQ is a trademark of its respective owner. SecureAnyCloud is not affiliated with or endorsed by the Apache Software Foundation or the AKHQ project.
Deploy on Azure
Launch AKHQ for Apache Kafka 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
Overview
AKHQ is the open source web console for Apache Kafka. It gives you a graphical view over a Kafka cluster: browse topics and partitions, read and search the actual messages on a topic, and watch consumer groups and their lag. You can also inspect broker and topic configuration, and manage ACLs, schema registry entries, Kafka Connect and ksqlDB from one interface.
AKHQ is a console, not a broker, so on its own it needs an existing Kafka cluster to point at. To make this image a self-contained appliance, it bundles a single-node Apache Kafka broker on the same instance and wires AKHQ to it. The console has a real cluster to manage the moment the instance starts.
What you get:
- A bundled Apache Kafka broker running in KRaft mode, so there is no ZooKeeper to operate.
- A broker that binds to the loopback interface only. It is not reachable from outside the instance and needs no SASL or TLS configuration of its own, because its only client is AKHQ on the same host.
- nginx publishing the AKHQ interface on port 80 and forwarding WebSocket and Server-Sent Event upgrades, so the live message tail works through the proxy.
- Kafka's log segments and cluster metadata at
/var/lib/kafka, and the AKHQ application, its bundled Java runtime and its configuration at/opt/akhq. Each is a dedicated, independently resizable EBS data volume, kept off the operating system disk. - An administrator password generated on the first boot of every deployed instance, so two instances launched from the same Amazon Machine Image never share a password. It is written to
/root/akhq-credentials.txtwith mode 0600, so only the root user can read it. - A fresh JSON Web Token signing secret and new KRaft storage generated at first boot, which gives every instance its own cluster identity.
When you are ready to work with your own cluster, repoint AKHQ at any external or managed Kafka and leave the bundled broker stopped.
Prerequisites
Before you deploy this image, make sure you have:
- 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 and port 80 for the AKHQ web interface
- The AWS CLI (version 2) installed locally if you plan to deploy from the command line
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 AKHQ. Select the SecureAnyCloud listing, choose Select, then choose Continue on the subscription summary.
Pick an instance type of m5.large or larger. The instance runs two Java virtual machines, one for the Kafka broker and one for AKHQ, so smaller burstable types are not recommended. Choose your EC2 key pair under Key pair (login).
Under Network settings, select your VPC and subnet. Create or select a security group that opens port 22 from your management network and port 80 for AKHQ. Leave the root volume at the default size or larger.
Select Launch instance. After the instance state becomes Running and the status checks pass, first-boot initialisation takes a short time. During this time the image generates its credentials, formats fresh Kafka storage and seeds the demonstration topics.
Launch the instance from the AWS CLI
Run the command below to launch an instance from the SecureAnyCloud AKHQ Marketplace AMI into an existing subnet and security group. Replace <ami-id> with the AMI ID shown on the Marketplace listing and <key-name> with your EC2 key pair name. Replace <subnet-id> with your subnet ID and <security-group-id> with a security group that opens ports 22 and 80 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> \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=akhq}]'When the instance reaches the Running state and its status checks pass, note its public IP address or DNS name. You can find it in the EC2 console or with aws ec2 describe-instances.
Connect to your instance
Connect over SSH using your key pair. For the Ubuntu 24.04 variant, the login user is ubuntu.
ssh -i <key-name>.pem ubuntu@<public-ip>Retrieve the administrator password
The administrator login is unique to your instance and was generated on first boot. The credentials file records the interface URL, the administrator user name and the generated password.
Confirm that the file is present and read the non-secret fields:
sudo grep -E '^(AKHQ_UI_URL|AKHQ_ADMIN_USER)=' /root/akhq-credentials.txtTo read the generated password, run sudo cat /root/akhq-credentials.txt and copy the value on the AKHQ_ADMIN_PASSWORD line. Keep it somewhere safe. The file is mode 0600 and owned by root, so you can read it only through sudo.
Confirm the services are running
Over SSH, confirm that the three services are active:
sudo systemctl is-active kafka akhq nginxAll three should report active. AKHQ exposes an unauthenticated management health endpoint on the loopback interface at port 28081. Monitoring uses it as the liveness probe:
curl -s http://127.0.0.1:28081/healthThe response reports "status":"UP" once AKHQ has finished starting and has connected to the broker.
Confirm the login wall is in place
Upstream AKHQ ships with security disabled and serves every page anonymously. This image enables Micronaut security and sets the default group to no-roles, so nothing is served to an unauthenticated visitor.
Confirm this on your own instance. The first request below carries no credentials and must be refused. The second uses the generated administrator password and must succeed. Neither command prints the password.
PASS=$(sudo grep '^AKHQ_ADMIN_PASSWORD=' /root/akhq-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'unauthenticated: HTTP %{http_code}\n' http://127.0.0.1/api/sac/topic
curl -s -o /dev/null -w 'authenticated: HTTP %{http_code}\n' -u "admin:$PASS" http://127.0.0.1/api/sac/topicThe unauthenticated request returns 401 and the authenticated request returns 200.
Sign in to AKHQ
Browse to http://<public-ip>/ in your browser. AKHQ redirects to its sign-in page. Enter admin as the user name and the password you retrieved in Step 4.
After you sign in, AKHQ opens the Topics view for the bundled cluster, which is registered under the connection name SecureAnyCloud.
First boot seeds the three demo- topics so the console has real content to show. They are ordinary topics, so delete them whenever you are ready to work with your own data.
Browse topics and read messages
Select a topic to open it. The Data tab reads the actual records on the topic. It shows the key, timestamp, partition, offset, headers and message body, with sorting, partition filtering, offset selection and search across the payload.
The Live Tail control at the bottom of the view streams new records as they arrive. This is what the Server-Sent Event support in the nginx configuration is for. The other tabs on a topic cover its partitions, the consumer groups reading it, its configuration, its ACLs and its logs.
You can confirm the same information from the command line with the standard Kafka tools, which are installed under /opt/kafka/bin:
/opt/kafka/bin/kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --listTo see how a topic is laid out across partitions:
/opt/kafka/bin/kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --describe --topic demo-ordersMonitor consumer groups and lag
The Consumer Groups view lists every group known to the cluster. For each group it shows the state, coordinator, member count, the topics it reads and the current lag.
The seeded demo-consumer group has committed offsets and a deliberate non-zero lag, so the view shows realistic data rather than an empty table. Confirm the same figures from the command line:
/opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server 127.0.0.1:9092 --describe --group demo-consumerThe output lists each partition with its current offset, log end offset and the resulting lag.
Inspect the broker
The Nodes view shows the brokers in the cluster, which node is the active controller, and how the partitions are distributed.
The host column reads 127.0.0.1:9092. This confirms that the bundled broker is bound to the loopback interface and is not reachable from outside the instance. Select the node to open its configuration and review every broker setting.
Review the dedicated data volumes
Kafka's data and the AKHQ application each sit on their own EBS volume. The volumes are mounted by filesystem UUID, so the layout survives reboots and instance replacement.
findmnt -no SOURCE,TARGET,FSTYPE,SIZE /var/lib/kafka
findmnt -no SOURCE,TARGET,FSTYPE,SIZE /opt/akhqYou can resize each volume independently in the EC2 console without touching the operating system disk. Kafka's log segments and KRaft metadata are under /var/lib/kafka/data. Retention is governed by the standard log.retention.hours setting in /opt/kafka/config/server.properties, which defaults to one week.
Point AKHQ at your own Kafka cluster
The bundled broker exists so the console is useful immediately. To manage your own cluster instead, edit the connections block in the AKHQ configuration file with sudo nano /opt/akhq/application.yml, or with the editor of your choice.
The akhq.connections section defines one entry named SecureAnyCloud, which points at the bundled broker. Add your own connection alongside it, or replace it. AKHQ supports multiple connections at once, so you can keep several clusters in one console and switch between them from the cluster selector:
akhq: connections: sac: properties: bootstrap.servers: "127.0.0.1:9092" production: properties: bootstrap.servers: "broker-1.example.com:9092,broker-2.example.com:9092" security.protocol: SASL_SSL sasl.mechanism: SCRAM-SHA-512 sasl.jaas.config: >- org.apache.kafka.common.security.scram.ScramLoginModule required username="<your-user>" password="<your-password>";A minimal external connection needs only the bootstrap servers. Most managed clusters also need the security protocol and SASL settings that your provider documents.
After you edit the file, restart the service with sudo systemctl restart akhq to reload the configuration. Then confirm that it came back with sudo systemctl is-active akhq.
If you no longer want the local broker, stop and disable it with sudo systemctl disable --now kafka so that it does not consume memory. Leaving it running costs nothing beyond its memory footprint, and it is useful as a scratch cluster for testing producers and consumers.
Enable HTTPS
The image serves AKHQ over plain HTTP on port 80. This is suitable behind a load balancer that terminates TLS, but not for direct exposure to the internet.
To terminate TLS on the instance itself, point a DNS name at the instance and open port 443 in the security group. Then use Certbot to obtain and install a certificate against the existing nginx site:
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <your-domain>Certbot rewrites the nginx site to listen on 443 with your certificate and installs a renewal timer. The proxy settings that carry the WebSocket and Server-Sent Event upgrades are preserved, so the live tail keeps working over HTTPS.
Alternatively, place the instance behind an Application Load Balancer with an ACM certificate, and leave the instance serving plain HTTP inside your VPC.
Backup and maintenance
Kafka's durable state is the log directory on the dedicated volume, so the simplest backup is an EBS snapshot of the /var/lib/kafka volume. You can schedule snapshots with Amazon Data Lifecycle Manager. For a consistent snapshot, stop the broker, take the snapshot, then start the broker again.
The AKHQ configuration is a single file, /opt/akhq/application.yml. It holds your cluster connections and your user definitions, so keep a copy alongside your infrastructure code. It also holds the hashed administrator password and the JWT signing secret generated for this instance, so treat it as sensitive.
Update operating system packages with the distribution's normal tooling. AKHQ itself is a single jar at /opt/akhq/akhq.jar, run by the bundled Java runtime at /opt/akhq/jre. To upgrade AKHQ, replace that jar with a newer release and restart the service.
The Kafka distribution is unpacked under /opt, with /opt/kafka as a symbolic link to the versioned directory. To upgrade Kafka, unpack the new version and repoint the link.
Check the service state at any time without searching the journal:
sudo systemctl show -p ActiveState -p SubState kafka akhq nginxSupport
SecureAnyCloud supports this image. Email sales@secureanycloud.com with your instance ID and AWS Region for help with deployment and sizing, connecting AKHQ to an external or managed Kafka cluster, authentication configuration including LDAP and OIDC, TLS termination, broker configuration and retention tuning, or version upgrades. Critical issues receive a one-hour average response.
AKHQ and Apache Kafka are trademarks of their respective owners. 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 AKHQ for Apache Kafka on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com