# The edge
URL: https://openship.io/docs/guides/the-edge.md

Openship's built-in reverse proxy — how your apps get a public address and HTTPS, how you protect a route, where your traffic numbers come from, and how to take over a proxy that's already running on a server.

import { Step, Steps } from 'fumadocs-ui/components/steps';

Every request from the internet has to land *somewhere* first, before Openship knows which of your apps it's
for. That somewhere is the **edge** — a reverse proxy that sits on ports `80` and `443` and acts as the front
door for the whole box. It answers the connection, sorts out HTTPS, works out which app the hostname belongs
to, forwards the request to that app's container, and counts it on the way past.

Most of the time you never think about it: `openship up` sets the edge up for you, and every deploy that has a
domain wires that domain into it automatically. You start caring about the edge directly in a few specific
moments — when you're moving an existing server across, when you want to route a bare hostname at a port, when
you need to rate-limit or block something, or when you're asking "where is my traffic actually coming from?"

<Callout title="The edge vs. the server" type="info">
Two different things run on a box, and it's worth keeping them straight. The **server** is Openship itself —
the API and dashboard, the control room. The **edge** is the public reverse proxy in front of your apps — the
front door. `openship up` installs both together; a visitor's request only ever touches the edge, never the
server.
</Callout>

## What the edge actually does

Four jobs, all on the request path:

- **HTTPS for free.** It terminates TLS and gets a Let's Encrypt certificate for each domain, so your apps are
  served over `https://` without you managing certs.
- **Routing.** It maps each hostname to the right container — that's how ten apps share one server and one IP,
  each on its own domain.
- **Protection.** Per-route rules — rate limits, IP/country bans, CIDR allow-lists — are enforced here, before a
  request ever reaches your app.
- **Traffic counting.** Because it already sees every request, it tallies them (requests, bandwidth, countries,
  top paths) at essentially no cost, and that's what fills the analytics you'll see later.

<Callout title="What you need first">
- A Linux server — the edge is a Linux reverse proxy. On macOS/Windows you can still *read* an edge's domains and
  traffic from the CLI, but installing and taking over the proxy happens on the Linux box.
- Openship installed on that box (`openship up`). The edge comes with it.
- For anything that reads data on a CLI-installed instance, a login: run `openship login` once with a token
  from the dashboard.
</Callout>

## Self-hosted vs Cloud

A self-hosted deployment uses an edge on your own server. Openship Cloud uses
a managed edge. Review these differences when moving a project between them.

| Feature | Self-hosted edge | Openship Cloud |
| --- | --- | --- |
| Proxy settings (`routingConfig.proxy`) | Supported, subject to validation and the proxy's capabilities. | New or changed nonempty proxy settings are rejected with `CLOUD_PROXY_SETTINGS_UNSUPPORTED`. Unchanged legacy settings may be carried forward; this does not make them supported. |
| Domain redirects | Applied through the server's edge configuration. | Applied through the managed Cloud edge. |
| Route rules: rate limits, IP/country access rules, and hotlink protection | Enforced by the OpenResty route-rule guard after the rules are successfully pushed. | The self-hosted route-rule push path skips Cloud projects. Saving a rule does not establish that equivalent protection is active on Cloud. |
| Live edge configuration inspection | Can compare saved proxy settings with the configuration served by a reachable server. | Self-hosted OpenResty inspection is unavailable; the report returns `reachable: false`. |
| Operator-defined trusted proxies and client-IP headers | Configured on the self-hosted edge, with the limitations below. | The self-hosted environment variables below do not configure the managed Cloud edge. |

### Real client IP and trusted proxies

IP-based rules need the visitor's real address. If another proxy sits in front
of a self-hosted edge, configure which proxy addresses it trusts:

- `OPENSHIP_EDGE_TRUSTED_PROXIES` adds trusted proxy addresses or CIDR ranges.
  Only include proxies you control or trust to supply the client address.
- On bare or remote OpenResty installations,
  `OPENSHIP_EDGE_REAL_IP_HEADER` selects the client-IP header when configuration
  is generated. The default is `CF-Connecting-IP`.
- On containerized edges, the generated configuration adds trusted proxy
  addresses but retains the header baked into the image. Setting
  `OPENSHIP_EDGE_REAL_IP_HEADER` does not override that header.

Reapply routing after changing these settings. A reload alone does not
regenerate configuration.

### Moving a project between deployment types

Review proxy settings, route rules, and client-IP handling on the destination.
Verify that the intended protections are active after deployment; saved
configuration alone is not proof of enforcement.

