> For the complete documentation index, see [llms.txt](https://docs.kangal.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kangal.dev/gateway/upstreams-targets.md).

# Upstreams and targets

An upstream is a named destination pool. Targets are the concrete HTTP or HTTPS endpoints in that pool. Use an upstream when a service needs more than one backend, health information, or runtime target selection.

The usual traffic chain is:

```
request -> route -> service -> upstream -> target
```

For a single stable backend, a service with a direct URL is simpler. For a pool, create the upstream and targets first, then point a service at the upstream.

## Before you begin

You need:

* A gateway and its ID.
* A tenant ID and an Admin API bearer token.
* At least one backend URL that the Kangal runtime can reach.
* A target base URL that accepts an unauthenticated `GET` health request.

Examples use the versioned Admin API:

```bash
export KANGAL_ADMIN_URL="https://control.example.com"
export KANGAL_TOKEN="replace-with-admin-token"
export KANGAL_TENANT_ID="tenant_123"
export KANGAL_GATEWAY_ID="gateway_123"
```

## Create an upstream

### Console workflow

1. Open a gateway and select **Upstreams**.
2. Select **Create upstream**.
3. Enter a name that identifies the backend pool.
4. Optionally enter a fallback URL.
5. Save the upstream, then open it to add targets.

### Admin API

```bash
curl -sS -X POST \
  "$KANGAL_ADMIN_URL/api/v1/admin/gateways/$KANGAL_GATEWAY_ID/upstreams" \
  -H "Authorization: Bearer $KANGAL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "'"$KANGAL_TENANT_ID"'",
    "name": "checkout-pool",
    "url": "https://checkout-fallback.internal.example"
  }'
```

Creating the same upstream name again in the same tenant and gateway returns the existing upstream. This makes repeated provisioning safe, but it does not update the existing URL. Use the update operation when configuration must change.

## Upstream fields

| Field       | Required      | Semantics                                                                                   |
| ----------- | ------------- | ------------------------------------------------------------------------------------------- |
| `tenant_id` | Yes on create | Tenant ownership and authorization scope.                                                   |
| `name`      | Yes           | Name unique within the tenant and gateway. Services can reference it by name or ID.         |
| `url`       | No            | Full HTTP or HTTPS fallback URL. It can receive traffic when the pool has no usable target. |
| `labels`    | No            | Metadata accepted by the update API for automation and inventory.                           |

The create and update workflows use round-robin target selection.

## Add targets

Get the upstream ID from the create response or list operation, then add one or more targets.

```bash
export KANGAL_UPSTREAM_ID="upstream_123"

curl -sS -X POST \
  "$KANGAL_ADMIN_URL/api/v1/admin/gateways/$KANGAL_GATEWAY_ID/targets" \
  -H "Authorization: Bearer $KANGAL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "upstream_id": "'"$KANGAL_UPSTREAM_ID"'",
    "name": "https://checkout-a.internal.example"
  }'
```

Add a second target in the same way:

```bash
curl -sS -X POST \
  "$KANGAL_ADMIN_URL/api/v1/admin/gateways/$KANGAL_GATEWAY_ID/targets" \
  -H "Authorization: Bearer $KANGAL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "upstream_id": "'"$KANGAL_UPSTREAM_ID"'",
    "target": "https://checkout-b.internal.example"
  }'
```

`name` and `target` are accepted aliases for the target address; at least one is required. The address must be a complete URL beginning with `http://` or `https://`. A host-and-port value such as `10.0.1.5:8000` is not a valid runtime target and produces `502`; submit `http://10.0.1.5:8000` instead.

Creating an address that already exists in the same upstream returns the existing target rather than a duplicate.

## Target state

New targets begin with:

* `status: active`
* `health_status: healthy`
* zero success and failure counts
* no measured latency

The initial `healthy` value is an administrative default, not proof that a probe succeeded. Wait for health-monitor updates and send a real request before using that state as readiness evidence.

## Health checks

In the combined Kangal deployment, the health monitor runs approximately every 30 seconds and sends `GET` to each target's base URL with a five-second timeout. Any `2xx` or `3xx` response is healthy. Repeated failures use backoff before the next probe.

### Health-check requirements

* The probe is an unauthenticated `GET` to the target's base URL. Make that URL return a cheap, side-effect-free `2xx` or `3xx` response.
* A target whose base URL requires authentication is reported unhealthy; place a health-compatible endpoint in front of the application.
* An invalid target address is reported with a `no_url` probe error.
* Passive observations made by an independent data plane remain local to that serving process. Use its status and logs together with control-plane records.

## How selection behaves during failures

The default selection is round robin across available targets. Healthy targets are preferred. If there are not enough healthy candidates, the runtime can widen the candidate set to include `degraded` and `unhealthy` targets. If every active target is unhealthy, an unhealthy target can still be selected.

This behavior favors continuity over strict health isolation. Remove a failing target from the pool when it must receive no traffic; a health flag alone is not a hard traffic block in all failure states.

If the pool has no usable target and the upstream has a valid `url`, the runtime can use that URL as a fallback. If a selected target itself contains an invalid address, the request fails with `502`; the fallback does not repair that target.

## Point a service at the upstream

Create or update a service using `upstream_id` or `upstream_name`:

```bash
curl -sS -X POST \
  "$KANGAL_ADMIN_URL/api/v1/admin/gateways/$KANGAL_GATEWAY_ID/services" \
  -H "Authorization: Bearer $KANGAL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "'"$KANGAL_TENANT_ID"'",
    "name": "checkout-service",
    "upstream_id": "'"$KANGAL_UPSTREAM_ID"'",
    "connect_timeout_ms": 5000,
    "read_timeout_ms": 30000,
    "write_timeout_ms": 30000
  }'
```

Creating an upstream or target does not route traffic by itself. A service must reference the upstream, and a route must reference that service or upstream.

## Update and replacement workflow

An upstream can be updated with `name`, `url`, and `labels`. To replace a target address:

1. Add the replacement target with a complete URL.
2. Wait for a successful probe and test traffic.
3. Delete the old target by ID.

This order preserves pool capacity during the change. For an immediate stop, remove the old target first and accept the temporary reduction in capacity.

## Deletion and dependency safety

Deleting an upstream does not cascade to targets or services and does not block the operation when references exist. Deleting a target does not update any external inventory. Before deleting an upstream:

1. Find services that reference its ID or name.
2. Repoint or delete those services and their routes.
3. Delete its targets.
4. Delete the upstream.

A dangling service or route can produce `502 Bad Gateway` at request time.

## Troubleshooting

### The target was created but traffic returns `502`

Confirm that the stored target starts with `http://` or `https://`, resolves from the data-plane network, and accepts the route's rewritten path. Also confirm the service references the intended upstream ID or name.

### A new target says healthy before it is reachable

That is the initial administrative value. Wait longer than one health interval, inspect the latest probe metadata, and send a request through the gateway.

### An unhealthy target still receives requests

When the healthy set is exhausted, the runtime can select unhealthy active targets. Remove or administratively disable the target when strict isolation is required.

### The health check fails but application requests work

The monitor calls the base URL with unauthenticated `GET`. Adjust the base URL so that request succeeds, or use a fronting endpoint compatible with the current probe behavior.

### Deleting an upstream broke a route

Deletes are not cascading. Recreate or repoint the upstream/service dependency, then confirm a fresh configuration bundle is active on the data plane.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.kangal.dev/gateway/upstreams-targets.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
