> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bytebase.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Organize SQL Server Availability Groups in Bytebase

With Always On Availability Groups, a Bytebase **Instance** is the group, not the server. A server hosts replicas of many groups and the primary moves on failover, so an instance registered on a server name stops working the first time its group fails over. The steps below build this mapping.

| SQL Server                 | Bytebase                                                                 |
| -------------------------- | ------------------------------------------------------------------------ |
| Availability Group         | **Instance**, with the listener as host, limited with **Sync Databases** |
| The group's tier           | **Environment** on the instance                                          |
| Readable secondary replica | Read-only connection with `applicationintent=ReadOnly`                   |
| Application                | **Project**                                                              |

For two applications on a production tier that share the same servers, the mapping looks like this:

<img src="https://mintcdn.com/dbx/e38sLpGBxlS0HuGg/content/docs/tutorials/sql-server-availability-groups/topology.svg?fit=max&auto=format&n=e38sLpGBxlS0HuGg&q=85&s=36f84b47ceec779f003ea61e611f03d9" alt="Availability group topology: each application is a project, each group is one Bytebase instance on its listener, and both groups span both servers" width="720" height="804" data-path="content/docs/tutorials/sql-server-availability-groups/topology.svg" />

## Scenario

Two applications, `orders` and `billing`, each with a dev group and a production group. The groups of both applications share the same servers.

| Group             | Listener            | Databases                | Servers                  |
| ----------------- | ------------------- | ------------------------ | ------------------------ |
| `ag-orders-dev`   | `orders-dev.corp`   | `orders`, `orders_audit` | `srv-dev1`, `srv-dev2`   |
| `ag-billing-dev`  | `billing-dev.corp`  | `billing`                | `srv-dev1`, `srv-dev2`   |
| `ag-orders-prod`  | `orders-prod.corp`  | `orders`, `orders_audit` | `srv-prod1`, `srv-prod2` |
| `ag-billing-prod` | `billing-prod.corp` | `billing`                | `srv-prod1`, `srv-prod2` |

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 follows `orders`. Each of its groups becomes an instance with these settings:

| Instance      | Host               | Environment | Sync Databases           |
| ------------- | ------------------ | ----------- | ------------------------ |
| `orders-dev`  | `orders-dev.corp`  | Dev         | `orders`, `orders_audit` |
| `orders-prod` | `orders-prod.corp` | Prod        | `orders`, `orders_audit` |

Create `orders-prod`:

1. Navigate to **Instances** and click **+ New Instance**. Select **SQL Server**.
2. Set **Name** to `orders-prod`, **Environment** to `Prod`, and **Host** to `orders-prod.corp` with the listener's port. Enter a login that exists on every replica; logins are server-scoped and do not fail over with the group.
3. Under **Extra Parameters**, add `multisubnetfailover` with the value `true`. Skip this if the group is on a single subnet.
4. Turn off **Sync all databases** under **Sync Databases**, then add `orders` and `orders_audit`. The listener lands on whichever server holds the primary, and that server also holds `billing` as a secondary of the other group. Without this limit, Bytebase syncs `billing` too.
5. Click **Test Connection**, then **Create**.

The instance lists exactly two databases. Repeat for `orders-dev`.

### Read-only connection

An instance can carry a second, [read-only connection](/get-started/connect/overview#read-only-connections). 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 with `applicationintent=ReadOnly` routes those reads to a readable secondary.

<img src="https://mintcdn.com/dbx/e38sLpGBxlS0HuGg/content/docs/tutorials/sql-server-availability-groups/routing.svg?fit=max&auto=format&n=e38sLpGBxlS0HuGg&q=85&s=5789e13c839aa0ff21b3142f643e896a" alt="Both connections target the listener; the admin connection lands on the primary and the read-only connection is routed to a readable secondary" width="720" height="502" data-path="content/docs/tutorials/sql-server-availability-groups/routing.svg" />

Add it to `orders-prod`:

1. On the instance page, click **+** next to **Connection Info**.
2. Use the same host and port, with a login that has read access.
3. Under **Extra Parameters**, add `applicationintent` with the value `ReadOnly`.
4. Save.

Bytebase does not add the parameter on its own, and results from a secondary can lag the primary. Repeat for `orders-dev`.

## Step 2 - Create the project

1. Click **Select Project** in the top bar, then **New Project**. Name it `orders`.
2. Navigate to **Databases** in the workspace, select `orders` and `orders_audit` on both instances, and click **Transfer** to move them into the project.

The project now holds:

| Database       | Instance      | Environment |
| -------------- | ------------- | ----------- |
| `orders`       | `orders-dev`  | Dev         |
| `orders_audit` | `orders-dev`  | Dev         |
| `orders`       | `orders-prod` | Prod        |
| `orders_audit` | `orders-prod` | Prod        |

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

1. In the `orders` project, create a schema change on both `orders` databases, against any table they share. This example uses `dbo.customer`:

   ```sql theme={null}
   ALTER TABLE dbo.customer ADD region NVARCHAR(50) NULL;
   ```

2. The rollout shows a Dev stage and a Prod stage. Run the Dev stage, then the Prod stage.

3. Open **SQL Editor**, connect to `orders` on `orders-prod`, and switch to the read-only data source. Run:

   ```sql theme={null}
   SELECT @@SERVERNAME, COL_LENGTH('dbo.customer', 'region');
   ```

`@@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:

```sql theme={null}
ALTER AVAILABILITY GROUP [ag-orders-prod] FAILOVER;
```

The primary is now on `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](/tutorials/manage-databases-with-terraform) creates instances from code, and its `sync_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.

| Layout                           | Instance host | Sync Databases                  |
| -------------------------------- | ------------- | ------------------------------- |
| Databases in no group            | Server name   | Those databases only            |
| One group holding every database | Listener      | Leave **Sync all databases** on |

## Summary

One principle decides the whole mapping: a Bytebase instance is an endpoint that always reaches the writable copy, limited to the databases that fail over together. With Availability Groups that endpoint is the listener and that set of databases is the group. Once the instance is right, the rest follows without further decisions: the environment is the group's tier, the project is whatever the groups are organized by, and the read-only connection is the same listener with a read-only intent.

The connection settings are documented under [SQL Server Availability Groups](/get-started/connect/overview#sql-server-availability-groups), and the resource model under [Organize Resources](/onboarding/organize-resources).
