# Manage Reverse ETL Connections using Rudder CLI

<p>A <strong>Reverse ETL connection</strong> is the link between a Reverse ETL source and a destination. It also holds the sync configuration for that pair: the <a href="https://www.rudderstack.com/docs/data-pipelines/reverse-etl/developer-guides/sync-modes/" >sync mode</a>, the <a href="https://www.rudderstack.com/docs/data-pipelines/reverse-etl/developer-guides/sync-schedule-settings/" >schedule</a>, the identifiers, and the column mappings. Rudder CLI already manages SQL models and destinations as YAML. Connection specs declare those links in the same project so <code>rudder-cli apply</code> creates the source, destination, and connection in the right order — without a dashboard step to wire them together.</p>
<p>Use <code>kind: retl-connections</code>. Each entry in <code>spec.connections</code> references resources in the <strong>same project</strong> (<code>#retl-source-sql-model:…</code> and <code>#destination:…</code>) and carries its sync configuration in a <code>config</code> block. Rudder CLI uses those references to create endpoints before the connection, and to delete the connection before the endpoints.</p>
<p>To connect an Event Stream source instead, see <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/connections/" >Manage Connections using Rudder CLI</a>.</p>
<p>To set up a connection step by step, follow the <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/retl-connections-walkthrough/" >Reverse ETL connections walkthrough</a>.</p>

<blockquote class="warning">
  <div class="tip-quote">
    
    <div class="tip-text"><p><strong>Reverse ETL connection support is experimental and requires Rudder CLI v0.27.0 or later</strong>.</p>
<p>v0.27.0 connects to HTTP Webhook only. The other <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/yaml-retl-connections/#supported-destinations" >supported destinations</a> need v0.28.0 or later.</p>
<p><a href="#enable-reverse-etl-connection-support" >Enable the experimental flags</a> before you validate or apply connection resources.</p>
</div>
  </div>
</blockquote>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="key-features">Key features</h2><!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="declarative-sync-configuration">Declarative sync configuration</h3><ul>
<li>Set <code>enabled</code> to control whether that pair syncs. If you omit <code>enabled</code>, it defaults to <code>true</code>. Both endpoints must also be enabled for the connection to sync.</li>
<li>Delete a connection entry to disconnect the pair without deleting the source or destination.</li>
</ul>
<!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="local-validation">Local validation</h3><p><code>rudder-cli validate</code> checks Reverse ETL connection specs — including that both endpoints exist in the project, the destination accepts Reverse ETL sources, the mappings fit the mapping flow, and the schedule syncs no more often than every five minutes.</p>
<p>See <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/yaml-retl-connections/#validation-rules" >Reverse ETL Connection YAML Reference</a> for the full rule list.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="git-integration-and-cicd-support">Git integration and CI/CD support</h3><ul>
<li>Import Reverse ETL connections from your workspace, then manage them in Git.</li>
<li>Run <code>validate</code> in CI so a pull request that violates connection rules fails before apply.</li>
</ul>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="who-can-use-this-feature">Who can use this feature?</h2><p>CLI-based Reverse ETL connections are useful when you need to:</p>
<ul>
<li>Keep the SQL model, the destination, and the sync configuration between them in one repository.</li>
<li>Review schedule and mapping changes in a pull request before they reach a workspace.</li>
</ul>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="enable-reverse-etl-connection-support">Enable Reverse ETL connection support</h2><p>Reverse ETL connections need two settings. The first turns on experimental features as a whole — without it, Rudder CLI ignores every experimental flag. The second turns on Reverse ETL connections.</p>
<table>
  <thead>
      <tr>
          <th>Setting</th>
          <th>Environment variable</th>
          <th>Config key</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Experimental features</td>
          <td><code>RUDDERSTACK_CLI_EXPERIMENTAL</code></td>
          <td><code>experimental</code></td>
      </tr>
      <tr>
          <td>Reverse ETL connections</td>
          <td><code>RUDDERSTACK_X_RETL_CONNECTION_SUPPORT</code></td>
          <td><code>flags.retlConnectionSupport</code></td>
      </tr>
  </tbody>
</table>
<div class="rs-tabs">
	<div class="rs-tabs__list">
		<button type="button" class="rs-tabs__tab"
			id="tab-edfbac"
		
		>Environment variables</button>
		<button type="button" class="rs-tabs__tab"
			id="tab-feadcb"
		
		>Config file</button>
	</div>
	
<div class="rs-tabs__panel" id="panel-edfbac">

<div class="rs-code">
  <div class="rs-code__head">shell<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">export RUDDERSTACK_CLI_EXPERIMENTAL=true
export RUDDERSTACK_X_RETL_CONNECTION_SUPPORT=true</code></pre></div>
</div>

</div>

<div class="rs-tabs__panel" id="panel-feadcb" hidden>

<p>Add these keys to <code>~/.rudder/config.json</code>, next to the ones already in the file:</p>
<div class="rs-code">
  <div class="rs-code__head">json<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-json" data-lang="json">{
  &#34;experimental&#34;: true,
  &#34;flags&#34;: {
    &#34;retlConnectionSupport&#34;: true
  }
}</code></pre></div>
</div>

</div>

</div>

