Skip to main content

App-to-App Private Networking and Per-Deployment Deploy Logs

· 6 min read
FoundryDB Team
Engineering @ FoundryDB

App Hosting started by putting your app next to your data: a container on a dedicated VM, served over HTTPS, attached to your managed databases over a private SDN with credentials injected as env vars. The launch post hinted at where this goes next, "more than one app talking to a shared set of databases," and that is exactly what shipped.

Two features this round. First, an app can now attach to another app, not just a database, so a frontend can call an internal API entirely over the private network with no public exposure and no credentials to manage. Second, every deployment revision now records a deploy log: the ordered steps the platform ran to roll that revision out, with each step's status and duration. Here is each one.

App-to-app: attach to a service, not a connection string

You already attach databases to an app with attached_service_ids. An attachment target can now be another app. The machinery is the same as for a database attachment: the platform peers the two apps' private SDN networks and opens the target's container port to the source app's subnet, but what gets injected is different. For an app target you get three variables, prefixed by the target app's name:

MDB_PAYMENTS_HOST     # the target app's PRIVATE SDN address
MDB_PAYMENTS_PORT # the target app's container port
MDB_PAYMENTS_URL # http://<host>:<port>, ready to call

No username, no password, no DATABASE_URL. An app target has no credentials to inject, because the trust boundary is the SDN itself. Your frontend reads MDB_PAYMENTS_URL from its environment and calls it directly.

# Inside the source app's container
curl "$MDB_PAYMENTS_URL/charges" \
-H 'Content-Type: application/json' \
-d '{ "amount": 4200, "currency": "eur" }'

That request is plain HTTP over the private SDN. It reaches the target app on its container port, never traverses the public internet, and never touches the target's public HTTPS ingress. The two apps talk on the cloud provider's backbone, the same private-network primitive the platform already uses for replication, pipelines, and app-to-database traffic.

Attaching at creation, or to a running app

You attach an app exactly the way you attach a database: by putting its service id in attached_service_ids at creation:

curl -u "$USER:$PASS" -X POST https://api.foundrydb.com/app-services \
-H 'Content-Type: application/json' \
-d '{
"name": "storefront",
"plan_name": "tier-2",
"zone": "se-sto1",
"app_config": {
"image_ref": "ghcr.io/acme/storefront:2.1.0",
"container_port": 8080
},
"attached_service_ids": ["PAYMENTS_APP_ID", "PG_SERVICE_ID"]
}'

Or attach to an app already running, which triggers the same re-wiring and a zero-downtime blue/green redeploy so the new MDB_* variables land in the environment:

curl -u "$USER:$PASS" -X POST \
https://api.foundrydb.com/app-services/APP_ID/attachments \
-H 'Content-Type: application/json' \
-d '{ "attached_service_id": "PAYMENTS_APP_ID" }'

This is what a service mesh of small apps looks like on FoundryDB: a storefront attached to a payments API, attached in turn to its own PostgreSQL, each call to the next service staying inside the private network.

The rules worth knowing

A few constraints fall out of how this works, and they are loud rather than silent:

  • The target needs a dedicated container port. Ports 80 and 443 are reserved for the public HTTPS ingress, so the target app must listen on a dedicated port like 8080. An attach to an app on 80 or 443 is rejected.
  • The target must already expose its internal listener. Apps deployed with app-to-app support open it automatically; an older app may need a one-time redeploy to open it.
  • Same owner, same region, must be Running. The same conditions as a database attachment.
  • An app cannot attach to itself, and the five-attachment cap counts databases and apps together.

If a database that an app depends on fails over, the platform detects the new primary and redeploys the app to re-inject the address. App targets resolve to a stable SDN address, so a routine redeploy of the target does not move it out from under you.

Deploy logs: what the deploy actually did

App Hosting already gives you runtime container logs, the serving container's own stdout and stderr, over GET /app-services/{id}/logs. Those tell you what your app is saying. They do not tell you what the deploy did: how long the health probe took, or which phase of the blue/green flip failed when a deploy did not go through.

Every deployment revision now carries a deploy log: the ordered steps the platform ran to roll that revision out, each with a status and a duration. They are returned inline on each revision in the deployments response, under deploy_logs:

curl -u "$USER:$PASS" https://api.foundrydb.com/app-services/APP_ID/deployments
{
"id": "...",
"image_ref": "ghcr.io/acme/storefront:2.1.0",
"reason": "configuration update",
"deploy_logs": [
{ "step": "start-container-green", "status": "ok", "duration_ms": 1840, "started_at": "..." },
{ "step": "health-probe-green", "status": "ok", "duration_ms": 920, "started_at": "..." },
{ "step": "point-ingress-green", "status": "ok", "duration_ms": 60, "started_at": "..." },
{ "step": "stop-previous-blue", "status": "ok", "duration_ms": 410, "started_at": "..." }
]
}

The four steps map directly onto the blue/green model the launch described: start the new container color, health-probe it, cut the ingress over, retire the old color. Each step's status is ok, failed, or info. When a phase fails, the step carries the error in message and, where available, the container's journal tail in detail, so a deploy that died at health-probe-green tells you the probe never went green and shows you why, rather than leaving you to reconstruct it from CI output.

This composes with the health-gated deploys and rollback that shipped earlier. A deploy that fails its health check fails at a visible step in the deploy log, with the previous revision still serving and one request away. In the dashboard, expand a revision in the Deployments tab to read its deploy log inline. Revisions created before this field existed simply have an empty deploy_logs.

Putting it together

These two features pull in the same direction: less guessing about what the platform is doing on your behalf. App-to-app attachment lets a mesh of small services talk privately without you wiring a single network rule or copying a single URL, and the deploy log on every revision tells you exactly what each rollout did, step by step, when it mattered.

Read the App Hosting guide for the full reference on east-west attachments and deploy logs, or drive the whole lifecycle from the REST API, the SDKs, or your AI assistant through the MCP server. Your apps were already next to your data. Now they are next to each other.