Private Network
Communicate securely between services without traversing the public internet.
Your Render services in the same region can communicate with each other over their shared private network, without traversing the public internet.
- Web services and private services each have a unique hostname on their private network. These services can listen for private network traffic on almost any port (see below) and use any protocol.
- Note that web services must use HTTP for public traffic.
- PostgreSQL and Redis instances each have an internal URL specifically for private network connections.
- Background workers and cron jobs can’t receive private network traffic, but they can send requests to other service types.
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 is also supported for advanced use cases. See Direct IP communication.
The following ports cannot be used for private network communication:
10000
(this port is reserved for web service public traffic)18012
18013
19099
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 and they belong to the same account or team.
Free web services can’t receive private network traffic. They can send private network requests to your data stores and paid services in the same region.
These service types each have an internal address or URL:
- Web services
- Private services
- PostgreSQL databases
- Redis instances
This value is available from each service’s Connect menu in the Render Dashboard (see the Internal tab):
Private services also display this value as their Service Address:
The elastic
private service above has the internal address elastic-qeqj:10000
. 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 and cron jobs 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.
Use this method only if hostname-based communication 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 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.
Each service exposes its discovery hostname to its own environment via the RENDER_DISCOVERY_SERVICE
environment variable. If you manage your services via Blueprints, you can also access another service’s discovery hostname (see Referencing values from other services).
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.
Regardless of your language, we strongly recommend using 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
).
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(', ')}`);
});
}