Skip to main content

Companion Apps

Companion apps let you add a ready-made application to one of your databases with a single click. Pick a companion app from the catalog (for example Metabase), and the platform provisions it, wires it privately to your database, serves it over HTTPS at its own subdomain, and meters it like any other service. There is no image to build, no connection string to copy, and no firewall to open by hand.

The catalog

Five companion apps ship today. Each one is pinned to a known-good image and wired to your database automatically when you attach it.

KindAppCategoryParent engineWhat it does
metabaseMetabaseBusiness intelligencePostgreSQL, MySQLExplore, query, and dashboard your database with no setup.
directusDirectusHeadless CMSPostgreSQL, MySQLInstant REST and GraphQL APIs, an admin app, and flows over your database.
hasuraHasura GraphQLGraphQL APIPostgreSQLInstant realtime GraphQL APIs over your database, with a console and permissions.
nocodbNocoDBNo-codePostgreSQL, MySQLAn Airtable-style smart spreadsheet UI over your database.
open-webuiOpen WebUIAI chatPostgreSQLA self-hosted AI chat interface that persists to your database.
adminerAdminerAdminPostgreSQL, MySQL, SQL ServerA lightweight web database admin for SQL engines.
mongo-expressMongo ExpressAdminMongoDBA web admin UI to browse and edit your MongoDB collections.
redis-commanderRedis CommanderAdminValkeyA web admin UI to inspect and edit your Valkey keys.
kafka-uiKafka UIAdminKafkaBrowse topics, messages, consumer groups, and broker health.
opensearch-dashboardsOpenSearch DashboardsAnalyticsOpenSearchVisualize, search, and explore your OpenSearch data.

GET /attachment-catalog returns the full catalog, including each app's kind (the value you pass to the attach endpoint), its default compute plan, and the parent database engines it supports.

Attach an app

You start from a database service and ask for a companion app by kind:

curl -u "$USER:$PASS" -X POST \
https://api.foundrydb.com/managed-services/$DATABASE_ID/attachments \
-H "Content-Type: application/json" \
-d '{ "kind": "metabase" }'

From that one field the platform:

  1. Validates the parent. The parent database engine must be one the app supports. An unsupported engine, an unknown kind, or a non-database parent is rejected with 400.
  2. Provisions a dedicated app. The container image, port, readiness probe, and storage size all come from the catalog descriptor, not from you. The app runs on its own resources, supervised as a real system service.
  3. Connects it privately. The app's network is peered to your database's private network, and the database firewall is opened to exactly the app's subnet. The connection host is always the database primary's private address, so app-to-database traffic never leaves the platform network.
  4. Serves it with HTTPS. The app gets its own subdomain at https://<kind>-<id>.foundrydb.com, with a certificate issued automatically.
  5. Runs first-boot wiring. Apps that stand up an admin account on first boot get a strong, one-time admin credential you can reveal through the API.

Provisioning is asynchronous. The POST returns 201 Created with the app in Pending. Poll GET /app-services/{id} until it is Running; the response then carries the public url.

Attach options

The attach body has one required field and two optional ones:

FieldRequiredDescription
kindYesCatalog companion-app kind to provision (from GET /attachment-catalog).
plan_nameNoCompute-only tier for the companion app. Defaults to the descriptor's default plan (typically tier-2).
subdomainNoSubdomain override. A single DNS label of letters, digits, and hyphens (max 40 characters). Defaults to a generated <kind>-<short> name.
# Give the companion app a friendly subdomain and a larger plan
curl -u "$USER:$PASS" -X POST \
https://api.foundrydb.com/managed-services/$DATABASE_ID/attachments \
-H "Content-Type: application/json" \
-d '{ "kind": "metabase", "subdomain": "analytics", "plan_name": "tier-4" }'

See what is attached

GET /managed-services/{id}/attachments lists the companion apps provisioned against a database service, each with its app service id, kind, status, wiring status, and public URL.

curl -u "$USER:$PASS" \
https://api.foundrydb.com/managed-services/$DATABASE_ID/attachments
{
"attachments": [
{
"attachment_id": "11111111-2222-3333-4444-555555555555",
"app_service_id": "ab12cd34-5678-90ab-cdef-1234567890ab",
"kind": "metabase",
"name": "analytics",
"status": "Running",
"wiring_status": "active",
"url": "https://analytics-xyz-db.foundrydb.com"
}
]
}

