Public Network Services
Fly.io has public and private network services available. The public network services connect applications to the wider public internet, while the private network services allow application instances to communicate with other application instances within the Fly.io private network.
We announce global IP blocks from all of our datacenters over BGP, otherwise known as anycast. Anycast is a core internet routing mechanism that connects clients to the “nearest” server advertising a block of IPs. You can read all about it on Wikipedia.
A new Fly App running a public service automatically gets a dedicated anycast IPv6 address when it’s first deployed.
Along with the dedicated IPv6, apps configured to handle either
- HTTP on port 80, or
- TLS & HTTP on port 443
(or both) are automatically given a shared anycast IPv4 address on the first deployment.
Shared IPs are recommended unless you have an explicit need for your own IP. These IPs are shared across many apps and organizations. Routing is based on your app’s domain. One reason to use a dedicated IP is if your app needs to do something over UDP.
If you want to allocate a shared IPv4 to an app without a public IPv4 address, this is possible (with flyctl v0.0.439 and newer) using
fly ips allocate-v4 --shared
Allocating a dedicated IPv4 anycast address is now opt-in only, but can still be done manually with
fly ips allocate-v4
when needed; for example, if you want your app to handle TCP directly.
If your app has a shared IPv4 address and you allocate a dedicated one, the shared IPv4 is released automatically.
IPv6 addresses and shared IPv4 anycast addresses are free. Dedicated IPv4 addresses are billed monthly.
The following steps should allow you to switch an app with a custom domain from a dedicated IPv4 address to a free shared IPv4 without downtime.
- Add a shared IPv4 address via
flyctl ips allocate-v4 --shared
- If you don’t use a CNAME to
.fly.dev, add the shared IPv4 as an A record.
- Confirm your app works via shared IP:
curl -Iv http://<your-hostname> --resolve <your-hostname>:80:<new-shared-ipv4>. You should receive a 301 redirect response.
- If you are using a custom domain and don’t already have an SSL certificate for this app, create one. The cert is required for routing to your app via the custom domain, even for HTTP connections.
- Wait for DNS caches to clear. Five minutes is likely enough, but this varies wildly. This is determined by the larger of your DNS record’s TTL and that of our
- Remove the dedicated IPv4 from the app using
fly ips release <ipv4-address-to-release>. You can view your app’s IP addresses using
fly ips list.
- Remove the unwanted IPv4 address from your DNS if it was set manually as an A record.
If you do not have a custom domain (your app is accessed by its
.fly.dev address), you don’t have to change any DNS records yourself, and you don’t need to add your own SSL cert.
handlers config setting in the
services.ports section of
fly.toml to specify which middleware applies to incoming TCP connections for each port you accept external connections on. Use these to convert TCP connections into something your application can handle.
Valid handler strings:
"http": Convert TCP connection to HTTP
"tls": Convert TLS connection to unencrypted TCP
"pg_tls": Handle TLS for PostgreSQL connections
"proxy_proto": Wrap TCP connection in PROXY protocol
If you don’t specify handlers, we just forward TCP to your application as-is. This is useful if you want to handle TLS termination yourself, for example.
Many applications have limited HTTP support, the
HTTP middleware normalizes HTTP connections and sends HTTP 1.1 requests to the application process. This is roughly how
nginx and other reverse proxies work, and allows your application to globally accept modern HTTP protocols (like HTTP/2) without extra complexity.
If your application stack has good HTTP/2 support (like Go), you will get better performance accepting TCP connections directly, and using the TLS handler to terminate SSL. Your application does need to understand
h2c for this to work, however.
The HTTP handler adds a number of standard HTTP headers to requests, and a few Fly.io-specific headers for convenience:
|The IP address Fly.io accepted a connection from
|A comma separated list of proxy servers the request passed through. MDN has full documentation for this header
|Original client protocol, either
|Indicates if client connected over SSL, either
|Original connection port, header may be set by client
|Original connection port, always set by Fly.io
|Original incoming connection region
You can set a preference on HTTP requests for which region you would like to connect to:
TLS middleware terminates TLS using Fly.io-managed application certificates, then forwards a plaintext connection to the application process. This is useful for running TCP services and offloading
TLS to the Fly Proxy.
For performance purposes, the Fly Proxy will terminate TLS on the host a client connects to, and then forward the connection to the nearest available application instance.
TLS handler includes ALPN negotiation for HTTP/2. When possible, applications will connect to these kinds of Fly.io services using HTTP/2, and we will forward an unencrypted HTTP/2 connection (
h2c) to the application process.
pg_tls handler manages Postgres connections, so that you can expose your Postgres databases over the proxy securely. Some interesting notes by @glebarez on why standard TLS termination won’t work for Postgres: https://github.com/glebarez/pgssl#readme
proxy_proto handler adds information about the original connection, including client IP + port and server IP + port (from the client’s perspective). Most applications need additional logic to accept the proxy protocol, either using a prebuilt library or implementing the proxy protocol directly.
Fly.io supports version 1 (human-readable header format) of the PROXY protocol by default with the
proxy_proto handler. You can configure the
proxy_proto handler to use version 2 (binary header format) in the
services.ports.proxy_proto_options section of your
force_https configuration option automatically redirects HTTP to HTTPS. When enabled, all HTTP requests return a redirect response with a
301 status code. This option can only be set on HTTP handlers - deployments will fail if set on other handlers.
Fly Machiness have IPv6 addresses from which they make requests to the wider Internet without going through the Fly Proxy.
We don’t offer static IPs or regional IP ranges, and we discourage the use of our outbound IPs to bypass firewalls. A Machine’s outbound IP is liable to change without notice. This shouldn’t be a daily occurrence, but it will happen if a Machine is moved for whatever reason, such as a load-balancing of Fly Machines between servers in one region.
If you depend on knowing this address for some reason, it’s up to you to monitor it for changes.
The Machine’s outbound IPv6 address is available in the
FLY_PUBLIC_IP environment variable. Use
fly ssh console -s to get an interactive shell on the Machine of your choice.
/ # echo $FLY_PUBLIC_IP
You can also call out to an external service that’ll tell you your IP. For example:
/ # curl text.ipv6.wtfismyip.com
/ # dig +short txt o-o.myaddr.l.google.com @ns1.google.com
So this Machine’s outbound IPv6 address is
You may have to install software on the Machine to use
curl. On Debian,
dig belongs to the