Deployment guide

Apache Pulsar 5.0.1

Overview

Apache Pulsar is a distributed messaging and event streaming platform. It offers multi-tenancy (tenants, namespaces, topics), persistent storage in Apache BookKeeper, and both queueing and streaming subscriptions.

This SecureAnyCloud AMI runs Pulsar in standalone mode: the broker, one local bookie and the metadata store in one process on one instance. It suits development, testing, single-node workloads and proof-of-concept work. This guide covers launching and using the Apache Pulsar 5.0.1 on Ubuntu 24.04 AMI on Amazon EC2. The AMI is one version of the SecureAnyCloud streaming and messaging listing on AWS Marketplace. You pick it as the software version when you subscribe and launch.

What you get:

  • Authentication and authorization on for every client. Pulsar's JWT token authentication (RS256) and its built-in authorization are switched on, and there is no anonymous role. A client without a valid token is refused, and a client with a valid token can only do what its role has been granted. The image uses Pulsar's default authorization provider, never the multi-roles provider.
  • No default credentials, no shared secret. The token signing key pair, your superuser token and the broker's own internal token are generated on your instance at first boot. The image ships none of them. Pulsar's data (cluster metadata, bookie identity, topics) is created on your instance when it first starts.
  • TLS only off the instance. Clients on other hosts connect over pulsar+ssl:// on port 6651 and use the admin REST API over https:// on port 8443. Both use TLS 1.3/1.2 and a certificate generated on your instance. It is an end-entity certificate: it can only vouch for this instance's own names and can never sign other certificates. The plaintext listeners (pulsar:// on 6650, http:// on 8080) and the bookie (3181) listen on 127.0.0.1 only.
  • Fail closed. Pulsar starts only after the boot service has written your keys and tokens. A configuration check refuses to start it with authentication or authorization off, an anonymous role, a plaintext listener on a network address, a dummy host name or a missing key, token or certificate.
  • Least-privilege tokens without shell history. pulsar-token mints a token for an application role straight into a root-only file. The token is never printed and never on a command line. You grant each role only the namespaces and actions it needs.
  • Smaller attack surface. The image has no web UI (the end-of-life Pulsar Manager is not included), no functions worker, no connectors, no WebSocket service, no proxy, no package management and no transaction coordinator. The broker is blocked from the instance metadata service, the AMI requires IMDSv2, and Pulsar runs under systemd sandboxing as an unprivileged user.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
  • Components on the image. Apache Pulsar 5.0.1 (the official binary release, verified against its published SHA-512 and its Apache release signature) on OpenJDK 21 (from Ubuntu 24.04), cloud-init and the AWS Systems Manager agent.

Default user: ubuntu; Architecture: x86_64; Recommended instance type: m5.large

Prerequisites

You need:

  • An AWS account subscribed to the listing, an EC2 key pair, and a VPC subnet with a route to the internet (or a private route you use for management).
  • A security group that allows TCP 22 (SSH) from your admin network, and TCP 6651 (Pulsar over TLS) and TCP 8443 (admin REST over HTTPS) from the networks your clients run in. Never open 6650, 8080 or 3181, because they listen on the loopback address only.
1

Launch the instance

In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick Apache Pulsar 5.0.1 on Ubuntu 24.04 as the software version and choose your Region. Then choose Launch through EC2. Choose m5.large, your key pair and a security group as described above.

To launch with the AWS CLI instead, 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=pulsar}]'

The AMI requires IMDSv2 (session tokens) for the instance metadata service.

First boot takes about a minute after the instance is running. The boot service reads the instance's own DNS names and addresses from the metadata service, and it generates the token key pair, the tokens and the certificate. Pulsar then starts and creates its cluster.

2

Connect to your instance

Version SSH user

Apache Pulsar 5.0.1 on Ubuntu 24.04 ubuntu

ssh -i <your-key.pem> ubuntu@<public-ip>
3

Check the services

systemctl is-active pulsar-metadata-names pulsar-firstboot pulsar
sudo test -f /var/lib/sac/data/pulsar-firstboot.done && echo FIRSTBOOT_DONE
sudo pulsar-health

All three units are active, the first-boot marker is present, and the broker answers an authenticated health check:

active active active FIRSTBOOT_DONE PRIMARY_HEALTH_OK broker healthy (authenticated health check on the loopback admin listener)

Only SSH and Pulsar's TLS listeners listen on network addresses. The plaintext listeners and the bookie are on the loopback address:

