---
title: "Heroku Connect Migration Guide: Cutover Runbook | Stacksync"
description: "Move off Heroku Connect with a parallel run and a schema swap. Keep the salesforce schema, sfid and __c columns, and replace the _hc_ status columns."
canonical: https://www.stacksync.com/heroku-connect-alternative/migration-guide
last_modified: 2026-09-29
---

→ MIGRATION GUIDE

# Heroku Connect migration guide *Keep the salesforce schema, sfid and \__c columns, and cut over with a schema swap.*

To migrate off Heroku Connect with minimal downtime, run Stacksync in parallel in a second Postgres schema built with the same tables and column names, reconcile it against the live salesforce schema, repoint your triggers, then drain Heroku Connect's pending writes and rename the two schemas during a short write freeze. Your app keeps querying the salesforce schema, and your exported Heroku Connect mappings become the checklist for the new field mappings.

- For engineering leads, backend engineers and DBAs
- Salesforce ↔ PostgreSQL, two-way sync

→ AT A GLANCE

## What changes and what stays when you leave Heroku Connect

| Question | Short answer |
| --- | --- |
| Can I keep the salesforce schema name? | Yes. Seed a salesforce_2 schema for the parallel run and rename it to salesforce at cutover. A schema rename needs a Stacksync configuration update, so plan it with the team. |
| Do \__c suffixes and the sfid column survive? | They can. Create the new tables with the same column names and map each Salesforce field, including the record ID, to its column. Confirm column naming on your demo and test app inserts in staging. |
| Can I import my Heroku Connect mappings? | Ask the Stacksync team on your scoping call. Either way, the exported mapping JSON lists every object and field the new field mappings must cover. |
| Do my Postgres triggers keep working? | Only after you move them. This guide syncs into new tables, so create your triggers on those tables, disabled, and enable them inside the swap transaction. |
| What replaces \_hc_lastop and \_hc_err? | The Stacksync issues dashboard for sync status and failed records, or your own columns maintained by triggers. |
| How do I avoid downtime? | Seed a second schema in parallel and reconcile it. During a short write freeze, drain Heroku Connect's pending writes, pause both syncs and rename the schemas in one transaction. |
| Can I leave Heroku Postgres later? | Yes. Plan it as a connector change: copy the data with dump and restore, then connect the new host with the PostgreSQL connector. |

→ Before you start

## Decide what moves, and in what order

A Heroku Connect exit usually bundles two projects: replacing the Salesforce sync, and sometimes moving the database off Heroku Postgres. Run them one at a time. Swap the sync first while the database stays put, then move the database once the new sync has run cleanly in production. Changing both in one window doubles the places a missing row can hide.

### Check your tables against the connector requirements

The Stacksync Postgres connectors need each synced table to have a single-column primary key with a database-generated default. The connectors don't support composite primary keys. Heroku Connect tables already use a serial `id` column as their primary key, so the tables you recreate from your mappings qualify as they are. Check any table your team added by hand.

### Know which connector you start on

Heroku Postgres does not grant the replication rights that logical replication needs. For that reason Stacksync connects to Heroku Postgres through the separate Postgres Heroku connector, which captures changes with triggers. Other Postgres hosts, such as Amazon RDS, Aurora or Render, use the PostgreSQL connector with logical replication. That split matters later if you leave Heroku; the last section covers it.

### Inventory what your app reads

- Every Heroku Connect connection and the objects in each mapping. Large orgs often run several connections with overlapping objects.
- Queries, ORM models and views that reference the `salesforce` schema, `__c` columns and `sfid`.
- Trigger functions that read from or write to the `salesforce` schema.
- Code that reads `_hc_lastop`, `_hc_err` or other `_hc_` columns.
- Foreign keys from your own tables into synced tables, and whether they store `sfid` or the local `id`.
- Indexes, grants and row-level security policies on synced tables.

That list becomes your test plan. Each item maps to a section of this guide.

