Skip to content
Fixedmark
Integration · PaaS and containers

Fly.io Static Egress IP: Native vs Portable Pair

Fly.io Machines get dynamic outbound IPs by default, and Fly sells app-scoped static egress IPs for $3.60 per month per IPv4, per region. That is the cheapest native option of any platform here. Use Fixedmark instead when you need the same IPs across Fly and other platforms, IPs that survive leaving Fly, or connection logs.

Why Fly.io's outbound IP changes

Fly runs Machines on shared hosts in each region. Without static egress, outbound connections use the host's address. When a Machine moves to another host, scales, or is replaced on deploy, the source IP can change.

Fly.io static egress IPs: an honest comparison

Per Fly's docs, you allocate static egress IPs with fly ips allocate-egress --app <app> -r <region>. They are scoped to one app and one region, so you need one per region the app runs in. Each costs $3.60 per month per IPv4 address, billed hourly, with IPv6 included. Fly documents limits of 64 Machines per IP and 1,024 concurrent connections per Machine to each destination IP, and notes that new Machines can take a while to pick up the IPs.

If your app lives only on Fly, runs in one or two regions, and the partner just needs a stable address, use Fly's native feature. It is cheaper than a Fixedmark dedicated pair and needs no client code. Fixedmark makes sense on Fly in narrower cases. You want IPs that survive moving the app off Fly. The same allowlisted pair must serve Fly and also Vercel or Lambda. You want per-connection logs to debug a partner's rejections. Or you want a destination allowlist, so a leaked token can only reach the hosts you named.

Sources for Fly.io pricing

Checked October 2026. Confirm current details on the vendor's site before you decide. Prices and limits change.

Set up a static IP on Fly.io

If you choose Fixedmark on Fly, store the URL as a secret and use the client snippets below. Machines are long-running, so SOCKS5 and bm tunnel both work.

Fixedmark is in early access. Proxy URLs are issued at launch. Join early access to get yours when your region opens.

