Orkestia
Blog
App Host

Static egress IP

A fixed outbound public IP your processes send vendor traffic from, for vendors that only accept allowlisted addresses.

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

  1. 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.
  2. Declare it on a process: which IP, and which vendor hosts (and TCP endpoints) go through it.
  3. 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.list shows 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"}]
}
FieldWhat it is
ipThe name you allocated.
destinationsHTTP(S) hosts reached through the IP. *.vendor.com covers vendor.com and every subdomain. The port defaults to 443.
tcpFixed 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.

VariableUse
EGRESS_PROXY_URLAn HTTP proxy (http://<proxy>:3128) for the declared hosts.
EGRESS_PAC_URLA 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.