Log in and see your own tables

Here is the experience that companion apps were built around. Attach Metabase to your PostgreSQL service, wait for it to reach Running, open its subdomain, and log in. There is no "add a data source" step: your database is already registered, and you are looking at your own tables on the first screen.

When Metabase comes up healthy, the platform fetches its one-time setup token, creates your admin account, and registers your parent database as a Metabase data source in the same pass. By the time you see a login prompt, the connection has already been made on your behalf with a credential scoped to your data.

The other apps follow the same spirit. Directus bootstraps its admin account and system collections directly inside your database on first start. Hasura comes up with its console live and your tables ready to expose over GraphQL. NocoDB turns your tables into editable grids. Open WebUI persists chats and users straight into your database.

How each app uses your database

How a companion app uses your database falls into two patterns:

  • Browses the database (Metabase). The app keeps its own state (dashboards, users, questions) on its own persistent volume and registers your database as a browsable data source. Your database stays clean; the app only reads from it.
  • Uses the database as its backing store (Directus, Hasura, NocoDB, Open WebUI). The app stores its own metadata in your database and exposes or edits your tables. Its system objects live alongside your data in the attached database.

Either way you reach the same result: the app is connected to your data the moment it is Running.

Reveal the login

Companion apps that bootstrap an admin account get a strong, generated login the platform stores encrypted at rest. Reveal it with the companion app's service id (the app_service_id from the attachment list):

curl -u "$USER:$PASS" \
https://api.foundrydb.com/app-services/$APP_SERVICE_ID/attachment-credentials

The credential is surfaced one of two ways, depending on how the app bootstraps:

  • Post-deploy hook (Metabase). After the container is healthy, the platform creates the admin account and registers your database as a data source, then records the admin login. It is returned as admin_email and admin_password:

    {
    "admin_email": "admin@example.com",
    "admin_password": "generated-one-time-password",
    "login_url": "https://analytics-xyz-db.foundrydb.com"
    }
  • Environment bootstrap (Directus, Hasura, and similar). The app reads its admin login from generated environment values on first start. The reveal-flagged values are returned in the generated map, keyed by environment variable name:

    {
    "generated": {
    "ADMIN_EMAIL": "admin@directus-ab12.foundrydb.com",
    "ADMIN_PASSWORD": "generated-one-time-password"
    },
    "login_url": "https://directus-ab12-xyz-db.foundrydb.com"
    }

    For Hasura the map carries HASURA_GRAPHQL_ADMIN_SECRET, the admin secret that guards its console.

Every secret is decrypted on demand and is never logged. While the companion app is still provisioning, and for a plain app service that has no attachment kind, the endpoint returns 404. Poll until the app is Running and try again.

Secure by default

Three things are true of every companion app the moment it comes up, with no toggles to find:

  • Private networking. The path between the app and your database is a peered private network with a firewall scoped to the app's subnet. There is no public database endpoint involved in the connection.
  • Automatic TLS. The app's subdomain serves over HTTPS with a certificate the platform issues and renews. You do not request or rotate anything.
  • EU data residency. Companion apps run in the same European regions as the database they attach to. Your data, and the tool browsing it, stay where your data already lives.

Manage the lifecycle

A companion app is a normal app service, so its full lifecycle is served by the /app-services routes keyed by the returned app service id:

ActionEndpoint
Restart the containerPOST /app-services/{id}/restart
Resize the compute planPOST /app-services/{id}/scale
Request logsPOST /app-services/{id}/logs then GET /app-services/{id}/logs?task_id=…
DeleteDELETE /app-services/{id}

Deleting the companion app tears down its container, ingress, certificate, DNS record, floating IP, the database peering, and the firewall opening. The parent database is left untouched. A database that still has companion apps attached cannot be deleted until they are removed.

Failover re-wiring

The connection delivered to a companion app points at the current database primary's private address. After a database failover moves the primary to a different node, the platform detects the change and automatically redeploys the companion app to re-inject the new primary's address, with no manual action required. Apps that read their connection at every deploy reconnect within about a minute.

Billing

A companion app bills by its compute plan exactly like a database service of the same tier. It has no managed backups, so no backup storage is provisioned or charged. Deleting the companion app stops its charges.