mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
feat(self-hosted): envoy is the default api gateway (#48153)
This commit is contained in:
1 parent
9596b5f3ed
commit
5a8eecf509
28 files changed
+255
-386
No files matched your search
@@ -20,7 +20,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config: [default, logs, envoy, rustfs, envoy-rustfs]
|
||||
config: [default, logs, kong, rustfs, kong-rustfs]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
@@ -51,12 +51,12 @@ jobs:
|
||||
yq -i '.services.supavisor.environment.RLIMIT_NOFILE=1024' docker-compose.yml
|
||||
if [ "${{ matrix.config }}" = "logs" ]; then
|
||||
docker compose -f docker-compose.yml -f docker-compose.logs.yml up --quiet-pull --wait --wait-timeout 180
|
||||
elif [ "${{ matrix.config }}" = "envoy" ]; then
|
||||
docker compose -f docker-compose.yml -f docker-compose.envoy.yml up --quiet-pull --wait --wait-timeout 180
|
||||
elif [ "${{ matrix.config }}" = "kong" ]; then
|
||||
docker compose -f docker-compose.yml -f docker-compose.kong.yml up --quiet-pull --wait --wait-timeout 180
|
||||
elif [ "${{ matrix.config }}" = "rustfs" ]; then
|
||||
docker compose -f docker-compose.yml -f docker-compose.rustfs.yml up --quiet-pull --wait --wait-timeout 180
|
||||
elif [ "${{ matrix.config }}" = "envoy-rustfs" ]; then
|
||||
docker compose -f docker-compose.yml -f docker-compose.envoy.yml -f docker-compose.rustfs.yml up --quiet-pull --wait --wait-timeout 180
|
||||
elif [ "${{ matrix.config }}" = "kong-rustfs" ]; then
|
||||
docker compose -f docker-compose.yml -f docker-compose.kong.yml -f docker-compose.rustfs.yml up --quiet-pull --wait --wait-timeout 180
|
||||
else
|
||||
docker compose up --quiet-pull --wait --wait-timeout 180
|
||||
fi
|
||||
|
||||
@@ -3085,7 +3085,7 @@ export const self_hosting: NavMenuConstant = {
|
||||
{ name: 'Overview', url: '/guides/self-hosting' },
|
||||
{ name: 'Deploy with Docker', url: '/guides/self-hosting/docker' },
|
||||
{ name: 'Configure new API keys', url: '/guides/self-hosting/self-hosted-auth-keys' },
|
||||
{ name: 'Enable Envoy API Gateway', url: '/guides/self-hosting/self-hosted-envoy' },
|
||||
{ name: 'Learn about API Gateway', url: '/guides/self-hosting/self-hosted-envoy' },
|
||||
{
|
||||
name: 'Add Reverse Proxy with HTTPS',
|
||||
url: '/guides/self-hosting/self-hosted-proxy-https',
|
||||
|
||||
@@ -123,7 +123,7 @@ For large migrations, consider adding `--transfers 4` to increase parallelism, o
|
||||
|
||||
</Admonition>
|
||||
|
||||
## Verify
|
||||
## Verify the copy
|
||||
|
||||
Compare object counts between source and destination:
|
||||
|
||||
|
||||
@@ -29,20 +29,9 @@ When connecting via an SSH tunnel to the Studio Docker container, the source IP
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kong"
|
||||
defaultActiveId="envoy"
|
||||
>
|
||||
|
||||
<TabPanel id="kong" label="Kong">
|
||||
|
||||
Determine the Docker bridge gateway IP on the host running your Supabase containers:
|
||||
|
||||
```sh
|
||||
docker inspect supabase-kong \
|
||||
--format '{{range .NetworkSettings.Networks}}{{println .Gateway}}{{end}}'
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="envoy" label="Envoy">
|
||||
|
||||
Determine the Docker bridge gateway IP on the host running your Supabase containers:
|
||||
@@ -54,6 +43,17 @@ docker inspect supabase-envoy \
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="kong" label="Kong">
|
||||
|
||||
Determine the Docker bridge gateway IP on the host running your Supabase containers:
|
||||
|
||||
```sh
|
||||
docker inspect supabase-kong \
|
||||
--format '{{range .NetworkSettings.Networks}}{{println .Gateway}}{{end}}'
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
This command will output an IP address, e.g., `172.18.0.1`.
|
||||
@@ -64,53 +64,9 @@ This command will output an IP address, e.g., `172.18.0.1`.
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kong"
|
||||
defaultActiveId="envoy"
|
||||
>
|
||||
|
||||
<TabPanel id="kong" label="Kong">
|
||||
|
||||
Add the IP address you discovered to the Kong configuration by editing the following section in `./volumes/api/kong.yml`:
|
||||
|
||||
1. Comment out the request-termination section
|
||||
2. Remove the # symbols from the entire section starting with `- name: cors`, including `deny: []`
|
||||
3. Add your local IP to the 'allow' list
|
||||
4. **Preserve the existing indentation** - YAML is whitespace-sensitive and the config will fail to load if it changes
|
||||
5. Your edited configuration should look like the example below:
|
||||
|
||||
```yaml name=volumes/api/kong.yml
|
||||
## MCP endpoint - local access
|
||||
- name: mcp
|
||||
_comment: 'MCP: /mcp -> http://studio:3000/api/mcp (local access)'
|
||||
url: http://studio:3000/api/mcp
|
||||
routes:
|
||||
- name: mcp
|
||||
strip_path: true
|
||||
paths:
|
||||
- /mcp
|
||||
plugins:
|
||||
# Block access to /mcp by default
|
||||
#- name: request-termination
|
||||
# config:
|
||||
# status_code: 403
|
||||
# message: "Access is forbidden."
|
||||
# Enable local access (danger zone!)
|
||||
# 1. Comment out the 'request-termination' section above
|
||||
# 2. Uncomment the entire section below, including 'deny'
|
||||
# 3. Add your local IPs to the 'allow' list
|
||||
- name: cors
|
||||
- name: ip-restriction
|
||||
config:
|
||||
allow:
|
||||
- 127.0.0.1
|
||||
- ::1
|
||||
# Add your Docker bridge gateway IP below
|
||||
- 172.18.0.1
|
||||
# Do not remove deny!
|
||||
deny: []
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="envoy" label="Envoy">
|
||||
|
||||
Add the IP address you discovered to the Envoy configuration by editing the `/mcp` route in `./volumes/api/envoy/lds.template.yaml`:
|
||||
@@ -177,39 +133,60 @@ Add the IP address you discovered to the Envoy configuration by editing the `/mc
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="kong" label="Kong">
|
||||
|
||||
Add the IP address you discovered to the Kong configuration by editing the following section in `./volumes/api/kong.yml`:
|
||||
|
||||
1. Comment out the request-termination section
|
||||
2. Remove the # symbols from the entire section starting with `- name: cors`, including `deny: []`
|
||||
3. Add your local IP to the 'allow' list
|
||||
4. **Preserve the existing indentation** - YAML is whitespace-sensitive and the config will fail to load if it changes
|
||||
5. Your edited configuration should look like the example below:
|
||||
|
||||
```yaml name=volumes/api/kong.yml
|
||||
## MCP endpoint - local access
|
||||
- name: mcp
|
||||
_comment: 'MCP: /mcp -> http://studio:3000/api/mcp (local access)'
|
||||
url: http://studio:3000/api/mcp
|
||||
routes:
|
||||
- name: mcp
|
||||
strip_path: true
|
||||
paths:
|
||||
- /mcp
|
||||
plugins:
|
||||
# Block access to /mcp by default
|
||||
#- name: request-termination
|
||||
# config:
|
||||
# status_code: 403
|
||||
# message: "Access is forbidden."
|
||||
# Enable local access (danger zone!)
|
||||
# 1. Comment out the 'request-termination' section above
|
||||
# 2. Uncomment the entire section below, including 'deny'
|
||||
# 3. Add your local IPs to the 'allow' list
|
||||
- name: cors
|
||||
- name: ip-restriction
|
||||
config:
|
||||
allow:
|
||||
- 127.0.0.1
|
||||
- ::1
|
||||
# Add your Docker bridge gateway IP below
|
||||
- 172.18.0.1
|
||||
# Do not remove deny!
|
||||
deny: []
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Step 3: Restart API gateway
|
||||
|
||||
After you've added the local IP address as above, restart your gateway:
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kong"
|
||||
>
|
||||
|
||||
<TabPanel id="kong" label="Kong">
|
||||
|
||||
```sh
|
||||
sh run.sh restart kong
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="envoy" label="Envoy">
|
||||
|
||||
```sh
|
||||
sh run.sh restart api-gw
|
||||
```
|
||||
|
||||
This assumes the Envoy override is already active (`sh run.sh config add envoy`).
|
||||
|
||||
</TabPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Step 4: Create the SSH tunnel
|
||||
|
||||
From your local machine, create an SSH tunnel to your Supabase host:
|
||||
@@ -268,7 +245,6 @@ Start your MCP client (Claude Code, Cursor, etc.) and verify access to the MCP t
|
||||
|
||||
If you are unable to connect to the MCP server:
|
||||
|
||||
1. Update Kong configuration file to the [latest version](https://github.com/supabase/supabase/blob/master/docker/volumes/api/kong.yml) and edit carefully
|
||||
2. Confirm the Docker bridge gateway IP is correctly added in `./volumes/api/kong.yml`
|
||||
3. Check Kong's logs for errors: `docker compose logs kong`
|
||||
4. Make sure your SSH tunnel is active
|
||||
1. Confirm the Docker bridge gateway IP is correctly added to the API gateway configuration
|
||||
2. Check the API gateway's logs for errors: `docker compose logs api-gw`
|
||||
3. Make sure your SSH tunnel is active
|
||||
@@ -11,13 +11,12 @@ You can configure self-hosted Supabase to use the [new API keys](/docs/guides/ge
|
||||
{/* supa-mdx-lint-disable-next-line Rule003Spelling */}
|
||||
|
||||
- Complete the [Docker setup guide](/docs/guides/self-hosting/docker) so that `JWT_SECRET`, `ANON_KEY`, and `SERVICE_ROLE_KEY` are set in your `.env` file. [Quick start (Linux)](/docs/guides/self-hosting/docker#quick-start-linux) handles this automatically; the manual path runs [`generate-keys.sh`](/docs/guides/self-hosting/docker#generate-keys-and-secrets).
|
||||
- If you are upgrading an existing self-hosted Supabase environment, make sure to check the [changelog](https://github.com/supabase/supabase/blob/master/docker/CHANGELOG.md) and add/update the following files:
|
||||
- If you are upgrading from a legacy self-hosted Supabase environment, make sure to check the [changelog](https://github.com/supabase/supabase/blob/master/docker/CHANGELOG.md#2026-03-16) and [add/update](/docs/guides/self-hosting/updating) the following files:
|
||||
- `.env.example` (merge new sections into your `.env` file)
|
||||
- `docker-compose.yml`
|
||||
- `utils/add-new-auth-keys.sh`
|
||||
- `utils/rotate-new-api-keys.sh`
|
||||
- `volumes/api/kong-entrypoint.sh`
|
||||
- `volumes/api/kong.yml`
|
||||
- `volumes/api/envoy/*`
|
||||
|
||||
## Adding the new keys
|
||||
|
||||
@@ -27,7 +26,7 @@ From your project directory where you have `docker-compose.yml`:
|
||||
sh utils/add-new-auth-keys.sh --update-env
|
||||
```
|
||||
|
||||
This generates new configuration environment variables and writes them to `.env`. Without `--update-env`, the script prints the values and prompts you interactively.
|
||||
This generates new configuration environment variables and writes them to `.env`. Omit `--update-env` to review and confirm the changes interactively.
|
||||
|
||||
<Admonition type="caution">
|
||||
|
||||
@@ -35,7 +34,7 @@ The script reads `JWT_SECRET` from `.env` and includes it as a symmetric key ins
|
||||
|
||||
</Admonition>
|
||||
|
||||
The following configuration should be uncommented in the `.env` file for the new authentication to work correctly:
|
||||
In addition, the following configuration is uncommented automatically by the script for the new authentication to work correctly:
|
||||
|
||||
```yaml name=docker-compose.yml
|
||||
auth:
|
||||
@@ -57,6 +56,11 @@ storage:
|
||||
environment:
|
||||
# JWKS for token verification (EC public + legacy symmetric)
|
||||
JWT_JWKS: ${JWT_JWKS:-{"keys":[]}}
|
||||
|
||||
functions:
|
||||
environment:
|
||||
# JWKS for token verification (EC public + legacy symmetric).
|
||||
SUPABASE_JWKS: ${JWT_JWKS:-{"keys":[]}}
|
||||
```
|
||||
|
||||
<Admonition type="caution">
|
||||
@@ -124,7 +128,7 @@ New variables default to empty values in `.env.example`. When empty, the API gat
|
||||
| `SUPABASE_PUBLISHABLE_KEY` | Opaque | **New:** Short random key with checksum. Replaces `ANON_KEY` for **client-side** use. |
|
||||
| `SUPABASE_SECRET_KEY` | Opaque | **New:** Short random key with checksum. Replaces `SERVICE_ROLE_KEY` for **server-side** use. |
|
||||
| `JWT_KEYS` | JSON array | **New:** JSON array of signing JWKs containing the new asymmetric key pair and the legacy symmetric key. Used by Auth to sign tokens. |
|
||||
| `JWT_JWKS` | JWKS (JSON) | **New:** Contains the new public key and the legacy symmetric key. Used by PostgREST, Realtime, and Storage to verify tokens. |
|
||||
| `JWT_JWKS` | JWKS (JSON) | **New:** Contains the new public key and the legacy symmetric key. Used by PostgREST, Realtime, Storage, and Functions. |
|
||||
|
||||
### Differences from the Supabase platform
|
||||
|
||||
@@ -187,93 +191,16 @@ Regenerating asymmetric keys invalidates all ES256 user sessions. Plan a mainten
|
||||
|
||||
## How it works
|
||||
|
||||
Below are a few notes on the details of the new authentication architecture.
|
||||
|
||||
### What client SDK sends
|
||||
|
||||
Every request via `supabase-js` includes two headers:
|
||||
Requests via `supabase-js` include two headers for every service except Edge Functions:
|
||||
|
||||
- `apikey` - the API key (`sb_` or legacy JWT)
|
||||
- `Authorization` - when unauthenticated, the client SDK copies the API key here (`Bearer sb_publishable_xxx` or `Bearer eyJ...`). When authenticated, this contains the user session JWT minted by Auth.
|
||||
|
||||
For **Realtime WebSocket** connections, the API key is sent as a `?apikey=` query parameter in the upgrade URL instead of an `apikey` header.
|
||||
|
||||
**Storage** and **Edge Functions** accept requests without an API key. These services handle their own authentication.
|
||||
**Storage** and **Edge Functions** also accept requests without an API key. These services handle their own authentication.
|
||||
|
||||
### Kong API gateway routing
|
||||
|
||||
Kong is configured with two consumers that each accept both the legacy and new API keys:
|
||||
|
||||
```yaml name=volumes/api/kong.yml
|
||||
consumers:
|
||||
- username: anon
|
||||
keyauth_credentials:
|
||||
- key: $SUPABASE_ANON_KEY # legacy HS256 JWT (ANON_KEY)
|
||||
- key: $SUPABASE_PUBLISHABLE_KEY # new opaque key (omitted when not configured)
|
||||
- username: service_role
|
||||
keyauth_credentials:
|
||||
- key: $SUPABASE_SERVICE_KEY # legacy HS256 JWT (SERVICE_ROLE_KEY)
|
||||
- key: $SUPABASE_SECRET_KEY # new opaque key (omitted when not configured)
|
||||
```
|
||||
|
||||
When new API keys have not been added yet, the `kong-entrypoint.sh` script removes the empty credential entries before Kong loads the config.
|
||||
|
||||
To assist with the authorization flows a specialized configuration in `kong.yml` substitutes internal, gateway-level-only pre-signed JWTs for `sb_publishable` and `sb_secret` API keys. These pre-signed JWTs are also auto-configured in `.env` but **should not** be used in any application code.
|
||||
|
||||
| Route | Service | API key required | Header substitution |
|
||||
| -------------------------- | -------------------- | ---------------- | ------------------- |
|
||||
| `/auth/v1/*` | Auth | Yes | `Authorization` |
|
||||
| `/rest/v1/*` | PostgREST | Yes | `Authorization` |
|
||||
| `/graphql/v1` | PostgREST | Yes | `Authorization` |
|
||||
| `/realtime/v1/api/tenants` | Realtime (REST) | Denied (blocked) | - |
|
||||
| `/realtime/v1/api/openapi` | Realtime (REST) | Denied (blocked) | - |
|
||||
| `/realtime/v1/api/*` | Realtime (REST) | Yes | `Authorization` |
|
||||
| `/realtime/v1/*` | Realtime (WebSocket) | Yes | `x-api-key` |
|
||||
| `/storage/v1/*` | Storage | No | `Authorization` |
|
||||
| `/functions/v1/*` | Edge Functions | No | - |
|
||||
|
||||
### Request flows
|
||||
|
||||
The API gateway (Kong) configuration has the logic to decide what `Authorization` header the upstream service, such as Auth, receives. The logic handles two cases: requests that only carry an API key (no user session), and requests that carry a user session JWT.
|
||||
|
||||
#### Unauthenticated requests (API key only, no user session JWT)
|
||||
|
||||
When the client sends only an `apikey` header with the API key (no `Authorization` header), or also the API key duplicated in `Authorization` by `supabase-js`:
|
||||
|
||||
1. The client sends `apikey: sb_publishable_xxx` (or legacy `apikey: eyJ...`).
|
||||
2. The API gateway checks the key and identifies the consumer (`anon` or `service_role`).
|
||||
3. The API gateway inspects the `Authorization` header. Since it is either absent or starts with `Bearer sb_` (an opaque key, not a session JWT), the plugin replaces it:
|
||||
- **The new `sb_` key:** `Authorization` header is set to the internal pre-signed ES256 JWT that corresponds to the role.
|
||||
- **The Legacy JWT key:** `Authorization` header is set to the legacy HS256 JWT (the `apikey` value is copied as-is).
|
||||
4. The upstream service receives a valid JWT in `Authorization` and verifies it using `JWT_JWKS` (or `JWT_SECRET`).
|
||||
|
||||
#### Authenticated requests (user session JWT)
|
||||
|
||||
When the client has previously signed in through Auth and has a valid user session JWT token:
|
||||
|
||||
1. The client sends `Authorization: Bearer eyJ...` (a JWT session token from Auth) alongside `apikey: sb_publishable_xxx` (or legacy `apikey: eyJ...`).
|
||||
2. The API gateway checks the API key and identifies the consumer.
|
||||
3. The API gateway inspects the `Authorization` header. Since it exists and does **not** start with `Bearer sb_` (it's a real JWT, not an `sb_` API key), the plugin **passes it through unchanged**. This works the same way regardless of whether the `apikey` is a new `sb_` key or a legacy JWT - the gateway only looks at the `Authorization` header to decide whether a user session is present.
|
||||
4. The upstream service verifies the session JWT. If Auth signed it with ES256 (when `JWT_KEYS` is configured), verification uses the EC public key. If Auth signed it with HS256 (legacy), verification uses the symmetric key. Both keys are available in `JWT_JWKS`.
|
||||
|
||||
The `request-transformer` expression in `kong.yml` implements this as a single Lua conditional:
|
||||
|
||||
```lua
|
||||
-- Pseudocode for the Authorization header logic:
|
||||
if authorization exists AND does not start with "Bearer sb_" then
|
||||
-- User session JWT: pass through unchanged
|
||||
keep authorization
|
||||
elseif apikey matches secret key then
|
||||
-- Replace with pre-signed service_role ES256 JWT
|
||||
set authorization = "Bearer <service_role ES256 JWT>"
|
||||
elseif apikey matches publishable key then
|
||||
-- Replace with pre-signed anon ES256 JWT
|
||||
set authorization = "Bearer <anon ES256 JWT>"
|
||||
else
|
||||
-- Legacy JWT key: copy apikey as authorization
|
||||
set authorization = apikey
|
||||
end
|
||||
```
|
||||
For details on how the API gateway routes and authenticates requests, see the [Envoy API Gateway guide](/docs/guides/self-hosting/self-hosted-envoy#authentication).
|
||||
|
||||
## Additional resources
|
||||
|
||||
@@ -287,3 +214,4 @@ On GitHub:
|
||||
|
||||
- [Upcoming changes to Supabase API Keys (Discussion #29260)](https://github.com/orgs/supabase/discussions/29260)
|
||||
- [Supabase Auth: Asymmetric Keys support (Discussion #29289)](https://github.com/orgs/supabase/discussions/29289)
|
||||
- [Self-hosted Supabase: Envoy becomes the default API gateway (Discussion #48048)](https://github.com/orgs/supabase/discussions/48048)
|
||||
@@ -4,39 +4,17 @@ description: 'Architecture and configuration of the Envoy API gateway for self-h
|
||||
subtitle: 'Architecture and configuration of the Envoy API gateway for self-hosted Supabase.'
|
||||
---
|
||||
|
||||
Self-hosted Supabase ships with an optional [Envoy](https://www.envoyproxy.io/)-based API gateway. It accepts incoming client requests, routes them to internal services (Auth, PostgREST, Realtime, Storage, Edge Functions, postgres-meta, Studio), and enforces API key authentication by translating opaque `sb_` keys into the internal credentials used by those services.
|
||||
Self-hosted Supabase uses an [Envoy](https://www.envoyproxy.io/)-based API gateway by default. It accepts incoming client requests, routes them to internal services (Auth, PostgREST, Realtime, Storage, Edge Functions, postgres-meta, Studio), and enforces API key authentication by translating opaque `sb_` keys into the internal credentials used by those services.
|
||||
|
||||
This guide explains the architecture, configuration layout, and security posture of the Envoy gateway for operators who want to understand or customize it. It is not an Envoy tutorial - for reference on filters, routes, and clusters, see the [Envoy documentation](https://www.envoyproxy.io/docs/envoy/latest/).
|
||||
|
||||
## Before you begin
|
||||
|
||||
- Complete the [Self-Hosting with Docker](/docs/guides/self-hosting/docker) setup
|
||||
- To enable opaque `sb_` key translation, see [New API Keys and Asymmetric Authentication](/docs/guides/self-hosting/self-hosted-auth-keys)
|
||||
|
||||
{/* supa-mdx-lint-disable-next-line Rule001HeadingCase */}
|
||||
|
||||
## Enabling the Envoy gateway
|
||||
## Using the gateway
|
||||
|
||||
The Envoy gateway is provided as a Docker Compose override.
|
||||
Envoy is the default API gateway and runs automatically when you start the stack - no extra configuration is required.
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
If your stack is already running from the initial setup, bring it down first with `sh run.sh stop`.
|
||||
|
||||
</Admonition>
|
||||
|
||||
Enable the Envoy override, then start the stack:
|
||||
|
||||
```sh
|
||||
sh run.sh config add envoy
|
||||
sh run.sh start
|
||||
```
|
||||
|
||||
The override disables the default Kong gateway and starts Envoy on the same port (default `8000`). It also reconfigures the Functions service to wait for Envoy via a dependency.
|
||||
|
||||
Envoy is registered as the `api-gw` service and also exposes `kong` as a network alias; the base Kong service likewise exposes `api-gw`. Either hostname resolves to whichever gateway is currently active, so internal configs that hardcode `kong:8000` (for example, in Edge Functions or Studio) keep working without changes.
|
||||
|
||||
### Verify
|
||||
Envoy is registered as the `api-gw` service and also exposes `envoy` and `kong` as network aliases to help transition from legacy configurations.
|
||||
|
||||
Confirm the gateway is routing requests and enforcing API keys:
|
||||
|
||||
@@ -231,16 +209,15 @@ If you customize the `cors:` block in `lds.template.yaml` to enable `allow_crede
|
||||
|
||||
The gateway is configured with these production-oriented settings:
|
||||
|
||||
| Setting | Purpose |
|
||||
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `normalize_path: true`, `merge_slashes: true` | Prevents path-confusion bypass of RBAC prefix rules |
|
||||
| `path_with_escaped_slashes_action: REJECT_REQUEST` | Rejects requests that contain URL-encoded slashes in the path |
|
||||
| `use_remote_address: true` | Treats Envoy as an edge proxy: uses the peer connection IP as the trusted client address (rather than trusting client-supplied `X-Forwarded-For`) and strips untrusted `x-envoy-*` request headers |
|
||||
| `headers_with_underscores_action: REJECT_REQUEST` | Blocks header smuggling attacks that exploit underscore-vs-hyphen normalization |
|
||||
| `per_connection_buffer_limit_bytes: 32768` | Caps per-connection buffer memory to 32 KiB |
|
||||
| `max_active_downstream_connections: 30000` | Overload manager limit on total downstream connections |
|
||||
| Admin interface bound to `127.0.0.1:9901` | The admin API is reachable only from inside the container, not from other containers or the host |
|
||||
| Image pinned to `envoyproxy/envoy:v1.37.2` (or newer) | Includes published security patches for Envoy 1.37.x |
|
||||
| Setting | Purpose |
|
||||
| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `normalize_path: true`, `merge_slashes: true` | Prevents path-confusion bypass of RBAC prefix rules |
|
||||
| `path_with_escaped_slashes_action: REJECT_REQUEST` | Rejects requests that contain URL-encoded slashes in the path |
|
||||
| `use_remote_address: true` | Treats Envoy as an edge proxy: uses the peer connection IP as the trusted client address (rather than trusting client-supplied `X-Forwarded-For`) and strips untrusted `x-envoy-*` request headers |
|
||||
| `headers_with_underscores_action: REJECT_REQUEST` | Blocks header smuggling attacks that exploit underscore-vs-hyphen normalization |
|
||||
| `per_connection_buffer_limit_bytes: 32768` | Caps per-connection buffer memory to 32 KiB |
|
||||
| `max_active_downstream_connections: 30000` | Overload manager limit on total downstream connections |
|
||||
| Admin interface bound to `127.0.0.1:9901` | The admin API is reachable only from inside the container, not from other containers or the host |
|
||||
|
||||
<Admonition type="caution">
|
||||
|
||||
@@ -260,7 +237,7 @@ All routing, filter, and cluster changes are made in the YAML files under `./vol
|
||||
|
||||
- **Adding or modifying routes.** Edit `lds.template.yaml`. Routes are ordered - place new routes before the catch-all `/` route to ensure they match. Keep per-route `basic_auth: disabled` for API routes and set an appropriate RBAC override if the route should bypass the global policy.
|
||||
- **Adding a new upstream service.** Add a cluster definition to `cds.yaml` with the service's DNS name and port, then reference it from a route's `cluster:` field.
|
||||
- **Adding a new environment variable.** Placeholders in the template use the `${VAR_NAME}` form. If you add a placeholder, update both `docker-compose.envoy.yml` (to pass the variable into the container) and `docker-entrypoint.sh` (to substitute it with `sed`).
|
||||
- **Adding a new environment variable.** Placeholders in the template use the `${VAR_NAME}` form. If you add a placeholder, update both the `api-gw` service in `docker-compose.yml` (to pass the variable into the container) and `docker-entrypoint.sh` (to substitute it with `sed`).
|
||||
- **Applying changes.** Envoy reads the rendered `lds.yaml` from the filesystem. Configuration changes require restarting the container so the entrypoint re-renders the template:
|
||||
|
||||
```sh
|
||||
@@ -323,3 +300,4 @@ The access log format is a standard combined log with the request method, origin
|
||||
- [New API Keys and Asymmetric Authentication](/docs/guides/self-hosting/self-hosted-auth-keys) - Background on opaque keys and asymmetric JWTs
|
||||
- [Configure Reverse Proxy and HTTPS](/docs/guides/self-hosting/self-hosted-proxy-https) - Caddy or Nginx in front of the gateway
|
||||
- [Envoy documentation](https://www.envoyproxy.io/docs/envoy/latest/) - Filter, route, and cluster reference
|
||||
- [Self-hosted Supabase: Envoy becomes the default API gateway (Discussion #48048)](https://github.com/orgs/supabase/discussions/48048)
|
||||
@@ -81,8 +81,7 @@ functions:
|
||||
env_file:
|
||||
- .env.functions
|
||||
environment:
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
SUPABASE_URL: http://kong:8000
|
||||
# ... existing variables ...
|
||||
```
|
||||
|
||||
<Admonition type="caution">
|
||||
@@ -106,9 +105,7 @@ functions:
|
||||
environment:
|
||||
# Custom variables
|
||||
MY_CUSTOM_VAR: ${MY_CUSTOM_VAR}
|
||||
# Required variables
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
SUPABASE_URL: http://kong:8000
|
||||
# ... existing variables ...
|
||||
```
|
||||
|
||||
Then define `MY_CUSTOM_VAR` in your main `.env` file, or specify the value directly.
|
||||
@@ -127,7 +124,7 @@ The functions service is pre-configured with the following environment variables
|
||||
|
||||
| Variable | Value | Purpose |
|
||||
| --------------------------- | --------------------------------- | ------------------------------------------------- |
|
||||
| `SUPABASE_URL` | `http://kong:8000` | Internal API gateway URL |
|
||||
| `SUPABASE_URL` | `http://api-gw:8000` | Internal API gateway URL |
|
||||
| `SUPABASE_PUBLIC_URL` | `http(s)://<your-domain>` | Base URL for accessing Supabase from the Internet |
|
||||
| `JWT_SECRET` | `your-jwt-secret` | Legacy symmetric encryption key for JWTs |
|
||||
| `SUPABASE_ANON_KEY` | `your-anon-key` | Client-side API key (`anon` role). |
|
||||
@@ -135,6 +132,7 @@ The functions service is pre-configured with the following environment variables
|
||||
| `SUPABASE_DB_URL` | `postgresql://...` | Postgres connection string |
|
||||
| `SUPABASE_PUBLISHABLE_KEYS` | `{"default":"sb_publishable_...}` | New publishable API key |
|
||||
| `SUPABASE_SECRET_KEYS` | `{"default":"sb_secret_...}` | New secret API key |
|
||||
| `SUPABASE_JWKS` | `{"keys":[{...}]}` | JWKS used to verify JWTs issued by Auth |
|
||||
|
||||
Here's an example function that queries a table using `@supabase/supabase-js`:
|
||||
|
||||
|
||||
@@ -33,12 +33,6 @@ If you already run [HAProxy](https://www.haproxy.com/), [Traefik](https://traefi
|
||||
|
||||
</Admonition>
|
||||
|
||||
<Admonition type="note" title="Using Envoy instead of Kong?">
|
||||
|
||||
Envoy is an optional [API gateway](/docs/guides/self-hosting/self-hosted-envoy), enabled via the `docker-compose.envoy.yml` override. If you already run Envoy instead of Kong, edit `docker-compose.caddy.yml` or `docker-compose.nginx.yml` to comment out the `kong:` block and uncomment the `api-gw:` block (and the matching `depends_on` entry) so the reverse proxy sits in front of Envoy.
|
||||
|
||||
</Admonition>
|
||||
|
||||
### Step 1: Update environment variables
|
||||
|
||||
Update the URL configuration in your `.env` file to use your HTTPS domain:
|
||||
@@ -51,7 +45,7 @@ SITE_URL=https://<your-app-domain>
|
||||
|
||||
<Admonition type="note" title="What your-app-domain means">
|
||||
|
||||
`<your-app-domain>` is the URL of your own frontend application (where users land after signing in) - not your Supabase instance. It's often a different domain, and possibly a different service entirely, from `<your-domain>` used above.
|
||||
`<your-app-domain>` is the URL of your own frontend application where users land after signing in - not your Supabase instance.
|
||||
|
||||
</Admonition>
|
||||
|
||||
@@ -126,7 +120,7 @@ Self-signed certificates trigger browser warnings and are rejected by most OAuth
|
||||
|
||||
</Admonition>
|
||||
|
||||
For development or internal networks where you cannot use Let's Encrypt, here's how you can configure Kong (the current default API gateway) to serve HTTPS directly using self-signed certificates.
|
||||
For development or internal networks where you cannot use Let's Encrypt, here's how you can configure the legacy Kong API gateway to serve HTTPS directly using self-signed certificates. This requires the Kong override (`sh run.sh config add kong`); the default Envoy gateway does not terminate TLS, so use Caddy or Nginx above instead.
|
||||
|
||||
### Step 1: Generate a self-signed certificate
|
||||
|
||||
@@ -147,20 +141,23 @@ openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
|
||||
Comment out Kong's **HTTP** port mapping in `docker-compose.yml`:
|
||||
|
||||
```yaml name=docker-compose.yml
|
||||
kong:
|
||||
```yaml name=docker-compose.kong.yml
|
||||
api-gw:
|
||||
# ...
|
||||
ports:
|
||||
#- ${KONG_HTTP_PORT}:8000/tcp
|
||||
# prettier-ignore
|
||||
ports: !override
|
||||
#- ${API_GW_HTTP_PORT:-${KONG_HTTP_PORT:-8000}}:8000/tcp
|
||||
- ${KONG_HTTPS_PORT:-8443}:8443/tcp
|
||||
```
|
||||
|
||||
Uncomment the certificate volume mounts and SSL environment variables in `docker-compose.yml`:
|
||||
|
||||
```yaml name=docker-compose.yml
|
||||
kong:
|
||||
```yaml name=docker-compose.kong.yml
|
||||
api-gw:
|
||||
# ... existing configuration ...
|
||||
volumes:
|
||||
volumes: !override
|
||||
- ./volumes/api/kong.yml:/home/kong/temp.yml:ro,z
|
||||
- ./volumes/api/kong-entrypoint.sh:/home/kong/kong-entrypoint.sh:ro,z
|
||||
- ./volumes/api/server.crt:/home/kong/server.crt:ro
|
||||
- ./volumes/api/server.key:/home/kong/server.key:ro
|
||||
environment:
|
||||
|
||||
@@ -154,7 +154,7 @@ storage:
|
||||
REGION: your-region
|
||||
```
|
||||
|
||||
## Verify
|
||||
## Verify the setup
|
||||
|
||||
{/* supa-mdx-lint-disable-next-line Rule003Spelling */}
|
||||
|
||||
|
||||
@@ -181,7 +181,8 @@ Long-running deployments are usually assembled from several upstream points over
|
||||
4. Back up your database separately - `update.sh` backs up configuration only.
|
||||
5. Apply the update, then resolve conflicts. Most conflicts will be in "vendor files" you never meant to own - `run.sh`, `setup.sh`, `tests/*`, the override templates. For those, copy the new version as-is. The conflicts that need manual editing are usually in the compose configuration you deliberately changed. Your `.env` is never conflicted - `update.sh` appends new keys for you to review separately. The tool surfaces everything for you to triage. Refer to [Resolving conflicts](#resolving-conflicts) for more details.
|
||||
6. Handle Postgres. Postgres 17 became the default in v0.6.0. If you are still on Postgres 15, do not recreate straight onto 17 - follow [Upgrade to Postgres 17](/docs/guides/self-hosting/postgres-upgrade-17), or pin Postgres 15 with the `docker-compose.pg15.yml` override. Read the [changelog](https://github.com/supabase/supabase/blob/master/docker/CHANGELOG.md) for the breaking changes across the range you are crossing.
|
||||
7. Run `sh run.sh pull`, then `sh run.sh recreate`.
|
||||
7. Upgrade the API gateway. Envoy became the default in v0.8.0, replacing Kong. If you customized `volumes/api/kong.yml` or the gateway service, use the Kong override to keep Kong; otherwise the merge switches you to Envoy. The gateway service is renamed from `kong` to `api-gw`, but the `kong` hostname alias still resolves.
|
||||
8. Run `sh run.sh pull`, then `sh run.sh recreate`.
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
|
||||
+6
-1
@@ -341,7 +341,12 @@ GOOGLE_PROJECT_NUMBER=GOOGLE_PROJECT_NUMBER
|
||||
# API gateway
|
||||
############
|
||||
|
||||
# Kong configuration variables
|
||||
# Host port the API gateway (Envoy by default) listens on.
|
||||
API_GW_HTTP_PORT=8000
|
||||
|
||||
# Kong gateway override only (sh run.sh config add kong). KONG_HTTPS_PORT is
|
||||
# Kong's built-in HTTPS listener; KONG_HTTP_PORT is kept as a fallback for
|
||||
# API_GW_HTTP_PORT so existing .env files continue to work.
|
||||
KONG_HTTP_PORT=8000
|
||||
KONG_HTTPS_PORT=8443
|
||||
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ The guide covers:
|
||||
This Docker Compose configuration includes the following services:
|
||||
|
||||
- **[Studio](https://github.com/supabase/supabase/tree/master/apps/studio)** - A dashboard for managing your self-hosted Supabase project
|
||||
- **[Kong](https://github.com/Kong/kong)** - Kong API gateway
|
||||
- **[Envoy](https://www.envoyproxy.io/)** - API gateway (default; Kong is available as an optional override via `sh run.sh config add kong`)
|
||||
- **[Auth](https://github.com/supabase/auth)** - JWT-based authentication API for user sign-ups, logins, and session management
|
||||
- **[PostgREST](https://github.com/PostgREST/postgrest)** - Web server that turns your PostgreSQL database directly into a RESTful API
|
||||
- **[Realtime](https://github.com/supabase/realtime)** - Elixir server that listens to PostgreSQL database changes and broadcasts them over websockets
|
||||
|
||||
@@ -1,22 +1,13 @@
|
||||
services:
|
||||
|
||||
# By default, Kong is used as the API gateway and its ports/env are reset
|
||||
# below so Caddy can terminate TLS in front of it.
|
||||
#
|
||||
# When running Envoy instead, e.g.:
|
||||
# docker compose -f docker-compose.yml -f docker-compose.envoy.yml \
|
||||
# -f docker-compose.caddy.yml up -d
|
||||
# comment out the `kong:` block below and uncomment the `api-gw:` block
|
||||
# (and the matching `depends_on` entry further down) so Caddy sits in front
|
||||
# of Envoy rather than Kong.
|
||||
|
||||
#api-gw:
|
||||
# ports: !reset []
|
||||
|
||||
kong:
|
||||
# Caddy terminates TLS and forwards to the API gateway (api-gw) on port 8000,
|
||||
# so the gateway's own host port binding is removed here. This works for
|
||||
# either gateway, since the service is named api-gw in both cases.
|
||||
api-gw:
|
||||
ports: !reset []
|
||||
environment:
|
||||
KONG_PORT_MAPS: "443:8000,443:8443"
|
||||
# When using the Kong override uncomment the following:
|
||||
#environment:
|
||||
# KONG_PORT_MAPS: "443:8000,443:8443"
|
||||
|
||||
caddy:
|
||||
container_name: supabase-caddy
|
||||
@@ -27,9 +18,7 @@ services:
|
||||
- "443:443"
|
||||
- "443:443/udp"
|
||||
depends_on:
|
||||
#api-gw:
|
||||
# condition: service_healthy
|
||||
kong:
|
||||
api-gw:
|
||||
condition: service_healthy
|
||||
studio:
|
||||
condition: service_healthy
|
||||
|
||||
@@ -1,51 +1,15 @@
|
||||
# Envoy override for Kong
|
||||
# Usage: docker compose -f docker-compose.yml -f docker-compose.envoy.yml up
|
||||
# DEPRECATED: Envoy is now the default API gateway defined directly in
|
||||
# docker-compose.yml, so this override is no longer needed and does nothing.
|
||||
#
|
||||
# This no-op shim is kept for one release cycle so existing COMPOSE_FILE
|
||||
# entries referencing it do not break. Remove it from your configuration:
|
||||
#
|
||||
# sh run.sh config remove envoy
|
||||
#
|
||||
# To run Kong instead of Envoy, use the Kong override:
|
||||
#
|
||||
# sh run.sh config add kong
|
||||
#
|
||||
# This file will be removed in a future release.
|
||||
|
||||
services:
|
||||
# Disable the original Kong service
|
||||
kong:
|
||||
profiles:
|
||||
- disabled
|
||||
|
||||
# Rewire dependencies that require Kong to Envoy
|
||||
functions:
|
||||
depends_on: !override
|
||||
api-gw:
|
||||
condition: service_healthy
|
||||
|
||||
# Envoy API gateway
|
||||
api-gw:
|
||||
container_name: supabase-envoy
|
||||
image: envoyproxy/envoy:v1.39.0
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- ${KONG_HTTP_PORT}:8000/tcp
|
||||
volumes:
|
||||
- ./volumes/api/envoy/envoy.yaml:/etc/envoy/envoy.yaml:ro
|
||||
- ./volumes/api/envoy/cds.yaml:/etc/envoy/cds.yaml:ro
|
||||
- ./volumes/api/envoy/lds.template.yaml:/etc/envoy/lds.template.yaml:ro
|
||||
- ./volumes/api/envoy/docker-entrypoint.sh:/docker-entrypoint.sh:ro
|
||||
depends_on:
|
||||
studio:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
ANON_KEY: ${ANON_KEY}
|
||||
SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY}
|
||||
SUPABASE_PUBLISHABLE_KEY: ${SUPABASE_PUBLISHABLE_KEY:-}
|
||||
SUPABASE_SECRET_KEY: ${SUPABASE_SECRET_KEY:-}
|
||||
ANON_KEY_ASYMMETRIC: ${ANON_KEY_ASYMMETRIC:-}
|
||||
SERVICE_ROLE_KEY_ASYMMETRIC: ${SERVICE_ROLE_KEY_ASYMMETRIC:-}
|
||||
DASHBOARD_USERNAME: ${DASHBOARD_USERNAME}
|
||||
DASHBOARD_PASSWORD: ${DASHBOARD_PASSWORD}
|
||||
entrypoint: ["/bin/sh", "/docker-entrypoint.sh"]
|
||||
healthcheck:
|
||||
# Using a TCP port check because this image does not include curl or wget.
|
||||
test: ["CMD-SHELL", "timeout 1 bash -c '</dev/tcp/127.0.0.1/8000'"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
networks:
|
||||
default:
|
||||
aliases:
|
||||
- kong
|
||||
- envoy
|
||||
services: {}
|
||||
@@ -0,0 +1,49 @@
|
||||
# Kong API gateway override.
|
||||
#
|
||||
# Replaces the default Envoy gateway with Kong. Enable with:
|
||||
# sh run.sh config add kong
|
||||
# sh run.sh start
|
||||
#
|
||||
# This overrides the `api-gw` service in place, so its dependents (functions),
|
||||
# the reverse-proxy overrides (caddy/nginx), and the `kong`/`envoy` network
|
||||
# aliases keep working unchanged. Kong re-adds an HTTPS listener on 8443.
|
||||
|
||||
services:
|
||||
api-gw:
|
||||
container_name: supabase-kong
|
||||
image: kong/kong:3.9.3
|
||||
healthcheck:
|
||||
test: !override ["CMD", "kong", "health"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
ports: !override
|
||||
- ${API_GW_HTTP_PORT:-${KONG_HTTP_PORT:-8000}}:8000/tcp
|
||||
- ${KONG_HTTPS_PORT:-8443}:8443/tcp
|
||||
volumes: !override
|
||||
- ./volumes/api/kong.yml:/home/kong/temp.yml:ro,z
|
||||
- ./volumes/api/kong-entrypoint.sh:/home/kong/kong-entrypoint.sh:ro,z
|
||||
#- ./volumes/api/server.crt:/home/kong/server.crt:ro
|
||||
#- ./volumes/api/server.key:/home/kong/server.key:ro
|
||||
environment: !override
|
||||
KONG_DATABASE: "off"
|
||||
KONG_DECLARATIVE_CONFIG: /usr/local/kong/kong.yml
|
||||
KONG_ROUTER_FLAVOR: expressions
|
||||
KONG_DNS_ORDER: LAST,A,CNAME
|
||||
KONG_DNS_NOT_FOUND_TTL: 1
|
||||
KONG_DNS_VALID_TTL: 5
|
||||
KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth,request-termination,ip-restriction,post-function
|
||||
KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k
|
||||
KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k
|
||||
KONG_PROXY_ACCESS_LOG: /dev/stdout combined
|
||||
#KONG_SSL_CERT: /home/kong/server.crt
|
||||
#KONG_SSL_CERT_KEY: /home/kong/server.key
|
||||
SUPABASE_ANON_KEY: ${ANON_KEY}
|
||||
SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
|
||||
SUPABASE_PUBLISHABLE_KEY: ${SUPABASE_PUBLISHABLE_KEY:-}
|
||||
SUPABASE_SECRET_KEY: ${SUPABASE_SECRET_KEY:-}
|
||||
ANON_KEY_ASYMMETRIC: ${ANON_KEY_ASYMMETRIC:-}
|
||||
SERVICE_ROLE_KEY_ASYMMETRIC: ${SERVICE_ROLE_KEY_ASYMMETRIC:-}
|
||||
DASHBOARD_USERNAME: ${DASHBOARD_USERNAME}
|
||||
DASHBOARD_PASSWORD: ${DASHBOARD_PASSWORD}
|
||||
entrypoint: !override ["/bin/sh", "/home/kong/kong-entrypoint.sh"]
|
||||
@@ -1,22 +1,13 @@
|
||||
services:
|
||||
|
||||
# By default, Kong is used as the API gateway and its ports/env are reset
|
||||
# below so nginx can terminate TLS in front of it.
|
||||
#
|
||||
# When running Envoy instead, e.g.:
|
||||
# docker compose -f docker-compose.yml -f docker-compose.envoy.yml \
|
||||
# -f docker-compose.nginx.yml up -d
|
||||
# comment out the `kong:` block below and uncomment the `api-gw:` block
|
||||
# (and the matching `depends_on` entry further down) so nginx sits in front
|
||||
# of Envoy rather than Kong.
|
||||
|
||||
#api-gw:
|
||||
# ports: !reset []
|
||||
|
||||
kong:
|
||||
# nginx terminates TLS and forwards to the API gateway (api-gw) on port 8000,
|
||||
# so the gateway's own host port binding is removed here. This works for
|
||||
# either gateway, since the service is named api-gw in both cases.
|
||||
api-gw:
|
||||
ports: !reset []
|
||||
environment:
|
||||
KONG_PORT_MAPS: "443:8000,443:8443"
|
||||
# When using the Kong override, uncomment the following:
|
||||
#environment:
|
||||
# KONG_PORT_MAPS: "443:8000,443:8443"
|
||||
|
||||
nginx:
|
||||
container_name: supabase-nginx
|
||||
@@ -26,9 +17,7 @@ services:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
depends_on:
|
||||
#api-gw:
|
||||
# condition: service_healthy
|
||||
kong:
|
||||
api-gw:
|
||||
condition: service_healthy
|
||||
studio:
|
||||
condition: service_healthy
|
||||
|
||||
+24
-32
@@ -48,7 +48,7 @@ services:
|
||||
DEFAULT_PROJECT_NAME: ${STUDIO_DEFAULT_PROJECT}
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||
|
||||
SUPABASE_URL: http://kong:8000
|
||||
SUPABASE_URL: http://api-gw:8000
|
||||
SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL}
|
||||
SUPABASE_ANON_KEY: ${ANON_KEY}
|
||||
SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
|
||||
@@ -65,53 +65,45 @@ services:
|
||||
- ./volumes/snippets:/app/snippets:z
|
||||
- ./volumes/functions:/app/edge-functions:ro,z
|
||||
|
||||
kong:
|
||||
container_name: supabase-kong
|
||||
image: kong/kong:3.9.3
|
||||
# Envoy is the default API gateway
|
||||
# See: https://github.com/orgs/supabase/discussions/48048
|
||||
api-gw:
|
||||
container_name: supabase-envoy
|
||||
image: envoyproxy/envoy:v1.39.0
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
default:
|
||||
# Expose `envoy` and `kong` as network aliases, so internal configs
|
||||
# that reference either hostname resolve to whichever gateway is active.
|
||||
aliases:
|
||||
- api-gw
|
||||
- envoy
|
||||
- kong
|
||||
healthcheck:
|
||||
test: ["CMD", "kong", "health"]
|
||||
interval: 5s
|
||||
# Using a TCP port check because this image does not include curl or wget.
|
||||
test: ["CMD-SHELL", "timeout 1 bash -c '</dev/tcp/127.0.0.1/8000'"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
retries: 3
|
||||
depends_on:
|
||||
studio:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- ${KONG_HTTP_PORT}:8000/tcp
|
||||
- ${KONG_HTTPS_PORT}:8443/tcp
|
||||
- ${API_GW_HTTP_PORT:-${KONG_HTTP_PORT:-8000}}:8000/tcp
|
||||
volumes:
|
||||
- ./volumes/api/kong.yml:/home/kong/temp.yml:ro,z
|
||||
- ./volumes/api/kong-entrypoint.sh:/home/kong/kong-entrypoint.sh:ro,z
|
||||
#- ./volumes/api/server.crt:/home/kong/server.crt:ro
|
||||
#- ./volumes/api/server.key:/home/kong/server.key:ro
|
||||
- ./volumes/api/envoy/envoy.yaml:/etc/envoy/envoy.yaml:ro
|
||||
- ./volumes/api/envoy/cds.yaml:/etc/envoy/cds.yaml:ro
|
||||
- ./volumes/api/envoy/lds.template.yaml:/etc/envoy/lds.template.yaml:ro
|
||||
- ./volumes/api/envoy/docker-entrypoint.sh:/docker-entrypoint.sh:ro
|
||||
environment:
|
||||
KONG_DATABASE: "off"
|
||||
KONG_DECLARATIVE_CONFIG: /usr/local/kong/kong.yml
|
||||
KONG_ROUTER_FLAVOR: expressions
|
||||
# https://github.com/supabase/cli/issues/14
|
||||
KONG_DNS_ORDER: LAST,A,CNAME
|
||||
KONG_DNS_NOT_FOUND_TTL: 1
|
||||
KONG_DNS_VALID_TTL: 5
|
||||
KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth,request-termination,ip-restriction,post-function
|
||||
KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k
|
||||
KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k
|
||||
KONG_PROXY_ACCESS_LOG: /dev/stdout combined
|
||||
#KONG_SSL_CERT: /home/kong/server.crt
|
||||
#KONG_SSL_CERT_KEY: /home/kong/server.key
|
||||
SUPABASE_ANON_KEY: ${ANON_KEY}
|
||||
SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
|
||||
ANON_KEY: ${ANON_KEY}
|
||||
SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY}
|
||||
SUPABASE_PUBLISHABLE_KEY: ${SUPABASE_PUBLISHABLE_KEY:-}
|
||||
SUPABASE_SECRET_KEY: ${SUPABASE_SECRET_KEY:-}
|
||||
ANON_KEY_ASYMMETRIC: ${ANON_KEY_ASYMMETRIC:-}
|
||||
SERVICE_ROLE_KEY_ASYMMETRIC: ${SERVICE_ROLE_KEY_ASYMMETRIC:-}
|
||||
DASHBOARD_USERNAME: ${DASHBOARD_USERNAME}
|
||||
DASHBOARD_PASSWORD: ${DASHBOARD_PASSWORD}
|
||||
entrypoint: ["/bin/sh", "/home/kong/kong-entrypoint.sh"]
|
||||
entrypoint: ["/bin/sh", "/docker-entrypoint.sh"]
|
||||
|
||||
auth:
|
||||
container_name: supabase-auth
|
||||
@@ -447,7 +439,7 @@ services:
|
||||
- ./volumes/functions:/home/deno/functions:z
|
||||
- deno-cache:/root/.cache/deno
|
||||
depends_on:
|
||||
kong:
|
||||
api-gw:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "timeout 1 bash -c '</dev/tcp/127.0.0.1/9000'"]
|
||||
@@ -461,7 +453,7 @@ services:
|
||||
# For Podman, use: SUPABASE_JWKS: ${JWT_JWKS}
|
||||
#SUPABASE_JWKS: ${JWT_JWKS:-{"keys":[]}}
|
||||
|
||||
SUPABASE_URL: http://kong:8000
|
||||
SUPABASE_URL: http://api-gw:8000
|
||||
SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL}
|
||||
# Legacy API keys (HS256-signed JWTs)
|
||||
SUPABASE_ANON_KEY: ${ANON_KEY}
|
||||
|
||||
+2
-2
@@ -450,6 +450,6 @@ echo " sh run.sh config"
|
||||
echo " sh run.sh secrets"
|
||||
echo " sh run.sh start"
|
||||
echo ""
|
||||
echo "To enable docker-compose overrides (envoy, caddy, nginx, rustfs, s3, logs):"
|
||||
echo " sh run.sh config add envoy"
|
||||
echo "To enable docker-compose overrides (caddy, nginx, logs, rustfs, s3, kong):"
|
||||
echo " sh run.sh config add caddy"
|
||||
echo ""
|
||||
@@ -109,7 +109,7 @@ check "Legacy ANON_KEY" "200" \
|
||||
"$(http_status "$BASE_URL/storage/v1/bucket" -H "apikey: $ANON_KEY" -H "Authorization: Bearer $ANON_KEY")"
|
||||
|
||||
if [ -n "$SUPABASE_PUBLISHABLE_KEY" ]; then
|
||||
# With opaque key, Kong translates to asymmetric JWT in Authorization
|
||||
# With opaque key, the API gateway translates to asymmetric JWT in Authorization
|
||||
check "New PUBLISHABLE_KEY" "200" \
|
||||
"$(http_status "$BASE_URL/storage/v1/bucket" -H "apikey: $SUPABASE_PUBLISHABLE_KEY")"
|
||||
fi
|
||||
|
||||
@@ -117,12 +117,10 @@ check_logs db \
|
||||
check_logs auth \
|
||||
'db worker started'
|
||||
|
||||
check_logs_if_running kong \
|
||||
'init.lua.*declarative config loaded'
|
||||
|
||||
check_logs_if_running api-gw \
|
||||
'Envoy configuration generated successfully' \
|
||||
'Starting Envoy...'
|
||||
# API gateway: Envoy by default, or Kong when the kong override is enabled.
|
||||
# The service is named api-gw in both cases, so accept either startup marker.
|
||||
check_logs api-gw \
|
||||
'init\.lua.*declarative config loaded|Envoy configuration generated successfully'
|
||||
|
||||
check_logs rest \
|
||||
'Schema cache loaded in.*milliseconds'
|
||||
|
||||
@@ -306,7 +306,7 @@ if [ -n "$anon_key" ]; then
|
||||
check "PostgREST connectivity" "200" "$rest_status"
|
||||
fi
|
||||
|
||||
# Auth health (needs apikey header through Kong)
|
||||
# Auth health (needs apikey header through the API gateway)
|
||||
if [ -n "$anon_key" ]; then
|
||||
auth_status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||
-H "apikey: $anon_key" \
|
||||
|
||||
@@ -247,11 +247,7 @@ rm -f "$range_download"
|
||||
# ---------------------------------------------
|
||||
# 11. Presigned URLs
|
||||
# ---------------------------------------------
|
||||
# Storage supports S3 presigned URLs (query-parameter auth), but Kong's
|
||||
# request-transformer adds an empty Authorization header when the Lua
|
||||
# expression evaluates to nil. Storage sees typeof "" === "string" and
|
||||
# enters parseAuthorizationHeader instead of parseQuerySignature.
|
||||
# This test will pass once the Kong config is fixed.
|
||||
# Storage supports S3 presigned URLs (query-parameter auth).
|
||||
|
||||
echo ""
|
||||
echo "--- Presigned URLs ---"
|
||||
|
||||
@@ -308,7 +308,7 @@ if [ "$create_bucket_status" = "200" ]; then
|
||||
|
||||
if [ -n "$signed_path" ]; then
|
||||
check "Create signed URL" "true" "true"
|
||||
# Fetch signed URL without any auth headers (goes through Kong)
|
||||
# Fetch signed URL without any auth headers (goes through the API gateway)
|
||||
signed_content=$(curl -s "$BASE_URL/storage/v1$signed_path")
|
||||
check "Fetch signed URL (no auth)" "signed url test content" "$signed_content"
|
||||
else
|
||||
|
||||
Executable → Regular
File mode changed.
Executable → Regular
File mode changed.
@@ -26,5 +26,14 @@
|
||||
"API_EXTERNAL_URL now includes the /auth/v1 path prefix, and SAML SSO endpoints moved to /auth/v1/sso/saml/*. Update custom OAuth provider callback URLs and any SAML configuration accordingly.",
|
||||
"Anon (publishable) key access to the OpenAPI spec at /rest/v1/ has been removed. Use the service role or secret API key if you relied on it; normal data access via /rest/v1/<table> is unaffected."
|
||||
]
|
||||
},
|
||||
"0.8.0": {
|
||||
"breaking": true,
|
||||
"gate": null,
|
||||
"migration_guide_url": "https://github.com/orgs/supabase/discussions/48048",
|
||||
"requires": [
|
||||
"Envoy is now the default API gateway, replacing Kong. The gateway service is renamed from 'kong' to 'api-gw' (container 'supabase-envoy'); the 'kong' network alias still resolves, so internal service references keep working.",
|
||||
"If you customized volumes/api/kong.yml or the gateway service, enable the Kong override with 'sh run.sh config add kong' to keep running Kong; otherwise the merge switches you to Envoy."
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
@supabase_api path /auth/v1/* /rest/v1/* /graphql/v1 /realtime/v1/* /storage/v1/* /functions/v1/* /mcp /sso/*
|
||||
|
||||
handle @supabase_api {
|
||||
reverse_proxy kong:8000
|
||||
reverse_proxy api-gw:8000
|
||||
}
|
||||
|
||||
handle {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
upstream kong_upstream {
|
||||
server kong:8000;
|
||||
upstream api_gw_upstream {
|
||||
server api-gw:8000;
|
||||
keepalive 2;
|
||||
}
|
||||
|
||||
@@ -43,19 +43,19 @@ server {
|
||||
}
|
||||
|
||||
location /auth {
|
||||
proxy_pass http://kong_upstream;
|
||||
proxy_pass http://api_gw_upstream;
|
||||
}
|
||||
|
||||
location /rest {
|
||||
proxy_pass http://kong_upstream;
|
||||
proxy_pass http://api_gw_upstream;
|
||||
}
|
||||
|
||||
location /graphql {
|
||||
proxy_pass http://kong_upstream;
|
||||
proxy_pass http://api_gw_upstream;
|
||||
}
|
||||
|
||||
location /realtime/v1/ {
|
||||
proxy_pass http://kong_upstream;
|
||||
proxy_pass http://api_gw_upstream;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
@@ -71,7 +71,7 @@ server {
|
||||
}
|
||||
|
||||
location /storage/v1/ {
|
||||
proxy_pass http://kong_upstream;
|
||||
proxy_pass http://api_gw_upstream;
|
||||
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
@@ -82,14 +82,14 @@ server {
|
||||
}
|
||||
|
||||
location /functions {
|
||||
proxy_pass http://kong_upstream;
|
||||
proxy_pass http://api_gw_upstream;
|
||||
}
|
||||
|
||||
location /mcp {
|
||||
proxy_pass http://kong_upstream;
|
||||
proxy_pass http://api_gw_upstream;
|
||||
}
|
||||
|
||||
location /sso {
|
||||
proxy_pass http://kong_upstream;
|
||||
proxy_pass http://api_gw_upstream;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user