sudo ss -Htlnp | 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:6651 users:(("java")) 0.0.0.0:8443 users:(("java")) 127.0.0.1:3181 users:(("java")) 127.0.0.1:6650 users:(("java")) 127.0.0.1:8080 users:(("java")) [::]:22 users:(("sshd"),("systemd"))
4

Your URLs, superuser token and certificate fingerprint

sudo cat /root/pulsar-credentials.txt

Only root can read the file:

# Apache Pulsar 5.0.1 on Ubuntu 24.04 - generated on first boot by pulsar-firstboot.service.
# Every key and token below is unique to this instance; none was baked into the image. Keep this file secret.
# ... PULSAR_SERVICE_URL_TLS=pulsar+ssl://<public-dns>:6651 PULSAR_WEB_URL_TLS=https://<public-dns>:8443/ PULSAR_SERVICE_URL_LOCAL=pulsar://127.0.0.1:6650 PULSAR_WEB_URL_LOCAL=http://127.0.0.1:8080/ PULSAR_TLS_CERT_SHA256=<sha256-fingerprint> PULSAR_TLS_CERT_FILE=/etc/sac/pulsar-broker.pem
#
# Superuser (administration only; do not hand it to applications): PULSAR_ADMIN_ROLE=sac-admin PULSAR_ADMIN_TOKEN_FILE=/root/pulsar-admin.token PULSAR_ADMIN_TOKEN=<PULSAR_ADMIN_TOKEN>
# Change the advertised name:
sudo pulsar-set-hostname <dns-name> Re-verify the appliance:
sudo pulsar-selfcheck

Pulsar advertises itself to clients under the instance's public DNS name. It uses the public IP address when the VPC gives no DNS name, and the private address when there is no public one. It reads the name from the EC2 instance metadata at boot, never from a client request. The certificate also names the public IP address, the private DNS name and address, 127.0.0.1 and localhost.

If you stop and start the instance without an Elastic IP, the boot service picks up the new names and issues a new certificate for them. To use your own DNS name, see Step 9.

The superuser token is also in /root/pulsar-admin.token (root only). Use it for administration only. Give every application its own least-privilege token (Step 6).

Print the certificate's SHA-256 fingerprint:

sudo openssl x509 -in /etc/sac/pulsar-broker.pem -noout -fingerprint -sha256
sha256 Fingerprint=<sha256-fingerprint>

The certificate is self-signed and unique to this instance. Pin this broker's certificate for your Pulsar client only. Copy /etc/sac/pulsar-broker.pem to the client host. Check that openssl x509 -in pulsar-broker.pem -noout -fingerprint -sha256 prints the PULSAR_TLS_CERT_SHA256 value from /root/pulsar-credentials.txt, and point that client's own tlsTrustCertsFilePath at the copy. Never add it to an operating-system, browser or JVM-wide trust store. Alternatively, install a CA-issued certificate with sudo pulsar-install-cert <chain.pem> <key.pem> (Step 9).

5

Administer Pulsar on the instance

pulsar-local runs Pulsar's own pulsar-admin, pulsar-client and pulsar-perf tools against the loopback listener as the superuser. It reads the token from a file through /root/pulsar-client.conf (authParams=file:///root/pulsar-admin.token), so the token never appears on a command line or in your shell history:

sudo pulsar-local admin brokers version
sudo pulsar-local admin clusters list
sudo pulsar-local admin tenants list
5.0.1 standalone public pulsar

Create a tenant, a namespace and a partitioned topic for an application:

sudo pulsar-local admin tenants create acme --allowed-clusters standalone
sudo pulsar-local admin namespaces create acme/orders
sudo pulsar-local admin topics create-partitioned-topic persistent://acme/orders/events --partitions 3
sudo pulsar-local admin topics list-partitioned-topics acme/orders
persistent://acme/orders/events

Set a retention policy so that Pulsar keeps messages for a day (or up to 1 GB) after they are acknowledged:

sudo pulsar-local admin namespaces set-retention acme/orders --time 1d --size 1G
sudo pulsar-local admin namespaces get-retention acme/orders
{ "retentionTimeInMinutes" : 1440, "retentionSizeInMB" : 1024 }
6

Least-privilege tokens for your applications

Each application gets its own role and token. pulsar-token signs the token with this instance's key (/etc/pulsar/token-signing, root only) and writes it to /root/pulsar-tokens/<role>.token (0600). It prints only the file path. An optional second argument sets an expiry such as 90d, 12h or 1y:

sudo pulsar-token create orders-app 90d
pulsar-token: token for role 'orders-app' (expires in 90d) written to /root/pulsar-tokens/orders-app.token (0600; not printed) pulsar-token: grant it what it needs, e.g.
sudo pulsar-local admin namespaces grant-permission public/default --role orders-app --actions produce,consume

A new role can do nothing until you grant it permissions. Grant this role produce and consume on the acme/orders namespace only:

sudo pulsar-local admin namespaces grant-permission acme/orders --role orders-app --actions produce,consume
sudo pulsar-local admin namespaces permissions acme/orders
orders-app [consume, produce]

Try it on the instance with a client configuration that uses the role's token file. This uses the loopback listener, so no certificate is needed here:

sudo tee /root/orders-app-local.conf >/dev/null <<'EOF' webServiceUrl=http://127.0.0.1:8080/ brokerServiceUrl=pulsar://127.0.0.1:6650/ authPlugin=org.apache.pulsar.client.impl.auth.AuthenticationToken authParams=file:///root/pulsar-tokens/orders-app.token EOF
sudo chmod 0600 /root/orders-app-local.conf
sudo env PULSAR_CLIENT_CONF=/root/orders-app-local.conf /opt/pulsar/bin/pulsar-client produce persistent://acme/orders/events -m "order 1001 created" -n 3 2>&1 | grep -o '[0-9]* messages successfully produced'
sudo env PULSAR_CLIENT_CONF=/root/orders-app-local.conf /opt/pulsar/bin/pulsar-client consume persistent://acme/orders/events -s billing -p Earliest -n 3 2>&1 | grep -o 'content:order 1001 created' | sort | uniq -c
3 messages successfully produced 3 content:order 1001 created

Pulsar refuses the same token outside its grant, for example on the public/default namespace:

