# Private Network

Your Render services in the same region can communicate over their shared private network, _without_ traversing the public internet:

[img]

- Each [web service](web-services) and [private service](private-services) has a unique hostname on the private network.
  - These services can listen for private network traffic on _almost_ any port ([see below](#port-restrictions)) and use any protocol.
  - [Free web services](free#free-web-services) can _send_ private network requests, but they can't _receive_ them.
- Each Render [Postgres](postgresql) and [Key Value](key-value) instance has an internal URL specifically for private network connections.
- [Background workers](background-workers) and [cron jobs](cronjobs) can _send_ private network requests, but they can't _receive_ them.

Private network communication is fast, safe, and reliable. It uses stable internal hostnames and IPs that dynamically map to individual instance addresses (which can change between deploys).

[Direct IP-based communication](#direct-ip-communication-advanced) is also supported for advanced use cases.

> *Need a private connection to a non-Render system?*
>
> See [Integrating with AWS PrivateLink](#integrating-with-aws-privatelink).

## Port restrictions

Each service is limited to a maximum of 75 open ports.

The following ports _cannot_ be used for private network communication:

- `10000`
- `18012`
- `18013`
- `19099`

## What's on my private network?

[Static sites](static-sites) are _not_ on a private network.

Other Render services are on the same private network if they're deployed in the same [region](regions) _and_ they belong to the same workspace.

> With a [*Professional* workspace](professional-features) or higher, you can [block private network traffic](projects#blocking-cross-environment-traffic) from entering or leaving a particular environment.

## How to connect

These service types each have an internal address or URL:

- Web services
- Private services
- Render Postgres databases
- Render Key Value instances

This value is available from each service's *Connect* menu in the [Render Dashboard][dboard] (see the *Internal* tab):

[img]

Private services also display this value as their *Service Address*:

[img]

The private service above has the internal address `elasticsearch-2j3e:9200`. Other services _on the private network_ can communicate with it at this address.

> *You might need to specify a service's expected protocol in its internal address string when you connect.*
>
> For example, you might need to specify `http://elastic-qeqj:10000` instead of just `elastic-qeqj:10000`.

[Background workers](background-workers) and [cron jobs](cronjobs) _don't_ have an internal address, so they can't receive inbound private network traffic. However, they can _send_ requests to other service types on their private network.

## Integrating with AWS PrivateLink

With a *Professional* workspace or higher, you can create secure, low-latency connections from your private network to compatible non-Render systems hosted on AWS:

[img]

Use a private link to connect to Snowflake, MongoDB Atlas, or resources in your own AWS VPC.

For details, see [Private Link Connections](private-link).

## Direct IP communication (advanced)

> *Use this method _only_ if [*hostname-based communication*](#how-to-connect) does not serve your use case.*

For advanced use cases, you can send private network requests directly to the IP of a specific service instance. This is most commonly useful for [scaled](scaling) services in the following cases:

- You need to message each running instance of your service individually (such as to pull metrics with a monitoring tool like Prometheus).
- You want to implement custom load balancing logic for your service, instead of relying on Render's built-in load balancing.

Each web service and private service has an associated *discovery hostname* that resolves to _all_ of its active instance IPs. By convention, this hostname has the format `[INTERNAL_HOSTNAME]-discovery` (e.g., `myapp-ne5j-discovery`). To find your service's internal hostname, see [How to connect](#how-to-connect).

Each service exposes its discovery hostname to its own environment via the `RENDER_DISCOVERY_SERVICE` environment variable. If you manage your services via [Blueprints](infrastructure-as-code), you can also access _another_ service's discovery hostname (see [Referencing values from other services](blueprint-spec#referencing-values-from-other-services)).

### Example: Obtaining instance IPs

The snippet below shows a JavaScript function that fetches all of a service's instance IP addresses via DNS lookup and prints them to the console. For other languages, use a supported DNS lookup library.

> *Use a lookup API that relies on the underlying system's DNS resolver.*
>
> This ensures that your lookup applies necessary DNS configuration (such as rules defined in `/etc/resolv.conf`).

```js
const dns = require('dns')

// Obtain discovery hostname from environment variable
const discoveryHostname = process.env.RENDER_DISCOVERY_SERVICE

function fetchAndPrintIPs() {
  // Perform DNS lookup
  // all: true returns all IP addresses for the given hostname
  // family: 4 returns IPv4 addresses
  dns.lookup(discoveryHostname, { all: true, family: 4 }, (err, addresses) => {
    if (err) {
      console.error('Error resolving DNS:', err)
      return
    }
    // Map over results to extract just the IP addresses
    const ips = addresses.map((a) => a.address)
    console.log(`IP addresses for ${discoveryHostname}: ${ips.join(', ')}`)
  })
}
```