<p>Every command that loads a project containing a <code>retl-connections</code> spec needs both settings, including <code>validate</code> and <code>apply</code> in CI. <code>import workspace</code> needs them too, or it doesn&rsquo;t import Reverse ETL connections. Without them, Rudder CLI doesn&rsquo;t recognize the kind, and the spec fails validation with a <code>'kind' must be one of [...]</code> error that lists the kinds it does recognize.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="supported-sources-and-destinations">Supported sources and destinations</h2><ul>
<li><strong>Sources</strong> — <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/sql-models/" >SQL models</a> on any supported warehouse. Table sources are available behind an additional experimental flag. See <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/yaml-retl-connections/#supported-sources" >Supported sources</a>.</li>
<li><strong>Destinations</strong> — HTTP Webhook in Rudder CLI v0.27.0. v0.28.0 or later adds more destination types, including Amplitude, Braze, HubSpot, and Iterable. See <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/yaml-retl-connections/#supported-destinations" >Supported destinations</a> for the full list.</li>
</ul>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="limits">Limits</h2><ul>
<li><strong>Audience sources</strong>: Rudder CLI doesn&rsquo;t manage connections from <a href="https://www.rudderstack.com/docs/activate/audiences/" >Audiences</a>.</li>
<li><strong>Customer.io</strong>: Customer.io syncs Reverse ETL data through its own flow, which connection specs can&rsquo;t express. <code>validate</code> rejects a connection to it.</li>
<li><strong>One Reverse ETL source per destination</strong>: In a project, a destination can receive from only one Reverse ETL source, and can&rsquo;t receive from both Event Stream and Reverse ETL sources.</li>
<li><strong>Connections outside the project</strong>: <code>validate</code> sees only the connections in your project. The one-Reverse-ETL-source-per-destination and no-mixed-source rules are enforced by the dashboard and Rudder CLI, not by the API, so a conflict with a connection that exists only in the workspace isn&rsquo;t caught at apply either — it lands in the workspace as a misconfiguration.</li>
<li><strong>Sync operations</strong>: Rudder CLI declares the connection but doesn&rsquo;t start, stop, or report on syncs. Use the dashboard to <a href="https://www.rudderstack.com/docs/data-pipelines/reverse-etl/developer-guides/start-stop-syncs/" >start and stop syncs</a>.</li>
<li><strong>Single-connection import</strong>: You can import Reverse ETL connections only with <code>rudder-cli import workspace</code>.</li>
</ul>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="apply-and-replace-behavior">Apply and replace behavior</h2><p>What <code>apply</code> does with a change depends on the field you changed:</p>
<table>
  <thead>
      <tr>
          <th>Change</th>
          <th>What <code>apply</code> does</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>enabled</code>, <code>schedule</code>, <code>identifiers</code>, <code>mappings</code>, <code>constants</code>, <code>sync_settings</code></td>
          <td>Updates the connection in place. Adding or changing a mapping triggers a <a href="https://www.rudderstack.com/docs/data-pipelines/reverse-etl/developer-guides/sync-modes/#after-updating-mappings" >full sync</a>.</td>
      </tr>
      <tr>
          <td><code>sync_behaviour</code>, <code>cursor_column</code>, <code>object</code>, <code>event</code></td>
          <td>Fails. These fields are <strong>immutable</strong> after the connection is created.</td>
      </tr>
      <tr>
          <td><code>source</code> or <code>destination</code></td>
          <td><strong>Replaces</strong> the connection (delete + create). The backend treats each source–destination pair as unique.</td>
      </tr>
  </tbody>
</table>
<p>RudderStack keeps sync state per connection ID, so every connection that <code>apply</code> creates starts with a <a href="https://www.rudderstack.com/docs/data-pipelines/reverse-etl/developer-guides/sync-modes/#first-sync" >full sync</a>, whatever its <code>sync_behaviour</code>. That includes a connection it replaces, or one you delete and recreate.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="change-an-immutable-field">Change an immutable field</h3><p>Changing an immutable field fails the apply with an error that names the field. <code>validate</code> and <code>apply --dry-run</code> don&rsquo;t detect this change, so the error appears only when you apply:</p>
<div class="rs-code">
  <div class="rs-code__head"><button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">syncing resources: connection &#34;users-to-webhook&#34;: connection update: cursor_column is immutable (&#34;&#34; -&gt; &#34;updated_at&#34;); delete and recreate the connection to apply it</code></pre></div>
</div>
<p>To apply the change, remove the connection entry from the spec and run <code>apply</code>. Then, add the entry back with the new value and run <code>apply</code> again. The recreated connection has a new ID.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="replacement">Replacement</h3><p>Rudder CLI builds and checks the new connection before it deletes the existing one. A replacement that fails those checks leaves the live connection untouched.</p>
<p>A replaced connection has a new ID.</p>
<p>If the delete succeeds and the create then fails, the error says that the previous connection was deleted:</p>
<div class="rs-code">
  <div class="rs-code__head"><button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">recreating rETL connection &#34;users-to-webhook&#34; after an endpoint change (the previous connection was deleted): ...</code></pre></div>
</div>
<p>Fix the cause and run <code>apply</code> again to create the connection. The error doesn&rsquo;t include the ID of the deleted connection. Rudder CLI logs it as <code>connectionId</code> in the <code>replacing rETL connection after an endpoint change</code> warning in <code>~/.rudder/cli.log</code>.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="get-started">Get started</h2><ul>
<li>Follow the <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/retl-connections-walkthrough/" >End-to-End Walkthrough: Reverse ETL Connections with Rudder CLI</a> to sync a SQL model to a destination</li>
<li>See the <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/yaml-retl-connections/" >Reverse ETL Connection YAML Reference</a> for fields, mapping flows, and validation rules</li>
<li>Define endpoints first: <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/sql-models/" >SQL models</a> and <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/destinations/" >destinations</a></li>
<li>Automate with <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/github-actions/" >GitHub Actions for Rudder CLI</a></li>
</ul>