OUT=$(sudo env PULSAR_CLIENT_CONF=/root/orders-app-local.conf /opt/pulsar/bin/pulsar-client produce persistent://public/default/other -m x 2>&1 || true)
grep -o 'AuthorizationException[^,]*' <<<"$OUT" | head -1
AuthorizationException: {"errorMsg":"Client is not authorized to Get Partition Metadata"

To cut a role off, revoke its permissions and then delete its file. A token stays valid until it expires, so revoking the permissions is what takes effect at once:

sudo pulsar-local admin namespaces revoke-permission acme/orders --role orders-app
sudo pulsar-token delete orders-app

sudo pulsar-token list shows the role tokens you have minted.

7

Connect a client from another host (pulsar+ssl, port 6651)

On your client host, you need three things from the instance: the broker certificate, the application's token file and the TLS URLs. Copy the certificate and the token over SSH. The token file is root-only on the instance, so copy it through a root-readable pipe:

# on your client host scp -i <your-key.pem> ubuntu@<public-ip>:/etc/sac/pulsar-broker.pem .
ssh -i <your-key.pem> ubuntu@<public-ip> 'sudo cat /root/pulsar-tokens/orders-app.token' > orders-app.token chmod 0600 orders-app.token

Check that the certificate is the one your instance generated before you trust it. The fingerprint must equal PULSAR_TLS_CERT_SHA256 in /root/pulsar-credentials.txt (Step 4):

# on your client host openssl x509 -in pulsar-broker.pem -noout -fingerprint -sha256

Then point the client's own trust file at that copy. For Pulsar's command line tools (pulsar-client, pulsar-admin) and the Java client, use a client.conf like this one. The instance has a template in /root/pulsar-client-tls.conf:

webServiceUrl=https://<public-dns>:8443/ brokerServiceUrl=pulsar+ssl://<public-dns>:6651 authPlugin=org.apache.pulsar.client.impl.auth.AuthenticationToken authParams=file:///home/you/orders-app.token tlsTrustCertsFilePath=/home/you/pulsar-broker.pem tlsAllowInsecureConnection=false tlsEnableHostnameVerification=true
# on your client host, from an Apache Pulsar 5.0.1 binary distribution PULSAR_CLIENT_CONF=./client.conf bin/pulsar-client produce persistent://acme/orders/events -m "hello over TLS" PULSAR_CLIENT_CONF=./client.conf bin/pulsar-client consume persistent://acme/orders/events -s billing -n 1

The Python client (pip install pulsar-client) takes the same three settings:

import pulsar token = open("orders-app.token").read().strip() client = pulsar.Client( "pulsar+ssl://<public-dns>:6651", authentication=pulsar.AuthenticationToken(token), tls_trust_certs_file_path="pulsar-broker.pem", tls_validate_hostname=True, ) producer = client.create_producer("persistent://acme/orders/events") producer.send(b"order 1002 created") client.close()

Pulsar refuses a client without a token, with a token signed by another key, or with an expired token, and returns AuthenticationException: Failed to authenticate. A client that trusts only the system certificate authorities, or that connects under a name the certificate does not carry, refuses the connection during the TLS handshake. A plaintext pulsar:// client gets no answer on 6651, and 6650 is not reachable from other hosts at all.

8

The admin REST API over HTTPS (port 8443)

pulsar-admin on another host uses the webServiceUrl from the same client.conf, with the superuser token file for administration. You can also call the REST API directly. Keep the token out of the command line by putting the header in a root-only curl config file:

sudo bash -c 'umask 077; printf "header = \"Authorization: Bearer %s\"\n" "$(cat /root/pulsar-admin.token)" > /root/pulsar-admin.curl'
H=$(sudo sed -n 's#^PULSAR_WEB_URL_TLS=https://\([^:]*\):8443/$#\1#p' /root/pulsar-credentials.txt)
sudo curl -s --cacert /etc/sac/pulsar-broker.pem --resolve "$H:8443:127.0.0.1" -K /root/pulsar-admin.curl "https://$H:8443/admin/v2/tenants"; echo curl -s -o /dev/null -w '%{http_code}\n' --cacert /etc/sac/pulsar-broker.pem --resolve "$H:8443:127.0.0.1" "https://$H:8443/admin/v2/tenants"
["acme","public","pulsar"] 401

The second request has no token, and Pulsar refuses it with HTTP 401. Prometheus metrics (/metrics/) also need a token. Delete the curl config when you no longer need it:

sudo shred -u /root/pulsar-admin.curl
9

Your own DNS name and certificate

To advertise the broker under your own DNS name, for example a Route 53 record that points at an Elastic IP, run:

sudo pulsar-set-hostname pulsar.example.com

The command writes the name to /etc/sac/pulsar-hostname so it survives reboots. It issues a new self-signed certificate for the name, updates the URLs and the fingerprint in /root/pulsar-credentials.txt, and restarts Pulsar. Clients then use pulsar+ssl://pulsar.example.com:6651. Re-check and re-pin the new certificate.

To use a certificate from a certificate authority, for example Let's Encrypt, put the certificate chain (server certificate first) and its private key on the instance. Then run:

sudo pulsar-install-cert fullchain.pem privkey.pem pulsar.example.com

The boot service never replaces a certificate you installed. Clients then trust your CA as usual, with tlsTrustCertsFilePath pointing at your CA bundle, instead of pinning. sudo pulsar-set-hostname <name> goes back to a self-signed certificate.

10

Security checks

pulsar-selfcheck re-verifies the whole appliance in about a minute. It checks the listening set, the certificate on both TLS ports and the refusal matrix on the wire. In that matrix, no token, a forged token, an expired token and a role without permissions are each refused, and the superuser is accepted. It also checks the REST API, a real produce and consume over TLS with the pinned certificate, and the instance metadata block:

sudo pulsar-selfcheck
ok listening: off-host only 22, 6651 (pulsar+ssl), 8443 (https); 6650, 8080, 3181 on 127.0.0.1 only ok certificate: end-entity (CA:FALSE), names <public-dns>, served on 6651 and 8443, fingerprint <sha256-fingerprint> ok pulsar+ssl :6651 superuser token connects + looks up; anonymous, forged and expired tokens refused by authentication; a role without permissions refused by authorization ok no Pulsar answer to plaintext on 6651; a system-CA-only client and a client expecting another name both reject the self-signed certificate ok pulsar :6650 (loopback) also requires a token ok broker log reasons: anonymous = no anonymous role; forged = JWT signature mismatch; expired = JWT expired ok admin REST: https :8443 superuser 200; anonymous / forged / expired 401; no-permission role 401; /metrics needs a token; loopback :8080 needs a token ok pulsar-client: 3 messages produced and consumed over pulsar+ssl with the pinned certificate (topic deleted again) ok instance metadata refused inside pulsar.service (loopback control answered) SELFCHECK OK

The broker is blocked from the instance metadata service (169.254.169.254) and the Azure/WireServer address by its systemd unit, so a request the broker makes on a client's behalf can never reach instance credentials. Only pulsar-metadata-names.service reads the instance's names. It is a one-shot unit that runs at boot and can reach nothing but the metadata service:

systemctl show pulsar -p IPAddressDeny --value
systemctl show pulsar-metadata-names -p IPAddressAllow --value
169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32 fd00:ec2::254/128
11

Configuration and tuning

  • Configuration. The file is /etc/pulsar/standalone.conf (root:pulsar 0640). After a change, restart with sudo systemctl restart pulsar. The configuration check refuses to start Pulsar if a change switches off authentication or authorization, adds an anonymous role, opens a plaintext listener on a network address or turns on a feature this image keeps off.
  • Memory. The broker runs with a 1 GB initial and 2 GB maximum heap and 2 GB of direct memory, sized for m5.large (8 GiB). On a larger instance, raise them with a drop-in. Run sudo systemctl edit pulsar, add [Service] and Environment="PULSAR_MEM=-Xms4g -Xmx4g -XX:MaxDirectMemorySize=4g", then restart.
  • Data. Data lives in /var/lib/pulsar (bookie ledgers and journal, metadata store). Grow the instance's root EBS volume when you need more space. Logs are in /var/log/pulsar/pulsar-standalone.log.
  • Not included. The image has no web UI, Pulsar functions worker, built-in connectors, tiered-storage offloaders, WebSocket service or Pulsar proxy. Standalone mode is a single node. For a multi-node production cluster, use separate brokers, bookies and a metadata store.
12

Versions, provenance and file locations

cat /usr/share/doc/apache-pulsar/BUILD-PROVENANCE.txt java -version 2>&1 | head -1
Apache Pulsar 5.0.1 (Apache-2.0) as shipped by sac source: https://archive.apache.org/dist/pulsar/pulsar-5.0.1/apache-pulsar-5.0.1-bin.tar.gz sha512: 97baef9c5d04d906b1a5fb7b9a8905fd8f9db5e639c400bcedf0e5092dcde56d5b036bef4d9213fbe52fcc0030708689db1d7c8bffe13e4781d5659ca2637f27 signature: apache-pulsar-5.0.1-bin.tar.gz.asc verified against the Apache Pulsar KEYS file, signer C663D2F64DA2CA09DB28D9ABD3FA67D522C55256 license: LICENSE (Apache License 2.0) sha256 2224d108489b90a5298407bad70fe14948cef147f46c343d5778e584cc07291c runtime: openjdk version "21.0.12.1" 2026-08-18 (Ubuntu openjdk-21-jre-headless) removed: examples/, instances/ (functions runtime), bin/*.cmd not shipped: Apache Pulsar Manager (end of life, CVE-2026-105944), functions worker, connectors, stream storage, Pulsar proxy, WebSocket service, package management, transaction coordinator openjdk version "21.0.12.1" 2026-08-18

Path What

/opt/pulsar Apache Pulsar 5.0.1 (root owned, read only to the service)

/etc/pulsar/standalone.conf Broker configuration

/etc/pulsar/auth/token-public.key Token verification key (the broker reads only this)

/etc/pulsar/token-signing/token-private.key Token signing key (root only)

/etc/pulsar/tls/ TLS certificate and key

/etc/sac/pulsar-broker.pem The broker certificate to copy to clients

/root/pulsar-credentials.txt, /root/pulsar-admin.token URLs, fingerprint and the superuser token

/root/pulsar-tokens/ Role tokens minted with pulsar-token

/var/lib/pulsar, /var/log/pulsar Data and logs

13

Backup and maintenance

  • Back up the token signing key pair and the superuser token (/etc/pulsar/token-signing/, /etc/pulsar/auth/, /root/pulsar-admin.token) together with /var/lib/pulsar. Every token you issued was signed with that key. For a consistent copy, take EBS snapshots of the root volume with Pulsar stopped.
  • Ubuntu security updates install automatically. For Pulsar upgrades, check for a newer SecureAnyCloud version of the listing.
14

Security recommendations

  • Open 6651 and 8443 only to the networks your clients run in, and 22 only to your admin network.
  • Give every application its own role, the narrowest grant it needs and an expiry. Keep the superuser token for administration.
  • Rotate a leaked role token by revoking the role's permissions and minting a new role. Keep backups of the signing key pair private.
  • Prefer a certificate from a certificate authority for production clients (Step 9).

Support and licensing

Apache Pulsar is licensed under the Apache License 2.0 (/usr/share/doc/apache-pulsar/LICENSE). SecureAnyCloud provides the image, the secure-by-default configuration and support for launching and using it. Apache Pulsar, Pulsar and Apache are trademarks of the Apache Software Foundation. This image is not affiliated with or endorsed by the Apache Software Foundation.

Deploy on AWS

Launch Apache Pulsar 5.0.1 on Ubuntu 24.04 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