Apache Pulsar alternative — Apache Pulsar vs Skippr
Apache Pulsar is Apache-2.0 pub/sub. Brokers serve topics. BookKeeper (or, on StreamNative cost-optimized clusters, object storage) holds the log. Current LTS is 5.0.1 (26 Oct 2026); 4.0.14 still has security support. Producers, consumers, functions, and IO connectors sit on the bus. Iceberg is not in the Pulsar broker. StreamNative's pulsar-io-lakehouse sink (documented example pulsar-io-lakehouse-4.0.3.1.nar) writes Iceberg, Hudi, or Delta with EFFECTIVELY_ONCE, default maxCommitInterval 120 seconds. StreamNative Lakehouse Table is a later product: a compaction service reads the WAL, writes Parquet, and commits Iceberg or Delta to S3, GCS, or Azure Blob, into Unity, Snowflake Open Catalog / Horizon, S3 Tables, or BigLake. External Table is a separate copy of the topic. Internal Table (one copy for stream and table) is still listed as coming soon. CDC is a source connector into a topic, not Pulsar itself. Self-host is Apache-2.0; you pay BookKeeper, brokers, ZooKeeper or Oxia, and people. StreamNative Cloud bills reserved throughput on Dedicated and elastic throughput units on BYOC. This is not Iceberg ∪ WAL you query in one binary.
Skippr is an open Streamhouse you run as one Rust binary. Write skippr.yml (or the same thing in Python), discover, sync. You query live state over Iceberg ∪ WAL, and the history is ordinary Iceberg in your bucket. No BookKeeper cluster. ELT into a warehouse is one path, not the product.
| Feature | Skippr | Apache Pulsar |
|---|---|---|
| Deploy | Single Rust binary, your VPC or on-prem | Brokers plus BookKeeper, or StreamNative Cloud / Private Cloud |
| Schema | In-stream evolution, sibling columns | Topic schema. Lakehouse mapping encodes tenant/namespace into the catalog |
| Storage | Open Iceberg on Parquet you own | Pulsar log. Iceberg is lakehouse sink or StreamNative Lakehouse Table |
| CDC | Log-based; fails closed if the dest cannot honour | A source connector writes the topic. Pulsar is the bus |
| Query | skipprd query on Iceberg ∪ WAL; history in any Iceberg engine | Consumers on the topic. Iceberg after compaction or the sink |
| Pricing | Free self-hosted engine | Apache-2.0. StreamNative Dedicated reserved throughput; BYOC ETU. |
What Apache Pulsar is good at
A multi-tenant log with queues and streams on the same cluster, geo-replication, and many consumers on one topic. That is the product. BookKeeper for low-latency WAL, or StreamNative's object-store WAL when cost is the profile, is why some estates picked it over Kafka. If the job is "this event onto this bus," that is a real product. StreamNative Lakehouse Table is the honest Iceberg landing when you already run that bus.
Where the job is wider
Pulsar is a message bus. It is not a Streamhouse. Apache Pulsar does not query Iceberg ∪ WAL. Live SQL is not pulsar-admin. Iceberg is a StreamNative sink or a compaction service after the topic. External Table duplicates the records. Internal Table is not shipping. CDC still needs Debezium, Canal, or an app writing the topic. Lakehouse delivery on StreamNative is gated by cluster type, profile, and protocol (Pulsar latency-optimized plus Pulsar protocol, or cost-optimized plus Kafka protocol; other combinations wait).
The cluster is the ops. Brokers, BookKeeper, metadata, and a compaction fleet if you want Iceberg. Skippr will not replace a Pulsar topic that already fans out to many consumers. Skippr also will not run BookKeeper. Kafka / Tableflow is a separate page.
If the job is "Pulsar we already staff," Pulsar is the narrower tool. If the job is live SQL on Iceberg you run, the bus was never that.
Getting started
Install skipprd from the install guide, or pip install skippr. The three tabs write the same skippr.yml. Then discover, sync, and query.
skippr:
workspace: analytics
skipprd_el_storage_mode: local
pipelines:
orders:
data_source: data_sources.app_db
data_sink: data_sinks.lake
data_sources:
app_db:
Postgres:
host: db.internal
user: skippr_reader
password: ${APP_DB_PASSWORD}
database: app
tables: [orders, customers]
cdc_mode: snapshot_then_cdc
data_sinks:
lake:
AthenaIceberg:
warehouse: s3://my-iceberg-warehouse/
glue_database_name: analytics
athena_workgroup_name: primary
athena_results_s3_bucket: my-athena-results
region: us-east-1
schema_sink: schema_sinks.lake_schema
schema_sinks:
lake_schema:
AthenaIceberg:
warehouse: s3://my-iceberg-warehouse/
glue_database_name: analytics
athena_workgroup_name: primary
athena_results_s3_bucket: my-athena-results
region: us-east-1skipprd --workspace analytics --storage-mode local connect data-source postgres \
--pipeline orders \
--name app_db \
--host db.internal \
--user skippr_reader \
--password '${APP_DB_PASSWORD}' \
--database app \
--tables orders \
--tables customers \
--cdc-mode snapshot_then_cdc
skipprd connect data-sink athenaiceberg \
--pipeline orders \
--name lake \
--warehouse s3://my-iceberg-warehouse/ \
--glue-database-name analytics \
--athena-workgroup-name primary \
--athena-results-s3-bucket my-athena-results \
--region us-east-1
skipprd connect schema-sink athenaiceberg \
--pipeline orders \
--name lake_schemafrom skippr import (
Config,
DataSinkAthenaIceberg,
DataSourcePostgres,
DataSourcePostgresSourceCdcMode,
EnvRef,
LocalStorage,
Pipeline,
)
cfg = Config().workspace("analytics").storage(LocalStorage())
app_db = cfg.data_source(
"app_db",
DataSourcePostgres(
host="db.internal",
user="skippr_reader",
password=EnvRef("APP_DB_PASSWORD"),
database="app",
tables=["orders", "customers"],
cdc_mode=DataSourcePostgresSourceCdcMode.SNAPSHOTTHENCDC,
),
)
lake = cfg.data_sink(
"lake",
DataSinkAthenaIceberg(
warehouse="s3://my-iceberg-warehouse/",
glue_database_name="analytics",
athena_workgroup_name="primary",
athena_results_s3_bucket="my-athena-results",
region="us-east-1",
),
schema_sink="lake_schema",
)
cfg.pipeline("orders", Pipeline(data_source=app_db, data_sink=lake))
cfg.save("skippr.yml")export APP_DB_PASSWORD='your-postgres-password'
skipprd discover --pipeline orders
skipprd sync --pipeline orders --once
skipprd query "SELECT count(*) FROM analytics.orders"Which to choose
Choose Pulsar if you want a multi-tenant log, and the topic after produce is enough. Add StreamNative Lakehouse Table if that bus must also land Iceberg.
Choose Skippr if you want one binary, live SQL on Iceberg ∪ WAL, and files that stay yours. Skippr will not run a Pulsar cluster.
Install from Install. Connector and query reference live in Developer Docs. For optional onboarding in your VPC, Enterprise Jumpstart or Contact.
