nginx is the classic static reverse proxy — a generated `nginx.conf` forwards to
`app:6530` on the compose network. Right choice when TLS already terminates elsewhere
(Cloudflare, ALB, another edge) or you want a hand-edited config.

<Callout title="The one rule">
  Leave `images.proxy` unset until you need an edge in front of the app. Pin nginx for a static HTTP
  proxy; prefer [Caddy](/docs/recipes/caddy) or [Traefik](/docs/recipes/traefik) when you want
  automatic HTTPS in the same stack.
</Callout>

## Quick start

<Steps>

<Step>
### Pin the proxy

```typescript title="oke.config.ts"
images: {
  proxy: "nginx:1.31-alpine",
},
```

`create-oke` offers **nginx** in **Add a reverse proxy…?** (or `--proxy nginx`).

</Step>

<Step>
### Include the proxy layer

```bash
docker compose -f docker-compose.yml up -d
```

Generated `nginx.conf`: `upstream oke_app { server app:6530; }` with
`proxy_pass http://oke_app` on `:80`. The app host port (`6530`) is unpublished —
traffic enters through the proxy.

</Step>

<Step>
### Put TLS in front (optional)

Point Cloudflare / an ALB / another terminator at host `:80`, or switch the pin to
Caddy / Traefik when you want ACME inside Compose.

</Step>

</Steps>

## Required env

| Variable                | Required?  | Meaning                                                                     |
| ----------------------- | ---------- | --------------------------------------------------------------------------- |
| `OKE_PROXY_HOST`        | Optional   | Documented for the proxy role; nginx listens on any `Host` (`_`)            |
| `allowedHosts` (config) | Production | Must include the public hostname — see [Security](/docs/reference/security) |

## Data and backup

| Volume / bind | Path                                        | What it stores        |
| ------------- | ------------------------------------------- | --------------------- |
| Bind mount    | `./nginx.conf` → `/etc/nginx/nginx.conf:ro` | Generated site config |

**Backup means:** the `nginx.conf` is regenerated by `oke docker`. Keep overrides in
`compose.override.yml` or a replaced file if you customize routing.

## Production note

nginx here is **HTTP-only** on port 80 — no ACME, no Docker service discovery. For
automatic HTTPS use [Caddy](/docs/recipes/caddy). For `--scale app=N` use
[Traefik](/docs/recipes/traefik).

**Consequence:** `OKE_PROXY_URL` is `http://…` for this recipe (Caddy / Traefik use
`https://…`).

## Troubleshooting

<Accordions>
<Accordion title="App still publishes 6530">

You still have `6530:6530` on `app`. Confirm `images.proxy` is set and
`oke docker` regenerated compose so the app host bind was omitted.

</Accordion>
<Accordion title="502 Bad Gateway from nginx">

The upstream is `app:6530` on the `oke` network. Confirm the app service is healthy
and shares that network. Override `nginx.conf` only after checking the generated
upstream block.

</Accordion>
</Accordions>

## Learn more

- [Caddy](/docs/recipes/caddy) — automatic HTTPS alternative
- [Traefik](/docs/recipes/traefik) — multi-replica discovery via Docker labels
- [Security](/docs/reference/security) — `allowedHosts`

## Next

<Cards>
  <Card title="Caddy" description="Automatic HTTPS alternative." href="/docs/recipes/caddy" />
  <Card title="Traefik" description="Multi-replica alternative." href="/docs/recipes/traefik" />
  <Card
    title="PgDog"
    description="Pool Postgres behind the same stack."
    href="/docs/recipes/pgdog"
  />
</Cards>
