Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions docs/services/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Supporting Services (Beta)

The pgEdge Control Plane lets you run services alongside your
databases. Services are applications that attach to a database, run on
any host in the cluster, and connect via automatically-managed
database credentials.

## What Are Supporting Services?

A supporting service is an application that runs alongside a database.
Each service instance runs on a single host and receives its own set of
database credentials scoped to that instance. The Control Plane supports
the following service types:

- The [pgEdge Postgres MCP Server](mcp.md) connects AI agents and
LLM-powered applications to your database, enabling natural language
queries and AI-powered data access.
- The pgEdge RAG Server *(coming soon)* enables retrieval-augmented
generation workflows using your database as a knowledge store.
- PostgREST *(coming soon)* automatically generates a REST API from
your PostgreSQL schema, making your data accessible over HTTP without
writing backend code.

## Service Instances

When you add a service to a database, the Control Plane creates one
service instance per host listed in the service's `host_ids`. Each
instance runs on a single host and receives its own database
credentials. Services can run on any host in the cluster; they do not
need to be co-located with database instances.

The following table describes the lifecycle states for service
instances:

| State | Description |
|-------|-------------|
| `creating` | The Control Plane is provisioning the service instance. |
| `running` | The service instance is healthy and operational. |
| `failed` | The service instance exited or failed its health check. |
| `deleting` | The Control Plane is removing the service instance. |

## Deployment Topologies

Services are independent of your database node topology, so you can
place service instances on any host in the cluster. The following
deployment patterns are common:

- In a co-located topology, the service runs on the same host as a
database instance, which minimizes network latency between the
service and Postgres.
- In a separate-host topology, the service runs on a dedicated host
with no database instance, which isolates the service workload from
the database.
- In a multiple-instances topology, one service instance runs per host
for redundancy or regional proximity; each instance receives its own
credentials and connects to the database independently.

In the following example, the service runs on the same host as the
database node (`host-1`):

```json
"nodes": [ { "name": "n1", "host_ids": ["host-1"] } ],
"services": [ { ..., "host_ids": ["host-1"] } ]
```

In the following example, the service runs on a dedicated host
(`host-3`) with no database instance:

```json
"nodes": [ { "name": "n1", "host_ids": ["host-1"] },
{ "name": "n2", "host_ids": ["host-2"] } ],
"services": [ { ..., "host_ids": ["host-3"] } ]
```

In the following example, the service runs on each database host,
creating one instance per host for redundancy:

```json
"nodes": [ { "name": "n1", "host_ids": ["host-1"] },
{ "name": "n2", "host_ids": ["host-2"] } ],
"services": [ { ..., "host_ids": ["host-1", "host-2"] } ]
```

## Database Credentials

Each service instance is automatically provisioned with two dedicated
database users. The Control Plane manages these credentials; you do not
need to create or rotate them manually. The credentials are:

- `svc_{service_id}_ro` is a read-only user with read access to the
database; this user is the default for most service types.
- `svc_{service_id}_rw` is a read-write user with read and write access
to the database; this user is provisioned when the service needs
read/write access.

## Next Steps

To add a service to a database, see [Managing Services](managing.md).
Then refer to the page for your specific service type for configuration
details.
167 changes: 167 additions & 0 deletions docs/services/managing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# Managing Services

Services are declared as part of your database spec. You add, update,
and remove services by modifying the `services` array in a
`create_database` or `update_database` request. See the
[Services Overview](index.md) for a conceptual introduction.

## Service Spec Fields

Each service in the `services` array is declared using a service spec.
The following table describes the fields in a service spec:

