# Connect to a database from outside

URL: https://briq.cloud/docs/guides/connect-to-a-database-from-outside

> A briq's database reachable from psql, a BI tool or an app elsewhere, over TLS, with a normal connection string.

Recipes:

- [PostgreSQL](https://briq.cloud/cookbook/postgres.md): A PostgreSQL database with a persistent volume. Optional pgvector.
- [MySQL](https://briq.cloud/cookbook/mysql.md): A MySQL database with a persistent volume.
- [Redis](https://briq.cloud/cookbook/redis.md): A Redis server (Valkey-compatible) for caches and queues.
- [MongoDB](https://briq.cloud/cookbook/mongo.md): A MongoDB server with authentication and a persistent volume.
- [RabbitMQ](https://briq.cloud/cookbook/rabbitmq.md): AMQP message broker with the management UI enabled.
- [Neo4j](https://briq.cloud/cookbook/neo4j.md): A graph database with a persistent volume, for data an agent should query, not paraphrase.
- [Qdrant](https://briq.cloud/cookbook/qdrant.md): A vector database, for a knowledge file the agent can search by meaning, not grep.

Ask your agent to "give me a connection string for the database". It gives the briq a TCP
endpoint: the port, and the protocol it speaks.

```text
briq_expose({ briq: "bq_7f1…", port: 5432, protocol: "postgres" })
→ postgres TCP endpoint for port 5432:
  postgres://postgres:…@db-k3m9x2p7qa.tcp.briq.run:5432/app?sslmode=require
```

In the dashboard: the briq's page, **Endpoints**, **tcp**: pick the port and the protocol, then
**Expose**.

## What happens

1. The TCP endpoint gets a hostname, `<briq>-<10 characters>.tcp.briq.run`, kept for the briq's
   life.
2. briq's edge serves it on the protocol's usual port, over TLS 1.2 or later, and refuses
   plaintext.
3. A connection wakes a sleeping briq, and an open connection keeps its stack awake.

## Each protocol

| `protocol` | Recipes                                      | Edge port | Connection string                           |
| ---------- | -------------------------------------------- | --------- | ------------------------------------------- |
| `postgres` | [postgres](https://briq.cloud/cookbook/postgres)               | 5432      | `postgres://…?sslmode=require`              |
| `mysql`    | [mysql](https://briq.cloud/cookbook/mysql)                     | 3306      | `mysql://…?ssl-mode=REQUIRED`               |
| `redis`    | [redis](https://briq.cloud/cookbook/redis)                     | 6379      | `rediss://default:…@…:6379`                 |
| `mongodb`  | [mongo](https://briq.cloud/cookbook/mongo)                     | 27017     | `mongodb://…?authSource=admin&tls=true`     |
| `amqp`     | [rabbitmq](https://briq.cloud/cookbook/rabbitmq) (5672 inside) | 5671      | `amqps://…:5671`                            |
| `bolt`     | [neo4j](https://briq.cloud/cookbook/neo4j)                     | 7687      | `neo4j+s://…:7687`, user and password apart |
| `grpc`     | [qdrant](https://briq.cloud/cookbook/qdrant) (6334)            | 6334      | `https://…:6334`, with the `api-key` header |

A recipe's database ports are listed in `briq_status` as `databasePorts`. Its endpoint answers with
the full connection string, password included. Another briq's (your own image) answers with the
address: add its user and password.

## Send the hostname

The edge finds the database by the name the client sends in TLS (SNI). Connect by name, never by
IP address. Most clients send it; these need an option:

| Client                     | Option                                                                            |
| -------------------------- | --------------------------------------------------------------------------------- |
| `redis-cli`                | `--tls --sni <host>`                                                              |
| ioredis                    | `tls: { servername: host }`                                                       |
| node-redis                 | `socket: { tls: true, servername: host }`                                         |
| `mysql` CLI (8.1 or later) | `--ssl-mode=VERIFY_IDENTITY --tls-sni-servername=<host>`                          |
| psql, libpq                | 14 or later; `sslmode=verify-full&sslrootcert=system` also checks the certificate |

## The password is the gate

* There is no URL token: whoever has the connection string can connect. Keep it secret.
* Recipes generate passwords of about 140 bits.
* The edge refuses a Postgres, Redis or MongoDB that lets anyone in. Set a password on your own
  image before you expose it. For MySQL, AMQP, Bolt and gRPC, make sure the service asks for one.
* After 10 failed logins in 10 minutes, the edge refuses that address for 10 minutes (Postgres,
  MySQL, Redis).

## When it does not connect

Errors from the edge start with `briq:`.

| The client says                                   | Fix                                                                          |
| ------------------------------------------------- | ---------------------------------------------------------------------------- |
| `TLS is required`                                 | Turn TLS on: `sslmode=require`, `rediss://`, `tls=true`, `ssl-mode=REQUIRED` |
| `no server name (SNI)`                            | Connect by hostname; add the option above                                    |
| `no Postgres endpoint named …` (or Redis, MySQL…) | Check the hostname, and that the port is the protocol's                      |
| `lets anyone in` or `has no password`             | Set a password on the service, then connect again                            |
| `the database is stopped`                         | Start the briq; a sleeping one wakes on its own                              |
| `too many failed logins`                          | Wait 10 minutes; check the password                                          |

A MongoDB that lets anyone in is refused by closing the connection. Some clients (ioredis) show
only that the connection closed: try `redis-cli` or `psql` to read the edge's message.

## Good to know

* **Unlimited on every plan**, like HTTPS URLs.
* **Destroying the briq removes its endpoint.** Exposing again gives the same one.
* **Briqs of the same stack** use the internal address (`DATABASE_URL` and the like), not this one.
