> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ankra.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect to a Managed Service

> How an application on the service's cluster receives its connection as environment variables on its next deploy, the variable names for each service, and how to connect an application on another cluster by hand.

> [!WARNING]
>
> **Closed beta.** Connecting to a service is part of [Managed services](https://docs.ankra.ai/concepts/managed-services.md), which is in closed beta and enabled per organisation on request. [Contact support](https://docs.ankra.ai/platform/support.md) to have it turned on for your organisation.

A managed service answers only inside its own cluster, at fixed addresses in its namespace. It keeps the credentials it generated in a Secret in that namespace, and a workload can read a Secret only in its own namespace.

Ankra connects your application for you when it runs on the same cluster as the service: the application's next deploy copies the connection into the application's namespace, and its containers read it as environment variables such as `DATABASE_URL`. The copy happens inside the cluster, and Ankra never sees the values. An application on another cluster is not connected for you: give it the values yourself, as in [Connect from another cluster](#connect-from-another-cluster).

***

## How your application receives the connection

Every application environment a service was set up for, on the service's cluster, receives the service's connection on the application's next deploy:

1. The deploy adds a Secret named `<service>-connection` to the application's namespace, with `<service>` standing for the service name. Ankra copies its values inside the cluster from the service's own credentials Secret.
2. Every container and init container of the application's Deployments, StatefulSets and DaemonSets reads that Secret through `envFrom`, so each of its keys becomes an environment variable. The names are listed under [Variable names](#variable-names).

Nothing reaches the application before it deploys again. Set the service up, wait for it to finish deploying, then deploy the application to that environment. A deploy that runs before the service has generated its credentials adds no Secret, and the application still starts, because the reference to the Secret is optional. Deploy again once the service is running.

The service's page shows each environment's state under **Use it in \<application>**:

| In the portal | `connection_delivery.state` | Meaning |
| - | - | - |
| **Connected** | `delivered` | The `<service>-connection` Secret is in the application's namespace. |
| **Connects on its next deploy** | `pending` | Not delivered yet: the service is still deploying, or the application has not deployed since the service was set up. **Deploy \<environment>** opens the application to deploy it. |
| **Connect by hand** | `unsupported` | Ankra does not connect this environment, usually because it runs on another cluster. The reason is shown. See [Connect from another cluster](#connect-from-another-cluster). |
| **Not known** | `unknown` | Ankra could not read the delivery. It does not mean the connection is missing. |

The same state is in the API, on each of `consumers` in [Get a managed service instance](https://docs.ankra.ai/api-reference/services/get-a-managed-service-instance.md), as `connection_delivery` with the `secret_name`, the `variables` the application receives and a `reason`.

A workload annotated `ankra.io/deploy-overrides: "none"` keeps the manifest you committed, so it gets no `envFrom` reference. The `<service>-connection` Secret is still written to its namespace: reference it from the workload yourself. Jobs and CronJobs are not given the reference either.

When a service is [deleted](https://docs.ankra.ai/guides/delete-a-managed-service.md), the application's next deploy removes its `<service>-connection` Secret and the reference to it.

> [!WARNING]
>
> **Do not also set a delivered variable as an environment secret.** If the application already has, say, `DATABASE_URL` as an [environment secret](https://docs.ankra.ai/reference/cli/application.md#ankra-application-env-secrets), the container is given that variable from two places and Kubernetes keeps only one of the values, which may be the old copy. Once the environment reads **Connected**, delete the copy with `ankra application env-secrets delete <application-id> DATABASE_URL` and apply the secrets again with `ankra application env-secrets apply <application-id>`.

## Variable names

Each service delivers the variables its kind of client conventionally reads:

| Service | Variables | Values |
| - | - | - |
| PostgreSQL | `DATABASE_URL`, `JDBC_DATABASE_URL`, `PGHOST`, `PGPORT`, `PGDATABASE`, `PGUSER`, `PGPASSWORD` | `DATABASE_URL` and `JDBC_DATABASE_URL` carry the user and password, and name the read-write address. `PGHOST` is `postgresql-rw.<service>.svc.cluster.local`. |
| Valkey | `REDIS_URL`, `REDIS_HOST`, `REDIS_PORT`, `REDIS_USERNAME`, `REDIS_PASSWORD` | `REDIS_URL` is a complete `redis://` URL for the primary. |
| NATS | `NATS_URL`, `NATS_USER`, `NATS_PASSWORD` | `NATS_URL` carries no credentials. |
| OpenSearch | `OPENSEARCH_URL`, `OPENSEARCH_USERNAME`, `OPENSEARCH_PASSWORD`, `OPENSEARCH_CA_CERT` | `OPENSEARCH_CA_CERT` is the PEM text of the service's CA certificate, not a file path. |
| SeaweedFS (S3) | `S3_ENDPOINT`, `AWS_ENDPOINT_URL_S3`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, `S3_FORCE_PATH_STYLE` | Both endpoint variables are `http://seaweedfs-all-in-one.<service>.svc.cluster.local:8333`. The key pair is the admin pair. `AWS_REGION` is `us-east-1` and `S3_FORCE_PATH_STYLE` is `true`. |
| VictoriaLogs | `VICTORIA_LOGS_URL`, `VICTORIA_LOGS_USERNAME`, `VICTORIA_LOGS_PASSWORD` | |
| VictoriaMetrics | `VICTORIA_METRICS_URL`, `VICTORIA_METRICS_USERNAME`, `VICTORIA_METRICS_PASSWORD` | |

Every service also delivers each variable under its own prefix: the service name in capitals, with hyphens as underscores. A PostgreSQL service named `orders-db` delivers `ORDERS_DB_DATABASE_URL`, `ORDERS_DB_PGHOST` and so on.

The names without a prefix go to one service of each kind per application environment: the one connected first, recorded the first time a deploy gives it those names. An application with one database reads `DATABASE_URL`. When a second PostgreSQL service is added for the same environment, `DATABASE_URL` keeps pointing at the first, and the second is read through its prefixed names only. Valkey and any other Redis-compatible service count as one kind, as they share `REDIS_URL`.

The names without a prefix never move to another service by themselves. If the service that provides `DATABASE_URL` is retired while another PostgreSQL service still serves the environment, the application's next deploy is refused and nothing is deployed: giving `DATABASE_URL` to the other database would make the application read and write a different database without anyone choosing that. The service's page shows the same warning on the application. Choose which service provides the names, or choose none so the application reads only the prefixed names, then deploy again:

* Ankra AI or MCP: `set_service_connection_default` with the application, cluster, namespace, the kind (`postgres`, `redis`, `nats`, `opensearch`, `s3`, `victorialogs` or `victoriametrics`) and the service's instance id, or `none: true`.
* API: `PUT /api/v1/org/service-admission/connection-defaults` with `application_id`, `cluster_id`, `namespace`, `protocol` and `instance_id` (`null` for none).

If no other service of that kind serves the environment, the deploy goes ahead without the names and says so on its result. A service of that kind connected later then provides them.

A service set up again under the name of a retired one is a new service, not the old one: it does not hold the retired service's data. If the retired one provided `DATABASE_URL`, the deploy is refused in the same way and says so; choose the new service explicitly once you have moved the data. Removing the application's deployment from the namespace forgets which service Ankra recorded as providing the names, so a new deployment there starts fresh; a service you chose yourself stays chosen.

***

## Find the connection details

Open the service in **My services**. Its **Connect** section shows:

* the namespace, which is the service name;
* each address, with a **Copy** button;
* the name of the Secret that holds the credentials;
* for each key in that Secret, a command that prints its value, with a **Copy** button.

The portal never shows the values themselves. The details are derived from the service's package, not observed on the cluster, so they are shown as soon as the service is set up: the addresses answer once the service is running. The section is not shown for a deleted service or while a service is being deleted.

The same details are on the service in the API, as `connection` on [Get a managed service instance](https://docs.ankra.ai/api-reference/services/get-a-managed-service-instance.md): the `namespace`, the named `endpoints` and the `secret` with its `name` and `keys`. They follow a fixed pattern for each service, below, with `<service>` standing for the service name.

## Addresses and Secrets

| Service | Address | Secret | Keys |
| - | - | - | - |
| PostgreSQL | Read-write: `postgresql-rw.<service>.svc.cluster.local:5432` Read-only: `postgresql-ro.<service>.svc.cluster.local:5432` | `postgresql-app` | `fqdn-uri`, `fqdn-jdbc-uri`, `username`, `password`, `dbname`, `port` |
| Valkey | Primary: `valkey.<service>.svc.cluster.local:6379` Read replicas: `valkey-read.<service>.svc.cluster.local:6379` | `valkey-credentials` | `url`, `username`, `password`, `host`, `port` |
| NATS | `nats://nats.<service>.svc.cluster.local:4222` | `nats-credentials` | `url`, `username`, `password` |
| OpenSearch | `https://opensearch.<service>.svc.cluster.local:9200` | `opensearch-credentials` | `url`, `username`, `password`, `ca.crt` |
| SeaweedFS (S3) | `http://seaweedfs-all-in-one.<service>.svc.cluster.local:8333` | `seaweedfs-s3-secret` | `admin_access_key_id`, `admin_secret_access_key`, `read_access_key_id`, `read_secret_access_key` |
| VictoriaLogs | `http://victoria-logs.<service>.svc.cluster.local:9428` | `victoria-logs-credentials` | `url`, `username`, `password` |
| VictoriaMetrics | `http://victoria-metrics.<service>.svc.cluster.local:8428` | `victoria-metrics-credentials` | `url`, `username`, `password` |

The addresses resolve from any namespace on the service's cluster, and not from outside it. On a cluster with a custom DNS domain, use the shorter `<name>.<service>.svc` form, for example `valkey.<service>.svc:6379`. The delivered variables use the full `svc.cluster.local` form, so on such a cluster their addresses do not resolve and automatic delivery does not work yet: give the application the short-form values yourself, as in [Connect from another cluster](#connect-from-another-cluster), under variable names the delivery does not use.

## Read a value

You need a value only to connect something Ankra does not connect for you. Select the service's cluster, then read the Secret by name with `--reveal`, using the Secret and namespace from the table:

```bash theme={null}
ankra cluster select <cluster>
ankra cluster get secrets <secret> -n <service> --reveal -o json
```

Without `--reveal`, every value comes back as a `sha256:` digest. `--reveal` needs the `kubernetes.secrets_reveal` permission on the cluster, which the operator, admin and owner roles carry, and every reveal is recorded in the organisation's audit log. Values under `data` stay base64-encoded.

For a PostgreSQL service named `orders-db`, this prints its password:

```bash theme={null}
ankra cluster get secrets postgresql-app -n orders-db --reveal -o json | jq -r '.data["password"]' | base64 -d
```

A key with a dot in its name, such as OpenSearch's `ca.crt`, works the same way:

```bash theme={null}
ankra cluster get secrets opensearch-credentials -n search --reveal -o json | jq -r '.data["ca.crt"]' | base64 -d > opensearch-ca.crt
```

## Connect from another cluster

An application on another cluster than the service is not connected for you, and the service's addresses do not resolve there. Reaching the service from another cluster needs a network path between the clusters that you provide, and the address that path exposes.

Give the application the values as [environment secrets](https://docs.ankra.ai/reference/cli/application.md#ankra-application-env-secrets), from the service page's **Use it in \<application>** button, which opens the application's **Settings › Secrets**, or from the CLI. Piping the value keeps it out of your shell history:

```bash theme={null}
ankra cluster get secrets postgresql-app -n orders-db --reveal -o json \
  | jq -r '.data["password"]' | base64 -d \
  | ankra application env-secrets set <application-id> PGPASSWORD
ankra application env-secrets apply <application-id>
```

Storing a value does not reach a running workload: `env-secrets apply` seals the stored values into the application's deployments and rolls them. The URL keys, such as PostgreSQL's `fqdn-uri`, name the in-cluster address, so build the URL your application uses from your own network path's address rather than copying them.

For a workload that is not an Ankra application, keep the Secret in Git and encrypt it with [SOPS](https://docs.ankra.ai/guides/sops.md).

A value you copy this way is yours: [deleting the service](https://docs.ankra.ai/guides/delete-a-managed-service.md) does not remove it from the application. Delete it yourself once the application no longer uses the service.

## Notes per service

* **PostgreSQL.** `DATABASE_URL` and `JDBC_DATABASE_URL` carry the operator's `fqdn-uri` and `fqdn-jdbc-uri`. The operator also writes `uri`, `jdbc-uri` and `host` keys, but they name the short host `postgresql-rw`, which resolves only inside the service's own namespace. You connect as `app`, the owner of the database `app`, and superuser access is off. The read-only address reaches replicas only, so it answers only when `instances` is above 1, and no variable names it. The image includes the pgvector and pgaudit extensions.
* **Valkey.** Authenticate as the `default` user with the password; `REDIS_URL` is a complete `redis://` URL. Write to the primary address. The read-replicas address serves reads from every pod.
* **NATS.** Connect as `app`. `NATS_URL` carries no credentials, so pass `NATS_USER` and `NATS_PASSWORD` separately. JetStream is on; memory-backed streams are disabled.
* **OpenSearch.** Use HTTPS with basic auth as `admin`, the only user. The certificate is signed by a private CA generated in the cluster: have your client trust `OPENSEARCH_CA_CERT`, which holds that CA's certificate.
* **SeaweedFS.** Use the S3 API over plain HTTP inside the cluster, with path-style addressing. The admin key pair, the one delivered, can read, write, list and administer buckets. The read-only key pair can only read, and is in the service's Secret only.
* **VictoriaLogs.** Send logs over HTTP with basic auth, as JSON lines, Loki push, OpenTelemetry or Elasticsearch bulk, and query them with LogsQL. No log collector is installed, so ship logs with your own agent.
* **VictoriaMetrics.** Push metrics with basic auth using Prometheus `remote_write`, OpenTelemetry, InfluxDB or another supported protocol, and query with PromQL or MetricsQL on the Prometheus HTTP API. It scrapes nothing itself, so push from vmagent, a Prometheus agent or an OpenTelemetry Collector.

## Check that the service answers

A finished deployment is not yet a ready service. The service's **Service health** section says whether its workload runs as planned, which does not prove a client can connect. Ankra checks that a client can connect for every service it publishes, about every five minutes, and shows the result as the service's **Readiness**: see [Health and readiness](https://docs.ankra.ai/concepts/managed-services.md#health-and-readiness). Until readiness reads **Ready**, or while it reads **Not verified** because the cluster's agent is too old to run the check, check the service yourself before you rely on it. Its pods run in its namespace:

```bash theme={null}
ankra cluster get pods -n <service>
```

A PostgreSQL service can report its deployment finished 30 to 100 seconds before the database accepts connections. Check the database itself until its `Ready` condition is `True`:

```bash theme={null}
ankra cluster describe Cluster postgresql -n <service> --group postgresql.cnpg.io
```

About every five minutes, a service's namespace also holds a short-lived pod: labelled `app.kubernetes.io/name=ankra-postgres-connection-test` for PostgreSQL, and `app.kubernetes.io/name=ankra-service-canary` for every other service. That is Ankra's readiness check, which connects the way an application would; it is deleted when it finishes.

## Next steps

> **[Managed service settings](https://docs.ankra.ai/reference/managed-services.md)**
>
> What each setting changes, with its range and default.

> **[Managed services](https://docs.ankra.ai/concepts/managed-services.md)**
>
> How the services run, their health and readiness, and their current limits.

> **[Delete a managed service](https://docs.ankra.ai/guides/delete-a-managed-service.md)**
>
> What deleting removes and keeps, and how to confirm it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.