→ Step 1 · Mappings

## Carry your Heroku Connect mappings over

Your Heroku Connect mappings already list every object and field your app depends on, so start from them rather than from a blank sync. Export the mapping configuration from each Heroku Connect connection as JSON, and take a schema-only dump of the target database. Stacksync proposes field mappings that you review and change, and field names can differ as long as the types are compatible. The export becomes your checklist: every field it maps needs a matching column in the new tables and a matching field mapping in Stacksync. Ask the Stacksync team on your scoping call whether they can load the exported mappings for you.

### What to bring

- The exported JSON for every Heroku Connect connection you plan to retire.
- A schema-only dump of the target database, for example `pg_dump --schema-only --schema=salesforce`.
- A list of objects and fields you no longer use. Leaving them out makes the backfill smaller.

### Clean up while you carry them over

Keeping your configuration exactly as it is goes fastest. Many teams use the moment to drop fields nobody reads and to consolidate connections that sync the same objects into different databases. Each change you make here is a change your app has to absorb, so decide it before the backfill rather than after.

Review the mappings with the Stacksync team before the first backfill, and book time for it. Pro and higher plans include the Management API and config as code for maintaining the configuration afterwards; see [pricing](https://www.stacksync.com/pricing) for plan details.

→ Step 2 · Settings

## Lock these settings before the first backfill

Don't assume Stacksync's defaults match Heroku Connect's. If the backfill runs on settings nobody checked, you can end up with a schema your app can't query and a second backfill to fix it. Agree on each row below, in writing, before any data moves.

| Setting | Heroku Connect today | What to set |
| --- | --- | --- |
| Schema name | `salesforce` | Seed into `salesforce_2` for the parallel run, then rename to `salesforce` at cutover. |
| Column names | Lowercase API names with the `__c` suffix on custom fields | Map each field to a column with the same name, so `amount__c` stays `amount__c`. Confirm column naming on your demo rather than relying on defaults. |
| Salesforce ID | `sfid`, written back after Salesforce creates the record | Map the Salesforce ID to `sfid`, and test in staging that rows your app inserts get their `sfid`. |
| External IDs | External ID fields used for upserts and lookups | Map the same external ID fields, and keep them populated on every row your app creates. |
| Field selection | Fields listed in each mapping | The fields from your exported mappings, minus anything you dropped in the cleanup. |
| New Salesforce fields | Added to a mapping by hand | Agree how new fields reach Postgres, so an admin's change in Salesforce doesn't alter a table your app reads mid-cutover. |

Write direction is a per-field decision too. Stacksync runs as a two-way sync, and you can restrict write-back on fields Postgres should never change in Salesforce, such as formula fields or IDs owned by another system.

→ Step 3 · App code

## Replace \_hc_lastop, \_hc_err and the other \_hc\_ columns

Heroku Connect stores sync state inside every mapped row. `_hc_lastop` holds the last operation or status, such as `PENDING`, `SYNCED` or `FAILED`, and `_hc_err` holds the error text when a write to Salesforce fails. Apps built on Heroku Connect often poll those columns to retry writes or show a sync badge to users. Stacksync reports sync status and failed records in its [issues dashboard](https://www.stacksync.com/two-way-sync/issue-management) instead of in your business rows, so code that reads `_hc_` columns needs one of two replacements.

### Option A: move the logic to the issues dashboard

Stacksync alerts on sync issues, and your team retries, reverts or ignores each failed record from the dashboard. The app stops tracking sync state itself. Your codebase shrinks, and the option fits best when operations staff handle sync failures and end users never see them.

### Option B: keep your own status columns

If app logic depends on a per-row flag, add your own columns and maintain them with Postgres triggers. For inserts, a trigger can set `sync_pending = true` when your app creates a row and clear it once the row's `sfid` is filled in, which you confirm in staging. Keep the columns out of the field mappings so they never sync to Salesforce.

### Budget for testing, most of all

A partner agency's estimate for one app built on `_hc_` columns was under two to three weeks of rework, mostly testing. Search the codebase for `_hc_` early, because that count drives the estimate more than anything else in this guide.

If you keep `_hc_err` history for audits, note that log retention in Stacksync depends on your plan (see [pricing](https://www.stacksync.com/pricing)). Export the issues you need to keep, or write failures to your own table.

For how Heroku Connect itself surfaces these errors, see the [Heroku Connect architecture deep dive](https://www.stacksync.com/blog/heroku-connect-architecture-deep-dive-limits-revealed).

→ Step 4 · Cutover

## The cutover runbook: rehearse, reconcile, swap

This guide syncs into new tables rather than taking over the tables Heroku Connect maintains. On Heroku Postgres, Stacksync installs its own change-capture triggers, and two sync engines writing to the same rows invite duplicates and lost updates. The runbook runs twice: once in staging against a Salesforce sandbox, then in production against your production org. Heroku Connect keeps serving your app until the write freeze, so you can stop at any point before the swap and lose nothing.

### Rehearse in staging

Staging runs against a Salesforce sandbox, so both schemas hold the same org and every count can match. Production stays on Heroku Connect the whole time.

1. #### Point both syncs at the same Salesforce sandbox

   In staging, let a Heroku Connect connection fill the salesforce schema from a Salesforce sandbox, and authorize Stacksync against that same sandbox with the integration user you plan to use in production. Give that user Author Apex and Customize Application (or Modify All Data) so Stacksync can use Apex triggers, and note which objects fall back to polling. Both schemas then describe one org, so the reconciliation compares like with like.
2. #### Create an empty salesforce_2 schema

   Create a second schema, salesforce_2, with the same tables and column names as salesforce, built from a schema-only dump without Heroku Connect's own triggers and \_trigger_log tables. Keep each table on a single-column primary key with a database-generated default, such as the serial id Heroku Connect tables already use, and add a unique index on sfid.
3. #### Seed salesforce_2 from the sandbox

   Run the initial backfill into salesforce_2 while Heroku Connect keeps filling the salesforce schema. Seed into empty tables, because Stacksync does not merge duplicate records that already exist on both sides.
4. #### Reconcile salesforce against salesforce_2

   Compare row counts object by object between the two schemas, then diff a sample of rows field by field, matching on sfid. Chase every gap: archived records and objects your integration user cannot fully see are the usual causes.
5. #### Build the new tables' dependencies, with app triggers disabled

   On the salesforce_2 tables, create the indexes, grants, row-level security policies and views your app relies on, and grant USAGE on the salesforce_2 schema to your app's role. Create your app's triggers too, then disable each one by name with ALTER TABLE ... DISABLE TRIGGER, so they don't fire on Stacksync's writes during the parallel run. Don't use DISABLE TRIGGER ALL or USER: on Heroku Postgres, Stacksync captures changes with its own triggers on these tables. Script every statement, because production repeats them.
6. #### Soak the parallel run

   Let both syncs run side by side through a full business cycle, including month-end jobs, batch imports and the edge cases your app depends on. Check the issues dashboard daily and fix mapping errors before you schedule production.
7. #### Rehearse the cutover

   Run the production cutover steps against staging, from the write freeze to lifting it, and time each one. Test your app's real write paths on the swapped schema before you book the production window.

### Run in production

Production repeats the seed and the reconciliation against your production org, then cuts over during a short write freeze. Heroku Connect serves your app until the freeze.

1. #### Connect the production org and seed salesforce_2

   Create salesforce_2 in the production database with the same tables, connect Stacksync to your production org with the same mappings and integration user, and run the backfill. Heroku Connect keeps serving your app from the salesforce schema throughout.
2. #### Reconcile production against production

   Compare row counts and diff sampled rows between salesforce and salesforce_2 in the production database, matching on sfid, until the two schemas agree.
3. #### Prepare the new tables

   Run the script from staging on the production salesforce_2 tables: indexes, grants, USAGE on the schema, row-level security policies, views, and your app's triggers created disabled. Stage the statements that repoint foreign keys and views in other schemas, which run inside the swap.
4. #### Freeze app writes and drain Heroku Connect

   Stop app writes to the salesforce schema. Wait until salesforce.\_trigger_log has no entries in NEW, PENDING or BULKSENT state and no mapped row shows PENDING in \_hc_lastop. Fix and resend, or export, every row marked FAILED: those writes never reached Salesforce and would stay behind in salesforce_old. Once Stacksync has carried the last writes from Salesforce into salesforce_2, pause the Heroku Connect connection.
5. #### Pause the Stacksync sync

   Pause the sync in the Stacksync dashboard so nothing writes to salesforce_2 while the schema names change.
6. #### Swap the schemas in one transaction

   In a single transaction, rename salesforce to salesforce_old and salesforce_2 to salesforce, enable your app's triggers on the new tables, repoint foreign keys from your own tables at the new tables on sfid or an external ID, and recreate views that live in other schemas. Your app then reads the tables Stacksync maintains, under the same schema name.
7. #### Update the sync configuration, then resume the sync

   Renaming a schema requires a Stacksync configuration update. Make the change with the Stacksync team during the window, confirm with them beforehand whether your setup needs a resync after the rename, and resume the Stacksync sync once the configuration names the new schema.
8. #### Lift the freeze and keep a rollback path

   Re-enable app writes, watch the issues dashboard and your app logs, and keep salesforce_old and the paused Heroku Connect connection until you have closed out a full cycle in production.

### Reconciliation query

Run one count per object and compare, inside one database. In staging both schemas come from the sandbox; in production both come from the production org. Generate the statement from your mapping list so no object gets skipped.

```
SELECT 'contact' AS object,
       (SELECT count(*) FROM salesforce.contact)   AS heroku_connect,
       (SELECT count(*) FROM salesforce_2.contact) AS stacksync;
```

When counts differ, check two causes before you suspect the sync. Archived or soft-deleted records can be present on one side only. And if the integration user can't see every row, Salesforce returns fewer rows without an error: a Profile query that returns one row instead of a hundred usually means the user lacks the View All Profiles permission.

### The swap itself

Postgres renames a schema as a catalog update, and wrapping both renames in one transaction means your app never sees a moment without a `salesforce` schema. Functions that name tables in their body, such as PL/pgSQL trigger functions, resolve against the new `salesforce` schema on their next call. Almost everything else is bound to the table, not its name, so it stays with the old tables in `salesforce_old` unless you move it:

- **Triggers** stay attached to the table they were created on. Create yours on the `salesforce_2` tables ahead of time, disabled by name, and enable them inside the swap. Enabled early, they would fire on every Stacksync write while the same triggers fire on the Heroku Connect tables, and each side effect would run twice.
- **Views** in other schemas keep reading the old tables. Recreate them inside the swap. Views you create inside `salesforce_2` move with it.
- **Foreign keys** from your own tables keep pointing at the old tables. Drop and re-add them inside the swap. Adding them `NOT VALID` keeps the lock short; validate them after the freeze lifts.
- **Indexes, table grants and row-level security policies** belong to each table, so create them on the `salesforce_2` tables before the swap. A schema's `USAGE` grant follows it through the rename, so grant it on `salesforce_2`.
- **The local `id` values** differ between the two schemas, because each sync assigned its own serial numbers. Join and reference on `sfid` or an external ID, never on `id`. If your own tables store the local `id` as a foreign key, add an `sfid` column to them and backfill it before the production window.

```
BEGIN;
ALTER SCHEMA salesforce RENAME TO salesforce_old;
ALTER SCHEMA salesforce_2 RENAME TO salesforce;
-- One statement per trigger, foreign key and outside view; names are examples.
ALTER TABLE salesforce.account ENABLE TRIGGER account_after_write;
ALTER TABLE public.invoice DROP CONSTRAINT invoice_account_fk;
ALTER TABLE public.invoice ADD CONSTRAINT invoice_account_fk
  FOREIGN KEY (account_sfid) REFERENCES salesforce.account (sfid) NOT VALID;
COMMIT;
```

Pause both syncs before the rename. Heroku Connect completes pending operations before it pauses, and while paused it still records database changes in its trigger log, which is why the freeze and the drain come first. The Stacksync sync stays paused until its configuration names the new schema.

### The fallback: delete and recreate

If a parallel schema won't work for you, for example because the database can't hold a second copy of the data, the alternative is to drain and pause Heroku Connect, empty the tables and let Stacksync backfill them in place. Downtime then grows with row count, so ask the Stacksync team for an estimate based on your volumes before you pick this path.

→ Step 5 · Backfill

## Plan a large backfill before it starts

Small orgs backfill without much planning. Large ones need a budget. The lessons below come from a proof of concept on a large org, and they apply to any org where the initial load runs into hundreds of millions of rows.

### Budget the Bulk API allocation

Salesforce caps Bulk API usage per rolling 24-hour window, and your backfill shares that allocation with every other bulk job in the org: data loaders, ETL exports, backup tools. Check the org's usage with your Salesforce admin, schedule the heaviest objects for quiet hours, and leave objects with zero rows out of the first load so they don't spend calls for nothing.

### Reconcile before deltas start

Confirm row counts per object after the backfill and before you switch on ongoing change sync. A gap found at this point costs a targeted reload. The same gap found after cutover means an incident, with your app already reading the new tables.

### Fix permission gaps first

Most missing rows trace back to what the integration user can see. Check object and field permissions, access to archived records, and setup permissions such as View All Profiles before the load starts.

### Know how a large backfill is billed

Stacksync prices on records in sync and active syncs. Pro includes 1M records in sync, and volumes in the hundreds of millions fall under Enterprise, where pricing is custom. Leave unused objects and fields out of the mappings to keep the count down, and see [pricing](https://www.stacksync.com/pricing) for how records in sync are counted.

→ Step 6 · Data integrity

## Deletes, foreign keys and external IDs

### Protect rows your own tables depend on

If your app's tables hold foreign keys into synced tables, a record deleted in Salesforce can orphan rows in Postgres or fail on a constraint. Stacksync includes a Delete Protection option in its sync settings. Decide per sync whether Salesforce deletes should reach Postgres, turn protection on where your schema can't absorb them, and test a delete in staging to confirm the behavior you get matches the one you expect.

### Create parents before children

When your app inserts a parent and its children in the same transaction, the children reference a parent that Salesforce hasn't created yet. Keep the parent's external ID populated on the child row so the lookup can resolve once the parent exists, and test the case where both records arrive in the same batch.

### Keep external IDs stable

If your app generates its own identifiers, such as UUIDs, map them to an External ID field in Salesforce and keep them populated on every row your app creates. Rows without an external ID give the sync nothing to match on, and a retried insert can then create a duplicate in Salesforce. Stacksync doesn't merge duplicate records that already exist on both sides, so clean up existing duplicates before the backfill.

→ Step 7 · Access

## Set up the Salesforce integration user and database role

### Salesforce: a dedicated integration user over OAuth 2

Stacksync connects to Salesforce with OAuth 2. Create a dedicated integration user rather than connecting as a person, so access survives staff changes and every change the sync makes shows up under one name in Salesforce audit history. Scope the user with permission sets: read and edit on each synced object and field, plus the setup permissions your reconciliation found missing.

Real-time change detection needs more. Stacksync uses Apex triggers in Salesforce where it can, which requires API Enabled plus Author Apex and Customize Application (or Modify All Data) on the integration user. Without those permissions, or on objects that can't take Apex triggers, Stacksync falls back to incremental polling, and those objects sync on a slower cycle. Check which mode each object runs in during the sandbox run, so the parallel-run comparison measures what production will do.

If your org restricts which connected apps users may authorize, a Salesforce admin has to approve the Stacksync app before the integration user can complete the OAuth flow. Plan that approval into the sandbox phase so it doesn't block production day.

### Postgres: a role with the documented permissions

Create a separate database role for Stacksync instead of reusing your app's role. The role needs the ownership and replication-related permissions listed for your connector in the [Stacksync Postgres documentation](https://docs.stacksync.com/two-way-sync/connectors/postgres), and access to the schemas it syncs, including `salesforce_2` during the parallel run. Grant the same role in staging and production so the cutover doesn't surface a permission error you never saw in testing.

Stacksync supports OAuth 2, SSH tunnelling, SSL certificates, IP allowlisting, VPN gateways and VPC peering for connections. See [security](https://www.stacksync.com/security) for the controls and compliance frameworks behind them.

→ Later · Database move

## Moving off Heroku Postgres after the sync cutover

Once Stacksync runs cleanly against Heroku Postgres, you can move the database to Amazon RDS, Aurora, Render or another Postgres host as a separate project. Plan it as a connector change. A new connection string alone won't do it.

### Copy the data with dump and restore

Heroku Postgres doesn't allow logical replication out to another host, so you can't stream the database across and switch over. Take a `pg_dump` of the database during a write freeze and restore it on the new host, or use the migration tooling your new provider offers. Verify row counts on the new host the same way you reconciled the backfill.

### Switch to the PostgreSQL connector

On Heroku, Stacksync uses the Postgres Heroku connector, which captures changes with triggers. On RDS, Aurora, Render and most other hosts, the PostgreSQL connector captures changes through logical replication. That requires `wal_level = logical` on the new server and replication rights for the Stacksync role, which managed providers usually expose through a parameter group or dashboard setting. Set both before the move.

Then point the sync at the new database with the Stacksync team, and ask before the window whether your volumes and configuration call for a resync. Keep the Heroku database read-only until the new host has run a full cycle.

Related: [Postgres Heroku connector](https://www.stacksync.com/connectors/postgres-heroku), [PostgreSQL connector](https://www.stacksync.com/connectors/postgresql), [Render Postgres](https://www.stacksync.com/connectors/render-postgres), [Aurora PostgreSQL](https://www.stacksync.com/connectors/aws-aurora-postgresql), and [Amazon RDS and Postgres Heroku](https://www.stacksync.com/integrations/amazon-rds-and-postgres-heroku).

FAQ

## Heroku Connect migration questions

What engineering teams ask when they plan the move off Heroku Connect.

### Can I import my existing Heroku Connect mappings into Stacksync instead of rebuilding them?

Ask the Stacksync team on your scoping call; this guide does not assume a direct import. Export the mapping JSON from each Heroku Connect connection and bring it with a schema-only dump of your target database: together they list every object, field and table the new sync must cover. Stacksync proposes field mappings that you review and change, so the export works as the checklist for that review rather than a rebuild from memory.

### Can a Heroku Connect replacement keep the salesforce schema, \__c columns and sfid so my app's queries don't change?

Yes, if you plan the tables and mappings for it. Create the new tables with the same column names, \__c suffixes and sfid included, map each Salesforce field to its column (field names can differ in a Stacksync mapping as long as the types are compatible), and confirm column naming on your demo rather than relying on defaults. Seed into a parallel salesforce_2 schema, then rename it to salesforce at cutover so your queries keep the same schema, table and column names.

### Is changing the database connection string enough to switch from Heroku Connect to Stacksync?

No. The path in this guide syncs into new tables, so you recreate the triggers and views that depend on the salesforce schema on those tables, replace any code that reads \_hc\_ columns, and swap schema names at cutover. Your queries and column names can stay the same, but the tables underneath them change.

### How do I migrate off Heroku Connect without downtime when Postgres triggers read from the salesforce schema?

Run Stacksync in parallel into a salesforce_2 schema while Heroku Connect keeps running, reconcile row counts, and create your triggers, views, indexes and grants on the new tables, with the triggers disabled. Then freeze app writes, wait until Heroku Connect's trigger log has nothing pending, and pause Heroku Connect and the Stacksync sync. In one transaction, rename salesforce to salesforce_old and salesforce_2 to salesforce, enable the triggers and repoint foreign keys. Update the schema name in the Stacksync configuration with the team, confirm beforehand whether your setup needs a resync, and resume the sync.

### Our app reads \_hc_lastop and \_hc_err from Postgres. What replaces those columns?

Stacksync reports sync status and failed records in its issues dashboard, where your team can retry, revert or ignore each issue. If app logic needs a per-row flag, add your own status columns and maintain them with Postgres triggers. A partner agency's estimate for one app built on \_hc\_ columns was under two to three weeks of rework, mostly testing.

### Can I run a new Salesforce sync in parallel with Heroku Connect before cutting over?

Yes, in two rounds. In staging, point Heroku Connect and Stacksync at the same Salesforce sandbox, seed an empty salesforce_2 schema and let both syncs run through a full business cycle. In production, connect Stacksync to your production org and seed salesforce_2 in the production database while Heroku Connect keeps serving your app from the salesforce schema, then reconcile the two. Keep your app's triggers on the new tables disabled until the swap so their side effects don't fire twice. You can stop at any point before the swap without losing data.

### Can I move from Heroku Postgres to AWS RDS or Render and keep the Salesforce sync?

Yes, as a separate step after the sync cutover. Heroku Postgres does not allow logical replication out, so copy the data with pg_dump and restore. On the new host Stacksync uses the PostgreSQL connector with logical replication, which needs wal_level set to logical and replication rights for the Stacksync role. Ask the Stacksync team whether your setup needs a resync when you repoint the sync.

### How is a large Heroku Connect backfill priced on Stacksync?

Stacksync prices on records in sync and active syncs. Pro includes 1M records in sync, and loads in the hundreds of millions of rows fall under Enterprise, where pricing is custom. Leaving unused objects and fields out of the mappings keeps the count down.

→ GET HELP WITH THE CUTOVER

## Run the migration with Stacksync engineers

Every Heroku Connect migration starts with a call: bring your exported mappings, your schema and your list of triggers, and the Stacksync team scopes the mappings, the parallel run and the swap with you. Teams that want Stacksync engineers to carry the implementation work can choose Managed Pro ($4,200 per month, billed annually). If you are still under a Heroku Connect contract, ask whether it qualifies for a buyout; eligibility and terms depend on your contract and are confirmed in writing.

[Book a migration call](https://www.stacksync.com/book-a-demo)

PRICING

### [Plans and records in sync](https://www.stacksync.com/pricing)

Stacksync prices on records in sync and active syncs. Compare plans before you size the backfill.

See pricing

SECURITY

### [Security and compliance](https://www.stacksync.com/security)

Connection options, encryption and the compliance frameworks your security review will ask about.

Review security

COMPARISON

### [Heroku Connect alternatives](https://www.stacksync.com/heroku-connect-alternative)

Stacksync, CData Sync, Skyvia, integrate.io and a custom CDC pipeline, compared on write-back, change detection, billing and API quota.

Compare

DEEP DIVE

### [Heroku Connect architecture and limits](https://www.stacksync.com/blog/heroku-connect-architecture-deep-dive-limits-revealed)

How Heroku Connect polls Salesforce, reads its trigger log and surfaces sync errors, and where those limits show up.

Read the deep dive

## Plan your Heroku Connect cutover. Bring your mappings. We'll bring the runbook.

[Book a demo](https://www.stacksync.com/book-a-demo) [Get started](https://app.stacksync.com/)
