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

# Deploying Omni: Multiple instances (Prod/Stage)

> Set up and manage separate staging and production Omni instances using git follower mode — the recommended pattern for teams that require full environment isolation

export const categoryIcons = {
  'administration': 'lock',
  'api': 'terminal',
  'connections': 'database',
  'dashboards': 'table-columns',
  'embed': 'code',
  'errors': 'exclamation',
  'migration': 'angles-right',
  'modeling': 'wrench',
  'patterns': 'plus',
  'schedules & alerts': 'envelope',
  'visualizations': 'chart-column',
  'workbooks': 'book'
};

export const GuideSidebar = ({category, relatedLinks, updatedDate}) => {
  const [progress, setProgress] = React.useState(0);
  React.useEffect(() => {
    const sidebar = document.querySelector('.guide-sidebar');
    if (!sidebar) return;
    let container = sidebar.parentElement;
    while (container && !container.querySelector('.guide-header')) {
      container = container.parentElement;
    }
    if (container && !container.classList.contains('guide-page-layout')) {
      container.classList.add('guide-page-layout');
    }
  }, []);
  React.useEffect(() => {
    const handleScroll = () => {
      const scrollTop = window.scrollY;
      const docHeight = document.documentElement.scrollHeight - window.innerHeight;
      const scrollPercent = docHeight > 0 ? scrollTop / docHeight * 100 : 0;
      setProgress(Math.min(100, Math.max(0, scrollPercent)));
    };
    window.addEventListener('scroll', handleScroll, {
      passive: true
    });
    handleScroll();
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);
  const icon = category ? categoryIcons[category.toLowerCase()] || 'book' : 'book';
  return <aside className="guide-sidebar">
      <div className="guide-sidebar-content">
        <a href="/guides" className="guide-sidebar-back">
          <Icon icon="arrow-left" iconType="solid" size={14} />
          <span>All guides</span>
        </a>

        <div className="guide-sidebar-section">
          <div className="guide-sidebar-label">Progress</div>
          <div className="guide-sidebar-progress">
            <div className="guide-mascot">
              <svg viewBox="0 0 688 690" width="48" height="48">
                <defs>
                  <clipPath id="progressClip">
                    <rect x="0" y={0} width="688" height={progress * 6.9} />
                  </clipPath>
                </defs>

                {}
                <path d="M343.67 1.5C542.684 1.5 685.84 149.351 685.84 344.84C685.84 540.328 542.685 688.18 343.67 688.18C144.655 688.18 1.5 540.318 1.5 344.84C1.50007 149.361 144.655 1.50005 343.67 1.5Z" fill="#FCFCF7" stroke="#FF5FA2" strokeWidth="3" />

                {}
                <path d="M343.67 0C143.81 0 0 148.55 0 344.84C0 541.13 143.81 689.68 343.67 689.68C543.53 689.68 687.34 541.14 687.34 344.84C687.34 148.54 543.53 0 343.67 0Z" fill="#FF5FA2" clipPath="url(#progressClip)" />

                {}
                <path d="M337.89 319.29C337.89 336.75 322.49 350.14 302.81 349.83C286.18 349.57 273.89 337.29 274.37 321.45C274.88 304.82 290.91 290.88 309.98 290.44C325.69 290.09 337.88 302.69 337.88 319.29H337.89Z" fill="#4D122C" />
                <path d="M566.17 319.29C566.17 336.75 550.77 350.14 531.09 349.83C514.46 349.57 502.17 337.29 502.65 321.45C503.16 304.82 519.19 290.88 538.26 290.44C553.97 290.09 566.16 302.69 566.16 319.29H566.17Z" fill="#4D122C" />
                <path d="M367.74 342.07C360.22 346.32 359.4 354.9 370.62 366.4C381.85 377.9 399.76 389.56 420.81 389.18C441.88 389.1 460.67 377.72 472.47 363.53C473.83 361.93 478.84 356.88 478.51 351.07C478.32 348.35 476.17 341.19 467.83 341.38C463.46 341.44 461.21 343.68 456.69 347.36C445.2 356.14 432.7 361.21 420.56 361.27C408.43 361.43 395.68 356.17 385.39 347.22C380.32 342.81 375.25 337.82 367.74 342.07Z" fill="#4D122C" />
              </svg>
            </div>
            <span className="guide-sidebar-progress-text">{Math.round(progress)}%</span>
          </div>
        </div>

        {category && <div className="guide-sidebar-section">
            <div className="guide-sidebar-label">Category</div>
            <div className="guide-sidebar-category">
              <Icon icon={icon} iconType="solid" size={14} />
              <span>{category}</span>
            </div>
          </div>}

        {updatedDate && <div className="guide-sidebar-section">
            <div className="guide-sidebar-label">Last updated</div>
            <div className="guide-sidebar-date">{updatedDate}</div>
          </div>}

        {relatedLinks && relatedLinks.length > 0 && <div className="guide-sidebar-section">
            <div className="guide-sidebar-label">Related</div>
            <ul className="guide-sidebar-links">
              {relatedLinks.map((link, index) => <li key={index}>
                  <a href={link.href}>{link.title}</a>
                </li>)}
            </ul>
          </div>}
      </div>
    </aside>;
};

export const GuideTitle = ({title}) => {
  return <div className="guide-header">
      <h1 className="guide-title">{title}</h1>
    </div>;
};

<GuideSidebar
  categoryIcons={categoryIcons}
  category="deployment"
  updatedDate="March 2026"
  relatedLinks={[
{ title: "git follower mode setup", href: "/integrations/git/follower-mode/setup" },
{ title: "git follower mode best practices", href: "/integrations/git/follower-mode/best-practices" },
{ title: "Content Validator", href: "/modeling/develop/content-validator" },
{ title: "Migrating dashboards", href: "/visualize-present/dashboards/migrate" }
]}
/>

<GuideTitle title="Deploying Omni: Multiple instances (Prod/Stage)" />

This guide covers a two-instance Omni deployment: a **staging instance** where all active development happens, and a **production instance** that receives promoted changes through git. The production instance uses git follower mode to stay in-sync with staging's main branch.

This setup provides true environment isolation — developers work entirely in staging, and production is only updated through a deliberate, git-controlled promotion process. It is the recommended approach for teams that need:

* A stable, read-only production environment that cannot be affected by in-progress development
* Strict control over what reaches production and when
* Full separation of developer and end-user data access

## Git follower mode

[**Git follower mode**](/integrations/git/follower-mode/setup) lets you centralize model development by designating one Omni model as a **leader** and another as a **follower**. The follower is read-only and automatically updates when changes are merged from the leader's base branch into the follower's base branch through a pull request. This is useful for creating dev/production environments or deploying a model across multiple regions or organizations.

The staging instance holds the **leader model**: all development branches are opened here, all changes are tested here, and all PRs target staging's `main` branch. When you're ready to promote to production, you create a release branch in git that carries the changes from staging's `main` into production's base branch (e.g., `prod`). The production instance's follower model automatically detects this branch and creates a matching Omni branch, which you validate before merging.

Git follower mode promotes model changes only. Content (workbooks and dashboards) must be migrated separately to production using the content migration APIs, the [agent skills](/developers/agent-skills) or [Omni CLI](/developers/cli).

## Requirements

Before you begin:

* Two separate Omni instances: one for staging, one for production
* **Organization Admin** permissions on both instances
* **Connection Admin** permissions on both models' connections
* A staging database and a production database of the same type (e.g., both Snowflake) with identical schemas
* A git repository in GitHub, GitLab, or Azure DevOps
* Permissions in your git provider to add webhooks and deploy/SSH keys

## Architecture overview

|                            | Staging instance                        | Production instance                             |
| -------------------------- | --------------------------------------- | ----------------------------------------------- |
| **Role**                   | Leader — all development and validation | Follower — read-only, receives promoted changes |
| **Database**               | Staging database                        | Production database                             |
| **Git base branch**        | `main`                                  | `prod` (or equivalent)                          |
| **Git follower mode**      | Disabled                                | Enabled                                         |
| **Always create branches** | Not required                            | Required                                        |
| **Content**                | Builds and tests content                | Hosts live content (migrated through API)       |

## Initial setup

1. Create production database connection in production Omni instance
2. Create staging database connection in staging Omni instance 
3. Set up the git integration on each instance
   * In staging, you’ll be pointed to the `main` branch
   * In production, you’ll check the box for “Git Follower” and point to a separate base branch (e.g. `prod` - make sure you configure this in git as well)

## Development workflow (staging)

All development happens on the stage instance which acts as the **leader**.

<Steps>
  <Step title="Open a branch">
    From the  **staging** instance model IDE, create a new branch. Make model and content changes on this branch.
  </Step>

  <Step title="Validate with the Content Validator">
    Run the [Content Validator](/modeling/develop/content-validator) on your branch to surface any broken references before promoting.

    <Tip>
      Run the Content Validator before opening a pull request. Catching broken references early keeps your PR focused on model logic, not cleanup.
    </Tip>
  </Step>
</Steps>

## Deployment workflow (staging)

<Steps>
  <Step title="Merge model changes to STAGE ">
    Merge your changes to the shared model of STAGE (the leader instance)

    * **With pull requests required** (recommended):
      * Open a PR to `main` and merge after review. This publishes attached content **on the leader instance only**.
    * **Without pull requests**: Merge the branch directly from the model IDE.

    <Note>
      Content changes (published workbooks and dashboards) are **not** automatically propagated to the production instance through [git follower.](/integrations/git/follower-mode/index) Git follower syncs **model changes only**. To replicate content across instances, use the content migration API.
    </Note>
  </Step>
</Steps>

## Promotion workflow (staging to production)

Because STAGE is **leader** to follower **PROD**, the changes in stage's `main` need to be moved to `prod` through a pull request.

<Steps>
  <Step title="Create release branch in git">
    * In git repository
      * Create a branch off of the base branch of stage (`main`) (e.g. `release-to-prod-YYYYMMDD`). This pull request wraps all "followed" changes into the newly created branch.
      * Create a pull request that merges your changes into the follower's base branch (`prod` in git)
          <Tip>
            Make sure the “Always create branches” setting is enabled in Omni as it will create a branch in Omni with the same name
          </Tip>
  </Step>

  <Step title="Migrate content from STAGE to PROD instance">
    As git follower only promotes model changes, use the content migration API to promote content across instances prior to promoting model changes.

    <Tip>
      It's a best practice to consider your folder structures and their mapping between STAGE and PROD. This can be accomplished with Content, Folders, and Documents APIs.
    </Tip>

    <AccordionGroup>
      <Accordion title="New content?">
        Use the [get-document-state](/api/documents-v2/get-document-state) endpoint to create a copy of the content. Use [create-document](/api/documents-v2/create-document) endpoint to create a new document on PROD that will also be published.

        <Warning>
          You may also consider using the [get-draft-state](/api/documents-v2/get-draft-state) endpoint when migrating a draft.  Viewing data, ownership, schedules and history will NOT transfer to the copy and will need to be updated separately.
        </Warning>
      </Accordion>

      <Accordion title="Updating content?">
        If the document already exists, use [create-draft-and-patch-document](/api/documents-v2/create-draft-and-patch-document) to create the draft  of the published version and then the [publish-draft](/api/documents-v2/publish-draft) endpoint to publish it. You may also consider [patch-draft](/api/documents-v2/patch-draft) for further patch iterations on a draft. 
      </Accordion>
    </AccordionGroup>

    <Tip>
      Migrate content after the model promotion is confirmed in production. This ensures the PROD model is in the correct state before any content references it.
    </Tip>
  </Step>

  <Step title="Review changes in the Omni PROD instance">
    * In the Omni PROD instance
      * Open the model. The PROD model is read-only due to git follower settings, so you review changes here but don't edit them.
      * Open the `release-to-prod-YYYYMMDD` branch
      * Test the changes by reviewing the [Content Validator](/modeling/develop/content-validator#content-validator-basics) for any issues
  </Step>

  <Step title="Merge the release branch into base prod branch">
    * In git repository:
      * Merge the pull request in git to merge the release branch changes into the base branch (likely `prod`)
    * At this point, your model changes are live in the production instance
  </Step>
</Steps>

## Permissions considerations

Structuring permissions across two instances keeps the production environment clean and reduces the risk of accidental changes.

| Role                      | Staging instance                | Production instance             |
| ------------------------- | ------------------------------- | ------------------------------- |
| **Developers / Modelers** | Full branch and model access    | Read-only or no access          |
| **PR approvers**          | Controlled through git provider | Controlled through git provider |
| **Content consumers**     | Testing and preview             | Primary audience                |

Consider restricting developer access to the production instance to read-only. Changes to the production model should only arrive through the git promotion process, not through direct Omni access.

## Best practices

### Mirror your database schemas

The shared model is the same across both instances — it doesn't know which instance it's in. If your staging and production schemas diverge (different tables, columns, or data types), model references that work in staging will break in production after promotion. Keep schemas structurally identical.

### Use a consistent release branch naming convention

A format like `release-to-prod-YYYYMMDD` makes it straightforward to identify in-flight promotions, track the history of what landed in production and when, and quickly roll back by reverting the PR.

### Plan content migration as part of your release checklist

Content migration is often skipped under time pressure. Build it explicitly into your release process — document which content needs to move with each release and trigger migration before announcing changes are live to end users.

### Refresh the production schema after promotion

After merging a release, perform a schema refresh on the production model to ensure the schema matches staging. This is especially important after releases that include database schema changes.

## Next steps

* [git follower mode setup](/integrations/git/follower-mode/setup) — Detailed reference for git follower configuration
* [git follower mode best practices](/integrations/git/follower-mode/best-practices) — Recommendations for managing follower workflows
* [Migrating dashboards](/visualize-present/dashboards/migrate) — How to migrate content across instances using the API
* [Content validator](/modeling/develop/content-validator) — How to validate and repair content after model changes
