> ## Documentation Index
> Fetch the complete documentation index at: https://na-36-mintlify-aebde2c5.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Gateway and Orchestrator Interface

> Configure a node running both a gateway and an orchestrator on Livepeer. Covers deployment architecture, port allocation, self-routing behaviour, pricing alignment, and the off-chain gateway with on-chain orchestrator pattern.

export const BorderedBox = ({children, variant = "default", padding = "var(--lp-spacing-4)", borderRadius = "var(--lp-spacing-px-8)", accentBar = "", style = {}, className = "", ...rest}) => {
  const variants = {
    default: {
      border: "1px solid var(--lp-color-border-default)",
      backgroundColor: "var(--lp-color-bg-card)"
    },
    accent: {
      border: "1px solid var(--lp-color-accent)",
      backgroundColor: "var(--lp-color-bg-card)"
    },
    muted: {
      border: "1px solid var(--lp-color-border-default)",
      backgroundColor: "transparent"
    }
  };
  const accentBarColors = {
    accent: "var(--lp-color-accent)",
    positive: "var(--green-9)"
  };
  return <div data-docs-bordered-box="" data-accent-bar={accentBarColors[accentBar] ? "" : undefined} className={className} style={{
    ...variants[variant],
    padding: padding,
    borderRadius: borderRadius,
    ...accentBarColors[accentBar] ? {
      position: "relative",
      '--accent-bar-color': accentBarColors[accentBar]
    } : {},
    ...style
  }} {...rest}>
      {children}
    </div>;
};

export const TableCell = ({children, align = "left", header = false, style = {}, className = "", ...rest}) => {
  const Component = header ? "th" : "td";
  return <Component className={className} style={{
    padding: "0.75rem 1rem",
    textAlign: align,
    border: header ? "none" : "1px solid var(--lp-color-border-default)",
    ...style
  }} {...rest}>
      {children}
    </Component>;
};

export const TableRow = ({children, header = false, hover = false, style = {}, className = "", ...rest}) => {
  const rowId = `table-row-${Math.random().toString(36).substr(2, 9)}`;
  return <>
      {hover && <style>{`
          #${rowId}:hover {
            background-color: var(--lp-color-bg-card);
          }
        `}</style>}
      <tr id={rowId} className={className} style={{
    ...header && ({
      backgroundColor: "var(--lp-color-accent-strong)",
      color: "var(--lp-color-on-accent)",
      fontWeight: "bold"
    }),
    ...style
  }} {...rest}>
        {children}
      </tr>
    </>;
};

export const StyledTable = ({children, variant = "default", style = {}, className = "", ...rest}) => {
  const wrapperVariants = {
    default: {
      border: "1px solid var(--lp-color-border-default)",
      backgroundColor: "var(--lp-color-bg-card)",
      overflow: "hidden"
    },
    bordered: {
      border: "2px solid var(--lp-color-accent)",
      backgroundColor: "var(--lp-color-bg-page)",
      overflow: "hidden"
    },
    minimal: {
      border: "none",
      backgroundColor: "transparent",
      overflow: "visible"
    }
  };
  return <div data-docs-styled-table-shell className={className} style={{
    width: "100%",
    padding: 0,
    margin: 0,
    ...wrapperVariants[variant],
    ...style
  }} {...rest}>
      <table data-docs-styled-table style={{
    width: "100%",
    borderCollapse: "collapse",
    borderSpacing: 0,
    margin: 0,
    backgroundColor: "transparent"
  }}>
        {children}
      </table>
    </div>;
};

export const LinkArrow = ({href, label, description, newline = true, borderColor, className = '', style = {}, ...rest}) => {
  const linkArrowStyle = {
    display: 'inline-flex',
    alignItems: 'center',
    justifyContent: 'center',
    gap: "var(--lp-spacing-1)",
    width: 'fit-content',
    ...borderColor && ({
      borderColor
    })
  };
  return <span className={className} style={style} {...rest}>
      {newline && <br />}
      <span style={linkArrowStyle}>
        <a href={href} target="_blank" rel="noopener noreferrer">
          {label}
        </a>
        <Icon icon="arrow-up-right" size={14} color="var(--lp-color-accent)" />
      </span>
      {description && description}
      {description && <div style={{
    height: "var(--lp-spacing-3)"
  }} />}
    </span>;
};

