Sync customers into the Boom CDP straight from your own PostgreSQL or MySQL database.
Boom can read customers directly from your operational database and keep them
in sync with the CDP — no CSV exports, no custom ETL. You point Boom at a
read-only user on your database, write a SELECT that returns the records you
care about, and map the columns to people, custom objects, and relationships.This guide walks an admin through the whole setup:
1. Create a read-only user
A least-privilege database user Boom connects as.
2. Connect your database
Enter host, credentials, and TLS — then test the connection.
3. Define what to sync
Write the query and map columns to customer data.
Troubleshooting
Decode the most common connection errors.
You connect through the Boom dashboard, not the API. Open
app.useboom.ai and go to Integrations in the
sidebar. The whole flow needs an admin who can create a database user and
knows the database’s host, port, and TLS setup.
PostgreSQL or MySQL. Connecting and testing works for both today;
automated syncing currently runs for PostgreSQL (see
Current limitations).
A read-only user
Boom only ever reads. Create a dedicated user with SELECT-only grants —
snippets below.
Network reachability
Boom must be able to reach the database — either a publicly reachable
host, or a SSH bastion (jump host) in front of a private database.
TLS
The connection is encrypted in transit. Require is the default; use
Verify full to also validate the server certificate against a CA.
What Boom stores and how. Boom stores the connection details and
credentials encrypted at rest (AES-256-GCM). The password is never returned
to the browser after you save it, and credentials never appear in logs. Boom
opens short-lived, read-only connections — it does not write to your database.
Give Boom a dedicated user with read-only access to the tables you want to
sync. This keeps the blast radius small and makes the connection easy to audit
and revoke. The same snippets are available in-product, next to the connection
form.
CREATE USER boom_readonly WITH PASSWORD 'your-strong-password';GRANT CONNECT ON DATABASE your_db_name TO boom_readonly;GRANT USAGE ON SCHEMA public TO boom_readonly;GRANT SELECT ON ALL TABLES IN SCHEMA public TO boom_readonly;ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO boom_readonly;
CREATE USER 'boom_readonly'@'%' IDENTIFIED BY 'your-strong-password';GRANT SELECT ON your_db_name.* TO 'boom_readonly'@'%';FLUSH PRIVILEGES;
Replace your_db_name and your-strong-password with real values. The
final ALTER DEFAULT PRIVILEGES line (PostgreSQL) makes sure Boom can also
read tables created after you grant access — handy when your schema grows.
To scope Boom to specific tables instead of the whole schema, grant SELECT
on just those tables.
In the dashboard sidebar, open Integrations, then Add integration.
Pick PostgreSQL or MySQL — the default port fills in automatically
(5432 for PostgreSQL, 3306 for MySQL).
2
Fill in the connection details
Field
What to enter
Name
A label you’ll recognize later, e.g. Production Postgres.
Database type
PostgreSQL or MySQL.
Host
The database’s DNS name or public IP, e.g. db.example.com. Must be publicly reachable in Direct mode (see SSH tunnel for private databases).
Port
Defaults to the database’s standard port; change it if yours differs.
The user’s password. Stored encrypted; never shown again after saving.
3
Choose an SSL mode
Under Security, pick how strictly Boom verifies the server’s
certificate. TLS is always used — this controls verification:
Mode
Behavior
When to use
Require
TLS required; the server certificate is not verified.
Default. Good for most managed databases (Neon, RDS, Cloud SQL).
Verify full
TLS required and the server certificate verified against a CA certificate you supply.
Highest assurance — see the note below.
Disable
No TLS.
Not recommended — only for local development.
Avoid Disable for any database holding real customer data — the
connection would be unencrypted in transit.
Verify full requires a CA certificate, and uploading one isn’t
available in the connection form yet. Use Require — which still
encrypts the connection — unless you specifically need certificate
verification; if you do, contact support to
set it up. See Current limitations.
4
Pick a connection method
Direct (default) connects straight to the host you entered. If your
database is locked inside a private network, choose SSH Tunnel (bastion)
instead — see below.
5
Test the connection
Click Test connection. Boom opens a short, read-only connection, checks
TLS, and reads the database version. On success you’ll see the verified
version (e.g. Connection verified · PostgreSQL 16.2). On failure you get a
plain-language message and a hint — see Troubleshooting.
6
Save
Click Save. Boom re-runs the test automatically as it saves, so the
connection lands with the right status without a separate click — an
Active badge if it passed, or Error (with the message) if it didn’t.
A failed connection is still saved, so you can fix the details and retest
from its page.
If your database has no public endpoint, Boom can reach it through an SSH
bastion (a jump host that is publicly reachable and can connect onward to
the database). Choose SSH Tunnel (bastion) as the connection method and fill
in the bastion fields:
Field
What to enter
Bastion host
The jump host’s public address, e.g. bastion.example.com.
Bastion port
SSH port, usually 22.
Bastion username
The SSH user on the bastion.
SSH private key
A private key Boom authenticates with. Use a dedicated, least-privilege key that can only forward ports.
Key passphrase
Optional — only if your key is passphrase-protected.
Pinned host key
Optional — paste the bastion’s SHA256:… fingerprint to pin it, or pin it after the first successful test.
In tunnel mode you still enter the database host and port as the bastion
sees them (often a private address like 10.0.x.x or an internal DNS name) —
Boom dials the bastion, then forwards onward to the database.
If the test fails with a could not reach the database host/port error, the
bastion is likely refusing TCP port forwarding, or it can’t reach the
database from its own network. Confirm the bastion’s SSH server allows
forwarding — set AllowTcpForwarding yes in /etc/ssh/sshd_config and reload
sshd — and that the bastion can itself connect to the database host and port.
Host-key pinning protects against a man-in-the-middle on the bastion.
Leave it blank to trust the key on first connect, then pin the fingerprint
Boom shows you. Once pinned, a changed key is refused until you re-pin it.
A connection on its own doesn’t move any data — it just proves Boom can reach
the database. To actually sync, you add one or more data sources. A source
is a SELECT query plus a mapping that tells Boom how to turn each row into a
person, a custom object, or a relationship.Open the Data sources area for your connection to build them.
A link between two records (typically a many-to-many join table).
An edge only — no record of its own.
The fastest way to start: pick a table Boom discovered and choose Draft as
person or Draft as custom object. Boom writes a starter SELECT for you,
which you can then refine.
Every source query must project three columns, whatever else it returns.
They’re how Boom keeps the sync correct and incremental:
Column
Purpose
external_id
A stable, unique id for the row (usually the primary key). Boom uses it verbatim to match rows on every sync, so the same record updates instead of duplicating.
source_updated_at
A last-modified timestamp. Boom remembers the latest value it has seen and, on each run, pulls only rows changed since then — that’s what makes ongoing syncs cheap.
source_deleted
A boolean. When true, Boom marks the synced record as removed while keeping its last-known data.
SELECT id AS external_id, updated_at AS source_updated_at, (deleted_at IS NOT NULL) AS source_deleted, email, phone, plan_tier, signup_dateFROM public.users;
If you project source_deleted from a soft-delete column, don’t also filter
those rows out (e.g. WHERE deleted_at IS NULL) — the deleted rows would
never reach Boom, so source_deleted would always be false and deletions
would never sync. Either include them (as above) so Boom sees the flag flip,
or, if you only ever want live rows, drop source_deleted from the projection
and let Boom’s daily reconciliation detect removals.
Boom runs your query in a read-only transaction and previews only a small
number of rows while you’re editing, so it’s safe to iterate. Make sure
external_id is unique per row — for a join table without a single-column
key, project one (e.g. left_id || ':' || right_id AS external_id).
Map at least one of email or phone so Boom can reach the customer.
These populate the person’s contact channels, which is what lets
event-triggered journeys message them.
2
Attributes
Map any other columns to attributes and give each a type (String,
Number, Date, Boolean). Attributes become filterable fields in Segments, so
the right type matters — a Date compares chronologically, a Number
numerically.
3
Descriptions (optional but recommended)
Add a short description per column to build a data dictionary. Describe
with AI can draft these for you — it only ever sees column names and
types, never row values. Descriptions are saved to your attribute catalog
and survive future syncs.
Relationships connect a person to an object, or one object to another (a
customer who placed an order; an order that has_line_item). On a Person or
Custom object source, add a relationship by pointing a foreign-key column at
another source:
Field
Meaning
FK column
The column on this row that holds the other record’s id.
Kind
person_to_co (person → object) or co_to_co (object → object).
Target resource
The other source the id refers to.
Role
A label for the link, e.g. placed, has_line_item.
For a many-to-many join table, add it as its own Relationship source.
Its query projects left_external_id and right_external_id (plus the three
required columns) and emits one edge per row — useful when the link itself
carries data (a quantity, a redeemed-at timestamp), which becomes the edge’s
attributes.
Sync the target of a relationship before the source that points at it
(e.g. orders before line items). If an edge references a record Boom hasn’t
seen yet, Boom tolerates it and re-links automatically on a later run — so
ordering is a convenience, not a hard requirement.
Once a source is enabled, Boom keeps it in sync automatically — roughly
every 15 minutes — using source_updated_at to pull only what changed since
the last run:
First sync backfills all matching rows. Large tables backfill in batches
across several runs; you don’t need to do anything.
Ongoing syncs are incremental and usually pull a handful of rows (or
none).
Deletions are reconciled by a daily check, and rows flagged with
source_deleted = true are marked removed as they sync.
Use the Enabled toggle on a source to pause syncing without deleting your
mapping.
When a test or sync fails, Boom shows a plain-language message. Here’s what the
common ones mean.
Authentication failed. Check username and password.
The username or password is wrong, or the user isn’t allowed to connect
from Boom’s network. Re-run the Step 1
grants, confirm the password, and (MySQL) make sure the user is created
with a host pattern that allows remote connections, e.g. 'boom_readonly'@'%'.
Authentication succeeded but the user lacks permission.
Boom connected but can’t read a table. Grant SELECT on the tables (or
schema) you’re syncing — see the read-only snippets above.
Host not found. Check the hostname spelling.
The hostname didn’t resolve. Verify the host, and that it’s a public DNS
name or IP (in Direct mode). For private databases, use an
SSH bastion.
Connection timed out.
Boom reached the network but got no response in time — usually a firewall
or security group blocking the port. Allow inbound access to the database
port from Boom, or front the database with a bastion.
Connection refused.
The host is reachable but nothing is listening on that port. Check the port
number and that the database is running and accepting TCP connections.
TLS handshake failed.
The encrypted handshake didn’t complete. Check the SSL mode — if you
chose Verify full, the server’s certificate must be valid and match a
trusted CA. Try Require to confirm the rest of the connection works.
Cannot connect to private, loopback, or link-local addresses.
In Direct mode Boom refuses private (10.x, 172.16–31.x,
192.168.x), loopback (127.x), and link-local (169.254.x) addresses
for security. Use a publicly reachable host, or connect through an
SSH bastion.
Database '…' does not exist on this server.
The Database name is wrong, or the read-only user can’t see it. Confirm
the exact name and that the user has CONNECT on it.
Could not reach the database host/port (SSH tunnel).
The bastion connected but couldn’t forward to the database. Make sure the
bastion allows TCP forwarding and can itself reach the database host and
port. Double-check the database host/port as the bastion sees them.
The database-source feature is live, with a few capabilities still on the way.
Plan around these:
No static egress IP to allowlist (yet). Boom currently connects from a
shared, variable IP range, so it can’t give you a single fixed IP to add to
your firewall. If your database can’t be exposed publicly, use an
SSH bastion. Static egress IPs are
planned.
No AWS PrivateLink. Private connectivity is via SSH bastion today;
PrivateLink is not available yet.
No customer-managed keys (BYOK). Credentials are encrypted with a
Boom-managed key. Bring-your-own-key is not available yet.
Automated sync is PostgreSQL-first. You can create and test MySQL
connections today, but scheduled syncing currently runs for PostgreSQL
sources. MySQL syncing is on the roadmap.
Verify full needs a CA certificate that the form can’t yet accept. The
connection form doesn’t expose a CA-certificate field, so Verify full
isn’t selectable end-to-end yet. Use Require (still encrypted) for now, or
contact support if you need full certificate verification.