Static egress IP
Some vendors only accept calls from IP addresses they have allowlisted. App Host processes normally call out from default addresses that are not yours alone. A static egress IP is an outbound address that belongs to your organization alone. A process declares the IP and the vendor hosts it calls, and only those calls go out through it.
How it works
- Allocate an egress IP for your organization. You get the public address right away, so you can send it to the vendor before anything uses it.
- Declare it on a process: which IP, and which vendor hosts (and TCP endpoints) go through it.
- Use it per call in your code, through the proxy settings App Host gives the process. Everything else keeps leaving directly.
There is no password or key involved. The platform itself decides who may use an IP:
- only your organization's processes that declare that IP can send through it;
- no other organization and no other process can reach it;
- hosts you did not declare are refused by the proxy.
1. Allocate the IP
apphost.egress-ip.allocate
{"name": "vendor"}
The result has public_ip. That is the address to give the vendor's allowlist. name is how processes refer to the IP: lowercase, up to 20 characters.
- Up to 2 egress IPs per organization. They are metered and not charged today.
- The IP is yours until you release it.
apphost.egress-ip.listshows your IPs and the processes that send through each. - Only members of your organization can allocate or release one; end users and API keys cannot.
2. Declare it on a process
Add egress to the process in apphost.process.apply:
"egress": {
"ip": "vendor",
"destinations": ["*.vendor.com", "api.vendor.com:8443"],
"tcp": [{"name": "db", "to": "db.vendor.com:1433"}]
}
| Field | What it is |
|---|---|
ip | The name you allocated. |
destinations | HTTP(S) hosts reached through the IP. *.vendor.com covers vendor.com and every subdomain. The port defaults to 443. |
tcp | Fixed TCP forwards for non-HTTP services (SQL Server, SFTP, ...). Each gets its own port on the proxy. |
At most 16 destinations and forwards in total. Names only: IP addresses as destinations are refused.
apphost.process.apply replaces the process definition. When you add egress to an existing process, send its full spec (image, env, secret_env, ...) with egress added. If you leave egress out of a later apply, the process keeps it. "egress": {} removes it.
3. Use it in your code
The process gets these environment variables. None of them is secret.
| Variable | Use |
|---|---|
EGRESS_PROXY_URL | An HTTP proxy (http://<proxy>:3128) for the declared hosts. |
EGRESS_PAC_URL | A proxy auto-config for browsers: only the declared hosts go through the IP, everything else goes direct. |
EGRESS_TCP_<NAME> | host:port to connect to instead of the vendor's address, one per TCP forward (EGRESS_TCP_DB). |
Send only the vendor calls through the proxy. A call that skips it leaves from the default addresses, and the vendor refuses it. Do not set a global HTTPS_PROXY: the proxy refuses every host you did not declare.
Python
import os, requests
proxy = os.environ["EGRESS_PROXY_URL"]
r = requests.get("https://api.vendor.com/orders", proxies={"https": proxy})
Node
import { ProxyAgent, fetch } from "undici";
const agent = new ProxyAgent(process.env.EGRESS_PROXY_URL);
const r = await fetch("https://api.vendor.com/orders", { dispatcher: agent });
Chrome / Chromium
chromium --proxy-pac-url="$EGRESS_PAC_URL" ...
TCP forward
# Connect to the forward instead of db.vendor.com:1433
sqlcmd -S "${EGRESS_TCP_DB/:/,}" ...
Check it
From the process, curl -x "$EGRESS_PROXY_URL" https://<a declared host> reaches the vendor from your public_ip. To see the address the vendor sees, temporarily declare an IP-echo host such as api.ipify.org and call it through the proxy.
Availability
The service is highly available: after a failure, the same IP is back within about one to two minutes. Retry vendor calls that fail with a connection error.
Release an IP
apphost.egress-ip.release
{"name": "vendor", "confirm_ip": "<the public IP>"}
Release is refused while a process still declares the IP: remove egress from it first. A released address is deleted and can never be recovered, so the vendor's allowlist stops matching.
