Sign inSign up

humpcdc/hump

By humpcdc

•Updated 3 days ago

Your database, as an event stream.

Image
Integration & delivery
Developer tools
Databases & storage
0

354

humpcdc/hump repository overview

⁠Hump

In a microservice system each service owns its database, and other services still need to know what changed in it. Hump reads every insert, update, delete and truncate from PostgreSQL logical replication and publishes each change as an event.

It uses the built-in pgoutput plugin, so PostgreSQL needs no extensions. Your service keeps writing to its own tables. You don't add event-publishing code, an outbox table or dual writes.

Website⁠ · Documentation⁠ · GitHub⁠ · Issues⁠ · Releases⁠

  • A single static binary of about 30 MB that uses 20–30 MB of RAM. Run one container next to each database.
  • Hump creates its publication and replication slot on start. --cleanup drops them and exits.
  • Events use a Debezium-compatible envelope by default. Every topic, subject, routing key, header and payload is a Go template.
  • Delivery is at-least-once. Hump confirms the WAL position on transaction commit, after every sink has delivered. Each sink has its own retry policy (exponential or linear backoff) and failure policy (crash or skip).
  • Snapshots backfill existing rows into the same sinks.
  • A built-in web console shows live events and stats and runs snapshots. Prometheus metrics and health probes are included.
  • MIT-licensed, with no telemetry.
SourcePostgreSQL 15+, including declarative partitions and TimescaleDB hypertables
SinksKafka (TLS, SASL), NATS Core, NATS JetStream, RabbitMQ (AMQP 0.9.1), RabbitMQ Streams, HTTP webhooks

⁠Tags

Images are built from the v* release tags of hump-cdc/hump⁠. Each release gets three tags:

TagExamplePoints to
<major>.<minor>.<patch>1.0.1That exact release
<major>.<minor>1.0The newest patch of that minor
<major>1The newest release of that major; not published for 0.x
latestThe newest stable release

A pre-release gets only its full version tag. It does not move <major>.<minor>, <major> or latest.

For production, pin the image by digest:

docker buildx imagetools inspect docker.io/humpcdc/hump:<version>
# then use docker.io/humpcdc/hump:<version>@sha256:<digest>

⁠Architectures

linux/amd64 and linux/arm64, published as one multi-arch index.

⁠Image details

  • Base: chainguard/static. The image contains only the hump binary: no shell, no package manager.
  • Entrypoint: /usr/bin/hump. Anything after the image name in docker run is passed to it as flags.
  • User: 65532:65532 (nonroot). A mounted config file must be readable by that UID.
  • Exposed ports: 3200 (web UI and API) and 9100 (metrics and health probes). Both servers stay off until you set WEB_PORT / TELEMETRY_PORT.
  • No volumes. Hump keeps its replication position in the PostgreSQL replication slot, not on disk.

⁠Quick start

⁠1. Prepare PostgreSQL

Enable logical replication and restart the server:

ALTER SYSTEM SET wal_level = logical;

max_replication_slots and max_wal_senders must be at least 1; the defaults are fine. The user Hump connects as needs the REPLICATION attribute or superuser rights. Hump creates the publication and the replication slot itself.

⁠2. Write hump.yaml
tables:
  - schema: public
    table: users
    sinks:
      - kafka:
          url: '{{ env "KAFKA_URL" }}'
          topic: "{{ .Schema }}.{{ .Table }}"

Config values are Go templates, so env reads a variable from the container environment at render time. You can also write the address in directly, e.g. url: "kafka:9092".

⁠3. Run
docker run -d --name hump \
  -e DATABASE_URL="postgres://user:[email protected]:5432/app" \
  -e KAFKA_URL="kafka.example.internal:9092" \
  -e WEB_PORT=3200 \
  -e TELEMETRY_PORT=9100 \
  -p 3200:3200 -p 9100:9100 \
  -v "$PWD/hump.yaml:/etc/hump/hump.yaml:ro" \
  docker.io/humpcdc/hump:latest \
  --config-file /etc/hump/hump.yaml

Every change to public.users now lands in the public.users topic. The web console is at http://localhost:3200⁠ and metrics are at http://localhost:9100/metrics⁠.

Inside the container, localhost is the container itself. Use a hostname reachable from it: a service name on a shared Docker network, or host.docker.internal for a database on the Docker host (on Linux add --add-host=host.docker.internal:host-gateway).

The web console has no built-in authentication. Do not publish port 3200 outside a trusted network.

The image does not need a file mount. Hump also reads its config from the CONFIG variable or from stdin:

docker run -i --rm -e DATABASE_URL="postgres://..." docker.io/humpcdc/hump:1 < hump.yaml
⁠Remove the publication and slot

An unused replication slot makes PostgreSQL keep WAL. To drop the publication and slot, run:

docker run --rm -e DATABASE_URL="postgres://..." -v "$PWD/hump.yaml:/etc/hump/hump.yaml:ro" \
  docker.io/humpcdc/hump:1 --config-file /etc/hump/hump.yaml --cleanup

⁠Options

Each option is a flag or an environment variable.

FlagVariableDefaultMeaning
-d, --database-urlDATABASE_URLPostgreSQL connection URL
-f, --config-fileCONFIG_FILEPath to the YAML config
-c, --configCONFIGThe YAML config itself; excludes --config-file
-p, --publicationPUBhump_pubPublication name
-s, --slotSLOThump_slotReplication slot name
-w, --web-portWEB_PORToffPort of the web UI and API
-t, --telemetry-portTELEMETRY_PORToffPort of /metrics and the health probes
--cleanupDrop the publication and replication slot, then exit

Hump looks for the config in this order: --config, --config-file, CONFIG, CONFIG_FILE, stdin.

⁠Health probes and metrics

With TELEMETRY_PORT set, the telemetry server exposes:

  • /metrics: Prometheus metrics
  • /health/startup, /health/ready, /health/live: startup, readiness and liveness probes

The image has no shell or curl, so a Docker HEALTHCHECK that runs a command will not work. Point Kubernetes HTTP probes or an external checker at these endpoints instead.

⁠Configuration

The config lists tables and the sinks their changes go to. Reusable sink presets, table groups, per-operation filters, retry and failure policies, headers and payload templates are covered in the documentation⁠. The template functions and render context are in docs/template.md⁠.

⁠Verifying the image

The release workflow call-docker-publish.yml⁠ signs the multi-arch index digest with keyless cosign (Sigstore, GitHub OIDC). It also pushes two attestations to the registry: GitHub build provenance (SLSA Build L3) and an SPDX SBOM. BuildKit attaches its own SBOM and provenance too. Trivy rescans the published image right after the push.

Check the signature:

cosign verify docker.io/humpcdc/hump@sha256:<digest> \
  --certificate-identity-regexp '^https://github\.com/hump-cdc/hump/\.github/workflows/call-docker-publish\.yml@refs/tags/v' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Check the build provenance. --signer-workflow accepts only attestations signed by the release workflow. Add --source-ref refs/tags/v<version> to pin the release tag as well:

gh attestation verify oci://docker.io/humpcdc/hump@sha256:<digest> \
  --repo hump-cdc/hump \
  --signer-workflow hump-cdc/hump/.github/workflows/call-docker-publish.yml \
  --deny-self-hosted-runners

To check the SBOM attestation, run the same command with --predicate-type https://spdx.dev/Document/v2.3.

⁠License

MIT⁠. Report security issues as described in SECURITY.md⁠.

Tag summary

Content type

Image

Digest

sha256:ddb955fd1…

Size

11.3 MB

Last updated

3 days ago

docker pull humpcdc/hump