Domain redirects are supported on both deployment types. Redirect targets
must belong to the same project, and redirect cycles are rejected. See
[Redirect one domain to another](/docs/guides/custom-domains#redirect-one-domain-to-another).

## Taking over a server that's already serving

This is the headline case. You have a server that's *already* running nginx, Apache, or Caddy on `:80/:443`
with real sites on it, and you want Openship to run the front door instead — without a flag day where nothing
is served.

The edge does this as a **migration**: it reads the existing proxy's sites, registers each one on the edge so
it keeps serving, and only then takes the ports. The whole thing is journaled and rolls back cleanly if a step
fails, so you never end up with neither proxy up.

<Steps>

<Step>

### See what's there

On the server, ask the edge what it would import:

```bash
openship edge sites
```

It lists the reverse-proxy sites it found on the host — the hostnames and where each currently points.

</Step>

<Step>

### Take over and bring the sites across

```bash
openship edge migrate
```

That imports every site it just listed, then frees `:80/:443` and starts serving them from the edge. If you'd
rather *not* keep the old sites — you just want the ports — use `openship edge takeover` instead, which stops
the old proxy without importing anything.

</Step>

</Steps>

<Callout title="You don't have to go looking for this" type="info">
When a foreign proxy is holding `:80/:443`, Openship raises the same "there's another proxy here — take over
and migrate its sites?" prompt **automatically** during `openship up` and in the bare `openship` control panel.
The commands above are the manual version of that same offer, for when you want to run it on demand.
</Callout>

## Putting a domain on the edge

Once the edge is running, giving an app a public hostname is one command. You can point a domain at an existing
project, or bind a fresh one straight to a local port:

```bash
# Point a host at a port on this box (creates a tracked project for it)
openship edge domains add app.example.com --port 8080

# Attach a host to an existing project (uses that project's port)
openship edge domains add www.example.com --project prj_123 --primary
```

Registering a domain also verifies its DNS and issues the HTTPS certificate for you. If DNS isn't pointing at
the box yet, the command prints the records to add and you re-run verification once they've propagated. The full
domain workflow — DNS records, verification, primary vs. redirect — lives in the
[Custom domains](/docs/guides/custom-domains) guide; `openship edge domains` is the edge-scoped view of it.

## Protecting a route

Rules are applied by the edge itself, so an abusive request is turned away before it costs your app anything.
Scope a rule to a whole project, one domain, or a path prefix, and combine as many conditions as you like into
one rule:

```bash
# 20 requests/second per client IP on the login path, allowing short bursts
openship edge rules add -p prj_123 --path /login --rate-limit 20 --burst 40

# Block two countries and a bad IP, answering with 403
openship edge rules add -p prj_123 --ban-country RU,CN --ban-ip 203.0.113.7 --block-status 403

# Only let an office network in; deny everyone else
openship edge rules add -p prj_123 --allow-cidr 198.51.100.0/24

openship edge rules list -p prj_123
```

<Callout title="Rules that key on IP need the real client IP" type="warn">
Rate limits and country rules need the visitor's real address. If another proxy sits in front of the
self-hosted edge, it must pass the client IP through, and the edge must trust that proxy. See
[Real client IP and trusted proxies](#real-client-ip-and-trusted-proxies) for the edge configuration
and the differences between bare and containerized installations.
</Callout>

## Watching your traffic

This is the part that makes the edge something you *watch*, not just plumbing. Because it counts every request
as it passes, you get a traffic picture with no code and no cost to your visitors. There are two ways to see it.

**In the dashboard**, open the project's **Overview** tab for the rollup — Server Requests, Unique IPs, Avg
Response, Bandwidth Out, a traffic chart, and Top Paths — and the **Logs → Server** sub-tab for the individual
request lines. That's covered in [Logs & monitoring](/docs/guides/logs-monitoring).

**From the terminal**, the same numbers are three commands:

```bash
# Requests, bandwidth, and hourly periods
openship edge traffic -p prj_123

# Visitors, countries, and top paths for one domain
openship edge analytics -p prj_123 --domain app.example.com

# The recent raw request log — and follow it live
openship edge logs -p prj_123 --follow
```

<Callout title="Where the numbers come from" type="info">
**The edge counts; the control plane collects.** The proxy bumps counters in shared memory as each response
goes out — no database, no extra network hop, nothing a visitor waits on. A background job moves those totals
into the database every 30 minutes, and the commands and tabs above read them back. So a fresh app shows
nothing until it's had some traffic, and the newest few minutes may not be in the rollup yet.
</Callout>

## Prefer the full command reference?

Everything here is a task-level walkthrough. For the complete list of `openship edge` subcommands and every flag
— the interactive panel, `up`/`repair`, all the domain, rule, and analytics options — see the
[Edge & monitoring](/docs/cli/edge) CLI reference.

## Related guides

<Cards>
  <Card title="Edge & monitoring (CLI)" href="/docs/cli/edge" description="Every openship edge subcommand and flag, in full." />
  <Card title="Custom domains" href="/docs/guides/custom-domains" description="DNS records, verification, and certificates end to end." />
  <Card title="Logs & monitoring" href="/docs/guides/logs-monitoring" description="The dashboard view of runtime logs, request logs, and traffic." />
  <Card title="Migrate from Coolify / Dokploy / Dokku" href="/docs/guides/migrate-from-coolify-dokploy-dokku" description="Bring an existing PaaS box across, edge and all." />
</Cards>
