Skip to Content
Framework GuidesInternal Networking

Internal Networking

Critical: Apps deployed on Ghayma cannot reach *.ghayma.tech domains (e.g., auth.ghayma.tech, api.ghayma.tech, *.db.ghayma.tech). You must use internal Kubernetes URLs instead.

Why External URLs Don’t Work

Apps deployed on Ghayma run inside a Kubernetes cluster. When your app tries to reach auth.ghayma.tech, the DNS resolves to the server’s external IP. Due to how the network is configured (Cilium CNI), traffic that leaves the cluster and tries to come back in through the same IP gets dropped. This is known as hairpin NAT.

Your app → auth.ghayma.tech → the platform's external IP → Traffic exits cluster → tries to re-enter → ❌ TIMEOUT

This affects all *.ghayma.tech domains when called from inside the cluster.

External clients are not affected. Mobile apps, browsers, and servers running outside the cluster can reach *.ghayma.tech normally.

For incoming requests, your app reads the visitor’s address from X-Real-IP — see The visitor’s IP.

Internal URLs

Use these internal URLs in your deployed apps instead of the public *.ghayma.tech domains:

ServiceExternal URL (local dev)Internal URL (deployed app)
Auth APIhttps://auth.ghayma.techhttp://auth-service.paas-system.svc.cluster.local:9000
Platform APIhttps://api.ghayma.techhttp://paas-api.paas-system.svc.cluster.local:8080
PostgreSQLpg-{name}-{id}.db.ghayma.tech:5432pg-{name}-{id}.databases.svc.cluster.local:5432
MongoDBmg-{name}-{id}.db.ghayma.tech:27017mg-{name}-{id}.databases.svc.cluster.local:27017
ValkeyNone yet: use the tunnelvk-{name}-{id}.pdb-{project-uuid}.svc.cluster.local:6379

A database’s external hostname is its own, and a named principal is what opens it. The route exists only while the database has an active principal, and TLS is required on it.

Use different environment variables for local development and deployed environments.

Local Development (.env.local)

Use external URLs — your machine is outside the cluster, so they work normally:

.env.local
DATABASE_URL="postgresql://user:[email protected]:5432/mydb?sslmode=require" GHAYMA_AUTH_URL="https://auth.ghayma.tech/v1/my-auth-app" MONGODB_URI="mongodb://user:[email protected]:27017/mydb?authSource=admin&tls=true"

TLS is required for external PostgreSQL connections — every URL the platform hands out carries ?sslmode=require, and the per-database host refuses unencrypted connections.

The cleanest way to get those URLs for your own machine is a named principal per tool — its own credential, its own level, revocable on its own — or, for the deployed app’s exact variables, ghayma connect --local.

Deployed App (via CLI)

A managed database needs no env var of its own. The platform derives DATABASE_URL / MONGODB_URI / REDIS_URL (and the MONGODB_URL and VALKEY_URL aliases) — already pointing at the internal hostname — for every site connected to the database. Connect the app instead of writing a URL by hand:

# Let the 'admin' site reach the database — it gets the variables too ghayma db sites my-postgres --add admin

An auth app needs none either. A site connected to one is handed GHAYMA_AUTH_URL — already in its in-cluster form — along with the public URL, the app id and the server key.

Set a URL yourself only for an endpoint the platform does not derive for you — an auth app in another project, when yours has none of its own — and use its internal form:

# Another project's auth service (internal, no TLS) ghayma env set GHAYMA_AUTH_URL=http://auth-service.paas-system.svc.cluster.local:9000/v1/their-auth-app

Then redeploy:

ghayma deploy

Tip: a DATABASE_URL, MONGODB_URI or REDIS_URL you set by hand does not survive on a site granted a database of that engine — the platform’s derived value replaces it. Use a different variable name to point an app at some other database. See Automatically injected variables.

Finding Internal URLs

Databases

Run ghayma db credentials <name> — the Host field is the internal hostname, and no password is shown:

$ ghayma db credentials my-postgres 🔌 Connection details for 'my-postgres' (postgres) Host: pg-mypostgres-a1b2c3d4-0.databases.svc.cluster.local Port: 5432 Database: db_mypostgres ...

It also lists the connected sites and the variables each receives. A deployed app never needs the host by hand: its DATABASE_URL, MONGODB_URI or REDIS_URL already holds it, with the app’s own credential. ghayma db credentials <name> works the same way for a Valkey (CLI v0.14.0 or later; older: ghayma db info <name>) — see Valkey.

Auth Service

The auth service internal URL follows this pattern:

http://auth-service.paas-system.svc.cluster.local:9000/v1/{auth-app-id}

Replace {auth-app-id} with your auth app’s ID (shown in the dashboard or via ghayma auth list). A deployed app never has to build it by hand — GHAYMA_AUTH_URL already holds exactly this.

Which Sites Can Reach a Database

Using the internal URL is necessary but not always sufficient. Reaching a database is a connection — the one record of which app may use which service — and every database enforces it as a per-site network policy: only a connected app can open a connection to it.

  • A new database is connected only to the sites you choose — when you create it, or when ghayma deploy offers it — even in a project with a single site. Until then it is deny-all. See How a service gets connected.
  • Every other app (an admin console, an api service, … deployed as its own site) is blocked until connected — even with the correct internal URL and credentials, connections time out.

The connection also decides which sites receive the database’s connection variables: a connected site gets DATABASE_URL / MONGODB_URI / REDIS_URL, a site without one gets neither the credentials nor the network path. Disconnecting takes both away. See Automatically injected variables.

A connected site’s builds get the same two things — the network path, and the variables as build secrets — so a migration can run during the build. A Valkey is the exception: no build reaches it. See Using your database during the build and Valkey.

Manage them from the site’s Connections tab, from the database’s page in the dashboard, or with ghayma db sites, which works exactly as before:

ghayma db sites my-postgres # see which apps are connected ghayma db sites my-postgres --add admin # connect the 'admin' site

A change converges within ~2 minutes — no redeploy needed.

Troubleshooting: connection timeouts from a deployed app

Work through these in order — they cover every common case:

  1. Are you using the internal hostname? …databases.svc.cluster.local (a Valkey’s is vk-…pdb-<project-uuid>.svc.cluster.local), not …db.ghayma.tech. External hostnames time out from inside the cluster no matter what (see above).
  2. Is this site connected to the database? Run ghayma db sites <db-name> — if your site shows -, connect it with --add <site-slug> and wait ~2 minutes. ghayma db credentials <db-name> lists the connected sites too, with the variables each receives.
  3. Is the value clean? No literal quote characters inside the env value, and no tls=true on internal MongoDB URIs (internal traffic doesn’t use TLS — see TLS Differences).
  4. Redeployed after changing env vars? Env changes apply on the next deploy of that site — the platform’s own database variables are the exception: they reach the running app as soon as the connection or the app’s credential changes.

TLS Differences

Internal URLs use HTTP (not HTTPS) and don’t require TLS. This is safe because traffic stays entirely within the cluster’s private network.

ExternalInternal
Protocolhttps://http://
TLS requiredYesNo
MongoDB tls=trueRequiredRemove it
Certificate verificationAutomaticNot needed

A Valkey’s internal URL is a plain redis:// URL, without TLS; it has no external URL yet (see Valkey).