Before you start

  • A Fly app and flyctl, logged in.
  • A Fixedmark proxy URL (https://APP_ID:TOKEN@mum.egress.fixedmark.com:443) and the two IPs of your pair. Both are issued at launch. Join early access to get them.
  • For databases: the SOCKS5 URL (socks5h://APP_ID:TOKEN@mum.egress.fixedmark.com:1080) or the bm tunnel CLI.
  • Access to the partner's or database's allowlist, or a contact who can add two IPv4 addresses for you.

Steps

  1. 1
    Store the proxy URL as a Fly secretRun fly secrets set FIXEDMARK_PROXY_URL=https://APP_ID:TOKEN@mum.egress.fixedmark.com:443. Fly restarts the Machines with the new secret. Add --stage to apply it on the next deploy instead.
  2. 2
    Match regionsUse the Fixedmark region closest to your primary Fly region, for example Singapore for sin.
  3. 3
    Configure the HTTP clientPass the proxy URL to the client that calls the allowlisted API, using the snippet for your runtime.
  4. 4
    Verify from a MachineRun fly ssh console, then the curl check below. It should print one of your two Fixedmark IPs.
// npm install undici  (undici 8 needs Node.js 22.19+)
import { fetch, ProxyAgent } from "undici";

// FIXEDMARK_PROXY_URL=https://APP_ID:TOKEN@mum.egress.fixedmark.com:443
const proxy = new ProxyAgent(process.env.FIXEDMARK_PROXY_URL);

const res = await fetch("https://api.partner.example/v1/orders", {
  dispatcher: proxy,
  headers: { authorization: `Bearer ${process.env.PARTNER_API_KEY}` },
});
console.log(res.status, await res.json());

SDKs that do not use fetch (the Stripe and Salesforce Node SDKs, got, axios) accept an HTTP agent. Pass new HttpsProxyAgent(process.env.FIXEDMARK_PROXY_URL) from the https-proxy-agent package, which supports https:// proxy URLs. With axios, also set proxy: false so it does not apply its own proxy logic. undici 8 needs Node 22.19 or later. On older Node, install undici@7.

Verify the egress IP on Fly.io

Check the IP before you send it to a partner. fly ssh console -C 'sh -c "curl -sS --proxy $FIXEDMARK_PROXY_URL https://fixedmark.com/api/ip"' runs the check on a live Machine. Install curl in the image if it is missing. The endpoint https://fixedmark.com/api/ip returns the caller's IP as plain text, and the What's my IP tool shows the same thing in a browser.

The proxied call should print one of your two Fixedmark IPs every time. The direct call should print a different address. If both print the same platform IP, the client is not using the proxy.

# Needs curl 7.52 or later for an https:// proxy URL.
# Prints the IP the destination sees. Run it a few times:
# you should only ever see the two IPs of your pair.
curl -sS --proxy "$FIXEDMARK_PROXY_URL" https://fixedmark.com/api/ip

# Same check over SOCKS5, the path database drivers use.
curl -sS --proxy "$FIXEDMARK_SOCKS_URL" https://fixedmark.com/api/ip

# Without the proxy, for comparison. This is the platform's own IP.
curl -sS https://fixedmark.com/api/ip

Databases: Postgres, MySQL, and MongoDB

Database drivers do not speak HTTP CONNECT, so the proxy URL above does not cover them. On Fly.io you have two options. Drivers with SOCKS5 support can use FIXEDMARK_SOCKS_URL directly: the MongoDB Node.js driver accepts proxyHost and proxyPort options, and Go's pgx accepts a SOCKS5 dialer. For any other driver, run the bm tunnel CLI (planned for launch) as a process in your Dockerfile entrypoint or as a separate process group in fly.toml, then point the driver at 127.0.0.1.

TLS to the database stays end to end. Fixedmark forwards encrypted bytes and never sees your queries. If your driver verifies the server certificate, keep the real database hostname as the TLS server name when you connect through the local tunnel.

# Forward local port 5432 to the database through your Fixedmark IPs.
# bm tunnel is planned for launch; flags are not final.
bm tunnel 5432:db.example.com:5432 &

# Point the driver at the local end of the tunnel.
DATABASE_URL="postgres://app:secret@127.0.0.1:5432/app?sslmode=require"

Common errors and fixes

Some Machines show a Fly IP and others show a Fixedmark IP

Only code that passes the proxy uses it. Check every process group runs the same release, and that the secret is not staged on some Machines only.

407 Proxy Authentication Required

The proxy did not get valid credentials. Check that the env var holds the full URL with APP_ID:TOKEN@, with no quotes or trailing newline. If the token contains @, :, or /, URL-encode it. Some clients drop credentials from the URL: pass them separately (Deno's basicAuth, axios proxy.auth) or switch to undici's ProxyAgent, which reads them from the URL.

TLS errors to the proxy: wrong version number, EPROTO, Proxy CONNECT aborted, or unable to connect to proxy

The client does not support an https:// proxy URL and is speaking plain HTTP to port 443. Upgrade it: curl 7.52+, urllib3 1.26+ (requests), httpx 0.26+, undici ProxyAgent, Go 1.10+. In Ruby, use Typhoeus: Net::HTTP and Faraday's default adapter cannot tunnel HTTPS through a TLS proxy. For older axios releases, set proxy: false and pass https-proxy-agent as httpsAgent. Java's built-in HttpClient cannot speak TLS to a proxy at all. If you cannot upgrade, use the SOCKS5 URL.

ETIMEDOUT or ECONNRESET to the partner, or a 403 from the partner

The proxy connected, but the destination dropped you. Usually only one IP of the pair is on the allowlist, or the partner has not applied the change yet. Add both IPs. If a destination allowlist is set on your Fixedmark token, check the host and port are on it.

The IP check prints the platform's IP, not a Fixedmark IP

The request went direct. Node's built-in fetch ignores HTTPS_PROXY unless NODE_USE_ENV_PROXY=1 is set (Node 22.21+ and 24+). Many SDKs ignore proxy env vars too. Pass the proxy to the client explicitly, as in the snippets above.

429 or 407 with an X-Proxy-Error: quota_exceeded header

Planned behavior when you pass your plan's soft limit. Check usage in the dashboard or raise the plan.

Database driver has no proxy option

node-postgres, mysql2, psycopg, and most ORMs cannot use an HTTP proxy. Use a SOCKS5-capable driver with FIXEDMARK_SOCKS_URL, or run bm tunnel where the platform allows a second process.

Gotchas on Fly.io

Fly's private 6PN network between your apps is unaffected by the proxy. Only traffic you send through it uses the Fixedmark pair.

  • If you also allocate Fly static egress IPs, calls through the proxy still show the Fixedmark pair. The two do not conflict.
  • fly secrets set restarts Machines and resets their ephemeral file system.

Frequently asked questions

Does Fly.io support static outbound IPs?

Yes. Per Fly's docs, you can allocate static egress IPs scoped to an app and region with fly ips allocate-egress. They cost $3.60 per month per IPv4 address, with IPv6 included.

Should I use Fixedmark or Fly's static egress?

If the app runs only on Fly and the partner just needs a stable address, use Fly's static egress. Choose Fixedmark when you need the same IPs across several platforms, IPs that move with you, connection logs, or destination allowlists.

Can I keep my allowlisted IP if I move off Fly.io?

Not with Fly's native IPs, which belong to the app on Fly. Fixedmark IPs belong to your Fixedmark account, so you can redeploy anywhere and keep the same allowlist.

Fix your egress IP.

Fixedmark is in early access. Join the list to get a dedicated IP pair for Fly.io when your region opens.