| Field | Type | Required | Description |
|-------|------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `service_id` | string | Yes | A unique identifier for this service within the database. Used in credential names (`svc_{service_id}_ro` / `svc_{service_id}_rw`). |
| `service_type` | string | Yes | The type of service to run. One of: `mcp`, `rag`, `postgrest`. |
| `version` | string | Yes | The service version in semver format (e.g., `1.0.0`) or the literal `latest`. |
| `host_ids` | array | Yes | The IDs of the hosts to run this service on. One instance is created per host. |
| `config` | object | Yes | Service-type-specific configuration. See the page for your service type for valid fields. |
| `port` | integer | No | Host port to publish the service on. Set to `0` to let Docker assign a random port. When omitted, the service is not reachable from outside the Docker network. |
| `cpus` | string | No | CPU limit for the service container. Accepts a decimal (e.g., `"0.5"`) or millicpu suffix (e.g., `"500m"`). Defaults to container defaults if unspecified. |
| `memory` | string | No | Memory limit for the service container in SI or IEC notation (e.g., `"512M"`, `"1GiB"`). Defaults to container defaults if unspecified. |
| `database_connection` | object | No | Optional routing configuration for how the service connects to the database. See [Database Connection Routing](#database-connection-routing). |

## Adding a Service

Include a `services` array in your database spec when creating or
updating a database. In the following example, a `curl` command creates
a single-node database with one MCP service instance:

=== "curl"

```sh
curl -X POST http://host-1:3000/v1/databases \
-H 'Content-Type: application/json' \
--data '{
"id": "example",
"spec": {
"database_name": "example",
"nodes": [
{ "name": "n1", "host_ids": ["host-1"] }
],
"services": [
{
"service_id": "mcp-server",
"service_type": "mcp",
"version": "latest",
"host_ids": ["host-1"],
"port": 8080,
"config": {
"llm_enabled": true,
"llm_provider": "anthropic",
"llm_model": "claude-sonnet-4-5",
"anthropic_api_key": "sk-ant-..."
}
}
]
}
}'
```

The response includes a task ID you can use to track progress. See
[Tasks & Logs](../using/tasks-logs.md) for details.

## Updating a Service

To update a service's configuration, submit a `POST` request to
`/v1/databases/{database_id}` with the modified service spec in the
`services` array.

!!! important

The `services` array in an update request is declarative; it
replaces the complete list of services for the database. To keep an
existing service running unchanged, include its current spec
alongside any new or modified entries.

In the following example, a `curl` command updates the MCP service to
use a different model:

=== "curl"

```sh
curl -X POST http://host-1:3000/v1/databases/example \
-H 'Content-Type: application/json' \
--data '{
"spec": {
"database_name": "example",
"nodes": [
{ "name": "n1", "host_ids": ["host-1"] }
],
"services": [
{
"service_id": "mcp-server",
"service_type": "mcp",
"version": "latest",
"host_ids": ["host-1"],
"port": 8080,
"config": {
"llm_enabled": true,
"llm_provider": "anthropic",
"llm_model": "claude-opus-4-5",
"anthropic_api_key": "sk-ant-..."
}
}
]
}
}'
```

## Removing a Service

To remove a service, submit an update request that omits the service
from the `services` array. The Control Plane stops and deletes all
service instances for that service and revokes its database credentials.

!!! warning

Removing a service is irreversible. The Control Plane deletes all
service instances, their configuration, and their data directories.
Database credentials for the service are revoked. Any clients
connected to the service lose access immediately.

## Checking Service Status

To check the current state of your service instances, retrieve the
database and inspect the `service_instances` field in the response. In
the following example, a `curl` command retrieves the database:

=== "curl"

```sh
curl http://host-1:3000/v1/databases/example
```

Each service instance in the response includes a `state` field. See the
[Services Overview](index.md#service-instances) for a description of
each state.

## Database Connection Routing

By default, the Control Plane builds a connection string that includes
all database nodes, with the local node listed first. You can override
this behavior using the `database_connection` field in the service spec.
The following table describes the `database_connection` fields:

| Field | Type | Description |
|-------|------|-------------|
| `target_nodes` | array of strings | An ordered list of node names to include in the connection string. Nodes are tried in the order listed. |
| `target_session_attrs` | string | Overrides the libpq `target_session_attrs` parameter. Valid values: `primary`, `prefer-standby`, `standby`, `read-write`, `any`. |

In the following example, the `database_connection` field routes the
service to the `n1` node only:

=== "curl"

```sh
"database_connection": {
"target_nodes": ["n1"],
"target_session_attrs": "primary"
}
```

!!! tip

Use `database_connection` when your service needs to read from a
specific node or enforce write routing to the primary.
Loading