export const CustomDivider = ({color = "var(--lp-color-border-default)", middleText = "", spacing = "default", style = {}, className = "", ...rest}) => {
  const spacingPresets = {
    default: {
      margin: "24px 0"
    },
    overlap: {
      margin: "-1rem 0 -1rem 0"
    },
    tight: {
      margin: "0 0 -1rem 0"
    },
    section: {
      margin: "0 0 -2rem 0"
    },
    sectionOverlap: {
      margin: "-1rem 0 -2rem 0"
    },
    deepOverlap: {
      margin: "-1rem 0 -1.5rem 0"
    }
  };
  const spacingStyle = spacingPresets[spacing] || spacingPresets.default;
  return <div role="separator" aria-orientation="horizontal" className={className} style={{
    display: "flex",
    alignItems: "center",
    ...spacingStyle,
    fontSize: style?.fontSize || "16px",
    height: "fit-content",
    ...style
  }} {...rest}>
      <span style={{
    marginRight: "var(--lp-spacing-px-8)",
    opacity: 0.2
  }}>
        <Icon icon="/snippets/assets/logos/Livepeer-Logo-Symbol-Theme.svg" />
      </span>
      <div style={{
    flex: 1,
    height: "1px",
    background: "var(--lp-color-border-default)",
    opacity: 0.4
  }}></div>
      {middleText && <>
          <Icon icon="circle" size={2} />
          <span style={{
    margin: "0 8px",
    fontWeight: "bold",
    color: color,
    opacity: 0.7
  }}>
            {middleText}
          </span>
          <Icon icon="circle" size={2} />
        </>}
      <div style={{
    flex: 1,
    height: "1px",
    background: "var(--lp-color-border-default)",
    opacity: 0.4
  }}></div>
      <span style={{
    marginLeft: "var(--lp-spacing-px-8)",
    opacity: 0.2
  }}>
        <span style={{
    display: "inline-block",
    transform: "scaleX(-1)"
  }}>
          <Icon icon="/snippets/assets/logos/Livepeer-Logo-Symbol-Theme.svg" />
        </span>
      </span>
    </div>;
};

<Tip>
  The most common combined deployment is an off-chain gateway routing to a dedicated on-chain orchestrator. The gateway handles inbound client traffic; the orchestrator handles on-chain protocol participation. Running both roles on one machine is supported but requires deliberate port separation.
</Tip>

***

Run both a gateway and an orchestrator when you need control over client traffic, routing, and workload execution end to end. The deployment patterns, port separation, self-routing choices, and price alignment rules that matter when one operator owns both roles.

For detailed gateway setup, see the <LinkArrow href="/v2/gateways/quickstart/gateway-setup" label="Gateway Setup" newline={false} /> guide. For orchestrator setup, start with the <LinkArrow href="/v2/orchestrators/setup/guide" label="Orchestrator Setup Guide" newline={false} />.

<CustomDivider middleText="Deployment Patterns" />

## Deployment patterns

Three deployment patterns cover most use cases:

<BorderedBox variant="accent">
  <Tabs>
    <Tab title="Off-chain gateway + on-chain orchestrator" icon="cloud">
      **The most common pattern.** The gateway runs without on-chain participation – it holds no ETH, no keystore, and connects to your orchestrator directly via `-orchAddr`. The orchestrator runs with full on-chain registration and stake.

      The gateway routes jobs to the orchestrator via direct address specification, bypassing protocol discovery. Payment is handled by a remote signer or clearinghouse connected to the gateway.

      This pattern suits operators who want gateway capability without managing ETH deposits in the gateway process itself. The orchestrator handles all on-chain protocol activity.

      **Port layout:** Gateway on `:7935` (HTTP) and `:7935` (RTMP). Orchestrator on `:8935` (serviceAddr). No port conflict.
    </Tab>

    <Tab title="Same machine (combined)" icon="server">
      Both gateway and orchestrator processes run on one machine. Each runs as a separate go-livepeer process with different flags and different ports.

      This pattern suits operators with a single high-capacity machine who want to eliminate network latency between gateway and orchestrator. Combined with an AI worker, this is the densest configuration possible for a single node.

      **Port conflict risk is high on same-machine deployments.** Assign distinct ports to each role. See port allocation below.
    </Tab>

    <Tab title="Separate machines" icon="network-wired">
      Gateway and orchestrator run on separate machines connected over the network. The gateway is configured with `-orchAddr <orchestrator-ip>:8935` for an off-chain gateway, or discovers the orchestrator via on-chain registration if running on-chain.

      This pattern is appropriate at scale. A gateway that faces external traffic scales independently from orchestrator registration and staking.
    </Tab>
  </Tabs>
</BorderedBox>

<CustomDivider middleText="Port Allocation" />

## Port allocation

The two roles use different network interfaces and ports. On a single machine, assign each process its own non-overlapping ports.

<StyledTable variant="bordered">
  <thead>
    <TableRow header>
      <TableCell header>Role</TableCell>
      <TableCell header>Flag</TableCell>
      <TableCell header>Default port</TableCell>
      <TableCell header>Purpose</TableCell>
    </TableRow>
  </thead>

  <tbody>
    <TableRow>
      <TableCell>Gateway</TableCell>
      <TableCell>`-httpAddr`</TableCell>
      <TableCell>`:7935`</TableCell>
      <TableCell>Gateway HTTP API for client connections</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>Gateway</TableCell>
      <TableCell>`-rtmpAddr`</TableCell>
      <TableCell>`:1935`</TableCell>
      <TableCell>RTMP ingest port for video streams</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>Orchestrator</TableCell>
      <TableCell>`-serviceAddr`</TableCell>
      <TableCell>`:8935`</TableCell>
      <TableCell>Public address gateways connect to for job execution</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>Orchestrator</TableCell>
      <TableCell>`-cliAddr`</TableCell>
      <TableCell>`:7936`</TableCell>
      <TableCell>Internal livepeer\_cli management interface</TableCell>
    </TableRow>
  </tbody>
