Skip to content

Internal networking

Everything you deploy in one organization lands in the same private network. Your backend can reach your database, your KV store, and your other services directly by hostname — the traffic never leaves the cluster, never crosses the public internet, and never needs a public endpoint at all.

This page shows how to wire that up.

Each organization gets its own isolated namespace. Inside it, every service has a stable internal DNS name:

ResourceInternal hostnamePort
Container appcustom-<project-name>80
PostgreSQL<db-name>-postgresql.org-<orgId>.svc.<region>.pandastack.app5432
MySQL<db-name>-mysql.org-<orgId>.svc.<region>.pandastack.app3306
KV store (Redis)<store-name>-redis.org-<orgId>.svc.<region>.pandastack.app6379

Two things follow from this that matter:

Traffic between them stays inside the network. A backend talking to its database over the internal hostname does plain TCP inside the cluster. There is no TLS handshake to pay for, no public proxy hop, and nothing observable from the outside.

Another organization cannot resolve or reach your services. The namespace is the isolation boundary — your services are addressable from your workloads, not from anyone else’s.

The internal URLs are injected for you. Link a database or KV store to a project and PandaStack writes the connection string into that project’s environment and redeploys it:

You linkYour app gets
A databaseDATABASE_URL
A KV storeKV_URL

Both point at the internal hostname, with real credentials already filled in. So the code you actually write is:

import { Pool } from "pg";
import { createClient } from "redis";
const db = new Pool({ connectionString: process.env.DATABASE_URL });
const kv = createClient({ url: process.env.KV_URL });

No connection string to copy out of the dashboard, no secret to paste into a config file, nothing to rotate by hand. Unlink and the variable is removed and the app redeploys again.

Container apps in the same organization reach each other by Helm release name, on port 80:

http://custom-<project-name>

So if you deployed a project called api, another app in the same organization calls it at http://custom-api. This is the pattern for a frontend calling a backend, or a worker calling an internal service, without either one going out to the public internet and back.

Set it as an environment variable on the calling app so you’re not hardcoding it:

INTERNAL_API_URL=http://custom-api

Keeping a database or KV store off the public internet

Section titled “Keeping a database or KV store off the public internet”

Both databases and KV stores are created as either public or private:

  • Public — a TLS endpoint is issued at <name>.db.pandastack.app, reachable from anywhere with the password. Use this when something outside PandaStack needs to connect: a local dev machine, a GUI client, an external service.
  • Privateno public endpoint is issued. There is no public hostname to hand out and none appears in the dashboard. Your apps still reach it on the internal hostname exactly as before.

If your app runs on PandaStack and nothing outside needs access, choose private. It’s the smaller surface, and you lose nothing — the internal path is the one your app was going to use anyway.

Visibility is set when you create the resource:

Terminal window
curl -X POST https://api.pandastack.io/v1/kv \
-H "Authorization: Bearer $PANDASTACK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"sessions","tier":"kv-free","visibility":"private"}'

In the dashboard, it’s the public/private choice in the create form for both databases and KV stores.

A typical setup — a Next.js frontend, a Node API, Postgres, and Redis — wires up like this:

  1. Deploy the API as a project named api. Link the database and the KV store to it. It now has DATABASE_URL and KV_URL pointing at internal hostnames.
  2. Create the database and KV store as private. Nothing outside PandaStack needs them.
  3. Deploy the frontend. Give it INTERNAL_API_URL=http://custom-api for server-side calls.
  4. Only the frontend has a public URL. The API, the database, and the KV store are reachable from your own services and have no public address.

Server-side requests in the frontend hit http://custom-api and never leave the network. Browser-side requests still need a public route, so if the browser calls the API directly, the API needs its own public URL — internal DNS doesn’t resolve from a user’s laptop.