Skip to content

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.

FeatureSkipprApache Pulsar
DeploySingle Rust binary, your VPC or on-premBrokers plus BookKeeper, or StreamNative Cloud / Private Cloud
SchemaIn-stream evolution, sibling columnsTopic schema. Lakehouse mapping encodes tenant/namespace into the catalog
StorageOpen Iceberg on Parquet you ownPulsar log. Iceberg is lakehouse sink or StreamNative Lakehouse Table
CDCLog-based; fails closed if the dest cannot honourA source connector writes the topic. Pulsar is the bus
Queryskipprd query on Iceberg ∪ WAL; history in any Iceberg engineConsumers on the topic. Iceberg after compaction or the sink
PricingFree self-hosted engineApache-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.

yaml
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-1
bash
skipprd --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_schema
python
from 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")
bash
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.