</StyledTable>

The default gateway and orchestrator ports stay separate on the same machine. Verify no other process is bound to them before starting both roles:

```bash icon="terminal" title="Check gateway and orchestrator ports" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
ss -tlnp | grep -E ':7935|:8935|:1935|:7936'
```

<CustomDivider middleText="Self-Routing" />

## Self-routing

Self-routing is when a gateway you control routes jobs to an orchestrator you also control.

**Off-chain gateway to own orchestrator:** Configure the gateway with `-orchAddr <your-orchestrator-ip>:8935`. The gateway routes all jobs to the specified address. This is explicit self-routing – the gateway sends all work to your orchestrator.

**On-chain gateway discovering own orchestrator:** If both your gateway and orchestrator are on-chain, the gateway discovers your orchestrator through the normal protocol selection process, alongside all other active orchestrators. Your orchestrator competes on price and stake like any other.

Self-routing via explicit `-orchAddr` is appropriate when:

* Testing AI inference quality before serving jobs to clients
* Running a dedicated internal service (e.g. Transcoding your own content)
* You want guaranteed routing to your own infrastructure without depending on protocol selection

Self-routing through on-chain discovery remains competitive. Your orchestrator still has to win on price, stake, and performance to receive the job. Use an off-chain gateway with direct `-orchAddr` when routing must stay dedicated.

**Pricing alignment:** The gateway's `-maxPricePerUnit` (or `-maxPricePerCapability` for AI) must be at or above the orchestrator's `-pricePerUnit` (or `price_per_unit` in `aiModels.json`). A gateway with a cap below the orchestrator's advertised price will fail to route any jobs to it.

<CustomDivider middleText="Pricing Alignment" />

## Pricing alignment

When you control both gateway and orchestrator, configure the gateway's caps relative to the orchestrator's advertised prices:

```text icon="terminal" title="Price cap alignment rule" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
gateway -maxPricePerUnit  ≥  orchestrator -pricePerUnit
gateway -maxPricePerCapability  ≥  orchestrator aiModels.json price_per_unit (per pipeline)
```

A gateway cap exactly equal to the orchestrator price is sufficient for self-routing but leaves no margin for `autoAdjustPrice` adjustments (which increase the advertised price during gas spikes). Set the gateway cap 20 to 30% above the orchestrator base price to prevent job failures during gas price increases.

Example video pricing alignment:

```bash icon="terminal" title="Example price alignment" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
# Orchestrator startup
-pricePerUnit 1000
-autoAdjustPrice=true    # may raise advertised price during gas spikes

# Gateway startup (same operator)
-maxPricePerUnit 1300    # 30% above base to absorb autoAdjustPrice headroom
```

<CustomDivider middleText="Monitoring Both Roles" />

## Monitoring both roles

Each role produces its own Prometheus metrics. On a single machine, ensure both processes export to different ports to avoid metric collisions.

**Orchestrator metrics to watch:**

* `livepeer_transcode_duration_seconds` – transcoding latency
* `livepeer_winning_ticket_count` – PM ticket win frequency
* `livepeer_reward_call_success` – reward call outcome per round

**Gateway metrics to watch:**

* `livepeer_broadcaster_sessions_total` – active inbound sessions
* `livepeer_broadcaster_upload_errors` – upload failures (may indicate orchestrator issues)

For production deployments running both roles, maintain separate log streams per process to avoid interleaving:

```bash icon="terminal" title="Separate log streams per role" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
# Two separate systemd units or Docker containers
# Gateway logs
journalctl -u livepeer-gateway -f

# Orchestrator logs
journalctl -u livepeer-orchestrator -f
```

<CustomDivider />

## Related pages

<CardGroup cols={2}>
  <Card title="Gateway Setup" icon="server" href="/v2/gateways/quickstart/gateway-setup" arrow horizontal>
    Full gateway configuration guide including payment mode selection and pipeline configuration.
  </Card>

  <Card title="Gateway Relationships" icon="handshake" href="/v2/orchestrators/guides/advanced-operations/gateway-relationships" arrow horizontal>
    How gateways discover and select orchestrators – selection criteria from the orchestrator side.
  </Card>

  <Card title="Dual Mode Configuration" icon="layer-group" href="/v2/orchestrators/guides/deployment-details/dual-mode-configuration" arrow horizontal>
    Running video transcoding and AI inference from a single go-livepeer process.
  </Card>

  <Card title="Pricing Strategy" icon="tag" href="/v2/orchestrators/guides/config-and-optimisation/pricing-strategy" arrow horizontal>
    Aligning orchestrator price with gateway caps for self-routing and commercial relationships.
  </Card>
</CardGroup>
