For two applications on a production tier that share the same servers, the mapping looks like this:
Scenario
Two applications,orders and billing, each with a dev group and a production group. The groups of both applications share the same servers.
The groups are cut by application, which makes the project boundary obvious. If yours are cut differently, Step 1 is identical and only Step 2 changes.
Step 1 - Register one instance per group
The rest of the tutorial followsorders. Each of its groups becomes an instance with these settings:
Create
orders-prod:
- Navigate to Instances and click + New Instance. Select SQL Server.
- Set Name to
orders-prod, Environment toProd, and Host toorders-prod.corpwith the listener’s port. Enter a login that exists on every replica; logins are server-scoped and do not fail over with the group. - Under Extra Parameters, add
multisubnetfailoverwith the valuetrue. Skip this if the group is on a single subnet. - Turn off Sync all databases under Sync Databases, then add
ordersandorders_audit. The listener lands on whichever server holds the primary, and that server also holdsbillingas a secondary of the other group. Without this limit, Bytebase syncsbillingtoo. - Click Test Connection, then Create.
orders-dev.
Read-only connection
An instance can carry a second, read-only connection. SQL Editor queries and data exports use it instead of the admin connection. On an Availability Group with a read-only routing list, pointing this connection at the same listener withapplicationintent=ReadOnly routes those reads to a readable secondary.
orders-prod:
- On the instance page, click + next to Connection Info.
- Use the same host and port, with a login that has read access.
- Under Extra Parameters, add
applicationintentwith the valueReadOnly. - Save.
orders-dev.
Step 2 - Create the project
- Click Select Project in the top bar, then New Project. Name it
orders. - Navigate to Databases in the workspace, select
ordersandorders_auditon both instances, and click Transfer to move them into the project.
The environments come from the instances, so a change to
orders rolls to Dev first and Prod second with no further configuration. If your groups are cut by team instead of application, the project follows the team, and this is the only step that differs.
Step 3 - Verify the structure
-
In the
ordersproject, create a schema change on bothordersdatabases, against any table they share. This example usesdbo.customer: - The rollout shows a Dev stage and a Prod stage. Run the Dev stage, then the Prod stage.
-
Open SQL Editor, connect to
ordersonorders-prod, and switch to the read-only data source. Run:
@@SERVERNAME returns the secondary, and the column is there. The change ran once, on the primary, and reached the secondary through the group’s log stream.
To see why the listener matters, fail the group over. On the secondary replica, run:
srv-prod2. Create another change in the orders project and run the Prod stage. It succeeds without any edit to the orders-prod instance, because the listener now resolves to srv-prod2. Had the instance been registered with srv-prod1 as its host, this change would have failed: srv-prod1 is now a secondary and rejects writes.
Automate with Terraform
With many groups, registering each instance by hand does not scale. The Terraform provider creates instances from code, and itssync_databases attribute is the same Sync Databases setting from Step 1, so the inventory in the scenario table becomes the source for the configuration.
Other layouts
The mapping above assumes every database is in a group with a listener, and that a server hosts more than one group. Two common departures change only the host or the sync list:- Databases in no group do not fail over, so the server name is the right host. Register the server as its own instance and limit it to those databases, so the group databases on the same server stay with their listener instances.
- One group holding every database on the server means the listener and the server name see the same databases. Register the listener anyway, since it follows the primary, and leave the sync list unset.

