# How Render handles private networking

- Date: 2026-04-01T11:04:09.281Z
- Tags: Networking, Platform, configuration, guides
- URL: https://render.com/articles/how-render-handles-private-networking


When your web service needs to talk to a database, a background worker, or an internal API, that traffic shouldn't go over the public internet. Render's [private network](https://render.com/docs/private-network) keeps communication between your services internal: no VPCs to configure, no subnets to manage, no NAT gateways to set up.

This article covers how private networking works on Render, how to connect your services, and what to watch out for.

## How it works

Every Render service in the same workspace and region shares a private network. Services on this network can communicate directly using internal hostnames, without traffic leaving Render's infrastructure.

[Private services](https://render.com/docs/private-services) take this a step further: they have no public URL at all. They're only reachable by other services on the same private network. This makes them ideal for internal APIs, background workers that accept requests, and anything you don't want exposed to the internet.

Each service gets a stable internal hostname (formatted as `<service-name>-<hash>:<port>`) that you can find in the *Connect* menu of the Render Dashboard under the *Internal* tab. Render Postgres databases and Key Value instances also have internal connection URLs for private network access.

```mermaid
graph TD
    Client((Public Internet)) -->|Public URL| Web[Web Service]
    
    subgraph Render Private Network - Same Region
        Web -->|Internal: http://worker-ab1c:5000| Worker[Private Service: Worker]
        Web -->|Internal: postgres://...| DB[(PostgreSQL)]
        Worker -->|Internal: http://inference-de2f:8080| AI[Private Service: AI Inference]
    end
    
    style Client fill:#f9f9f9,stroke:#333
    style Web fill:#81c784,stroke:#388e3c
    style Worker fill:#ffb74d,stroke:#f57c00
    style DB fill:#64b5f6,stroke:#1976d2
    style AI fill:#ba68c8,stroke:#7b1fa2
```

Internal traffic between services in the same region is free. It's faster than routing through the public internet and doesn't count toward your outbound bandwidth.

## What's on your private network

Not all service types participate in the private network the same way:

| Service type | Can send internal traffic | Can receive internal traffic |
|---|---|---|
| Web services | Yes | Yes |
| Private services | Yes | Yes |
| Background workers | Yes | No |
| Cron jobs | Yes | No |
| Free web services | Yes | No |
| Static sites | No | No |

All services must be in the *same workspace and region* to communicate over the private network. A service in Oregon can't reach a service in Frankfurt internally. Cross-region communication requires public URLs.

On [Professional workspaces](https://render.com/docs/professional-features) and higher, you can [block private network traffic between environments](https://render.com/docs/projects#blocking-cross-environment-traffic) for stricter isolation.

*Important:* Services on the same private network can reach each other by default. This is a workspace-level trust model, not a zero-trust architecture. If you need service-to-service authorization, implement it at the application layer with JWTs, API keys, or mutual TLS.

## Connecting services with a Blueprint

The most practical way to wire up private networking is through a `render.yaml` Blueprint. Here's a web service that connects to a private worker and a database:

```yaml
services:
  - type: web
    name: api-gateway
    runtime: node
    plan: standard
    buildCommand: npm install && npm run build
    startCommand: npm start
    healthCheckPath: /healthz
    envVars:
      - key: WORKER_HOST
        fromService:
          type: pserv
          name: task-worker
          property: hostport
      - key: DATABASE_URL
        fromDatabase:
          name: app-db
          property: connectionString
  - type: pserv
    name: task-worker
    runtime: node
    plan: standard
    buildCommand: npm install && npm run build
    startCommand: npm start
databases:
  - name: app-db
    plan: standard
    postgresMajorVersion: "16"
```

The `fromService` reference with `property: hostport` gives you the internal hostname and port (like `task-worker-ab1c:10000`). The `fromDatabase` reference with `property: connectionString` provides the internal Postgres connection string. Render injects both as environment variables at runtime.

## Common patterns

### Database isolation

By default, Render Postgres databases are accessible from both the private network and the public internet. For tighter security, remove the `0.0.0.0/0` entry from your database's [access control list](https://render.com/docs/postgresql#access-control) in the Dashboard. This blocks all public connections, so only services on your private network can reach the database.

### Web service to background worker

A public-facing web service handles incoming requests and dispatches work to a private service over the internal network. This keeps compute-heavy processing off your request path:

```javascript
async function dispatchTask(payload) {
  const workerUrl = process.env.WORKER_HOST;

  const response = await fetch(`http://${workerUrl}/process`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.INTERNAL_API_KEY}`
    },
    body: JSON.stringify(payload)
  });

  return response.json();
}
```

The `WORKER_HOST` value comes from the `fromService` reference in the Blueprint, so it always points to the correct internal address.

### AI inference pipelines

ML models that serve predictions over HTTP are good candidates for private services. Keeping them off the public internet protects model endpoints from external abuse and avoids exposing inference APIs directly.

## Debugging connectivity

When services can't reach each other, the issue is usually one of a few things:

- *Wrong URL:* You're using the public `.onrender.com` URL instead of the internal hostname. This still works, but traffic routes through the public internet (slower, and it consumes outbound bandwidth).
- *Wrong port:* Private services can bind to almost any port, but you need to target the port the destination service is actually listening on. Check the *Connect* menu in the Dashboard for the correct internal address.
- *Cross-region:* Services in different regions can't communicate over the private network. Verify both services are deployed to the same region.
- *Service type:* Background workers and cron jobs can't receive incoming traffic. If you need a service that accepts internal requests, use a private service (`type: pserv`).

The [Render CLI](https://render.com/docs/cli) helps with debugging: use `render logs` to check for connection errors on both the calling and receiving service. The [MCP server](https://render.com/docs/mcp-server) lets you pull logs and metrics from your editor to diagnose issues without switching context.

## Next steps

- Define your private services and database connections in a [`render.yaml` Blueprint](https://render.com/docs/infrastructure-as-code) using `fromService` and `fromDatabase` references
- Lock down your database by removing public access from the [access control list](https://render.com/docs/postgresql#access-control)
- Add application-layer auth (JWTs or API keys) for service-to-service calls
- Review the [private network docs](https://render.com/docs/private-network) and [private services docs](https://render.com/docs/private-services) for the full reference

## FAQ

###### Can services in different regions communicate over the private network?

No. The private network is scoped to a single workspace and region. Services in Oregon can't reach services in Frankfurt over the private network. For cross-region communication, use public URLs with application-layer authentication (like JWTs or mutual TLS).

###### Is internal traffic between services free?

Yes. Traffic between Render services in the same region stays on the private network and doesn't count toward your outbound bandwidth. There's no charge for internal traffic.

###### What's the difference between a private service and a background worker?

Both are inaccessible from the public internet. The difference is that a [private service](https://render.com/docs/private-services) can receive incoming traffic from other services on the private network, while a [background worker](https://render.com/docs/background-workers) cannot. Use a private service when other services need to send it requests. Use a background worker when it only needs to pull work from a queue.

###### How do I find my service's internal hostname?

Go to the service's page in the [Render Dashboard](https://dashboard.render.com), click *Connect*, and select the *Internal* tab. The internal address is shown there in the format `<service-name>-<hash>:<port>`. In a `render.yaml` Blueprint, use `fromService` with `property: host` or `property: hostport` to inject it as an environment variable.

###### Do I need to add authentication between my internal services?

It's strongly recommended. Render's private network uses a workspace-level trust model: any service in the same workspace and region can reach any other service on the network by default. If you want to enforce that only specific services can call specific endpoints, add application-layer authentication like JWTs, API keys, or mutual TLS.

###### Can I block private network traffic between environments?

Yes, on [Professional workspaces](https://render.com/docs/professional-features) and higher. You can [block cross-environment traffic](https://render.com/docs/projects#blocking-cross-environment-traffic) so services in one environment can't reach services in another, even if they're in the same workspace and region.


