Answers you can trust, from Codeables
Every page on Codeables is structured and verified — built so people and the AI agents they rely on can trust it. Explore more from the source behind this answer.
Explore CodeablesHow do I use Fly.io .internal DNS to connect services privately across apps?
Most Fly.io apps don’t actually want to talk to the public internet when they’re calling each other. They want fast, encrypted, app‑to‑app traffic over Fly.io’s private network. That’s what .internal DNS is for: every app and Machine gets private names you can resolve from inside the Fly network, so services can connect across apps without exposing ports publicly.
Quick Answer: Fly.io’s
.internalDNS lets your apps resolve and connect to each other over Fly.io’s private network, using predictable hostnames instead of hard‑coding IPs. You use per‑app and per‑Machine.internalnames from within your Fly apps or over WireGuard, and Fly’s private DNS and overlay network handle the rest.
The Quick Overview
- What It Is: A private DNS naming scheme (
.internal) and resolver built into Fly.io’s private network so apps and Machines can discover and talk to each other securely, without going over the public internet. - Who It Is For: Teams running multi‑service or multi‑app architectures on Fly.io—APIs + workers, web + Postgres, internal control planes, AI agents, etc.—that need private, low‑latency networking without managing their own service discovery.
- Core Problem Solved: You no longer need to juggle static IPs, environment‑specific hostnames, or public services just to let two internal apps talk.
.internalgives you stable, private names that resolve correctly from anywhere on your Fly network.
How It Works
Fly.io gives every app and Machine an identity on a private, encrypted network. Inside that network, a DNS service resolves .internal hostnames to private IP addresses. Calls never touch the public internet; they ride Fly’s private overlay instead.
At a high level:
- Private names for apps and Machines:
Each app gets.internalnames you can use from other apps. Machines typically get internal addresses (e.g.,fdaa:0:…) and can be reached via app‑level DNS records. - Resolution from inside the private network:
When a process in your app doesgetaddrinfo("my-app.internal"), Fly’s private DNS answers with private IPs for Machines in that app. If you’re connected via WireGuard, your laptop/infra can resolve them too. - Encrypted, routed traffic:
Once you connect to that IP/port, traffic flows over Fly.io’s private network, not the public internet. The Fly Proxy and private routing layer handle region placement and delivery.
You don’t manage this DNS server or write zone files; you just use predictable .internal hostnames and let Fly handle mapping to Machines.
How It Works, Step by Step
-
Create apps that share a private network
Apps in the same Fly organization automatically share a private network. You deploy them as usual:
fly launch --name web-app fly launch --name api-serviceBy default, both apps get private addresses and register in
.internal. -
Use
.internalhostnames from your codeInside
web-app, you can callapi-servicewith a private hostname like:curl http://api-service.internal:8080/healthOr from your app code:
// Node/TypeScript example const res = await fetch('http://api-service.internal:8080/v1/tasks');DNS resolution happens against Fly’s private DNS, and the connection stays inside the Fly network.
-
Optionally connect from your own network via WireGuard
If you want your laptop, CI, or on‑prem services to reach apps privately:
fly wireguard create --org my-orgAfter you bring up the WireGuard tunnel,
*.internalnames for your Fly apps also resolve from your machine, and traffic goes through the tunnel—not the public internet.
Features & Benefits Breakdown
| Core Feature | What It Does | Primary Benefit |
|---|---|---|
Per‑app .internal hostnames | Gives each app predictable private DNS names (app-name.internal) | Simple, stable service discovery across apps |
| Private network routing | Routes traffic over Fly.io’s encrypted private network, not the public web | Better latency, no public exposure, avoids cross‑internet hops |
| Org‑wide reach & WireGuard | Makes .internal reachable from other org apps and via WireGuard tunnels | Unified, private addressing from apps, tools, and on‑prem infra |
Note: Exact hostname forms vary slightly per feature (e.g., Postgres, Sprites, Machines). The principle is the same: resolve
*.internal, get private IPs, talk over the private network.
Ideal Use Cases
- Best for multi‑service applications: Because it lets your web, API, worker, and database apps talk over private DNS without exposing ports.
web.internal → api.internal → worker.internalbecomes a normal, private call chain. - Best for hybrid private networking: Because
.internalplus WireGuard lets you treat Fly‑hosted services as if they’re on the same private LAN as your own infrastructure, without building your own overlay network.
Limitations & Considerations
-
DNS is only private to your Fly org and WireGuard peers:
.internalnames are not globally resolvable. They work inside your Fly apps and any WireGuard‑connected network. If you need public access, you still provision Anycast IPs or hostnames on Fly Proxy. -
You still need services listening on the right ports:
DNS just finds the private IP. Your app still has to listen on the expected port—e.g.,0.0.0.0:8080—and your client must connect with the right scheme (http://,https://,tcp://) and port. DNS won’t fix a misconfigured listener.
Pricing & Plans
.internal DNS and private networking are built‑in capabilities of the Fly.io platform; there’s no separate line item for “private DNS.” You pay for the Machines, storage, and bandwidth you actually consume.
A rough way to think about it:
- Baseline Fly.io usage: Best for teams deploying typical web/API apps that want private app‑to‑app networking “for free”—you just get
.internalhostnames as part of using Fly Machines. - Larger orgs with many services: Best for teams consolidating multiple internal services, databases, and sandboxes on Fly who want everything on a single private network, reachable via
.internaland WireGuard from their existing tooling.
(If you’re in the “we have compliance people and uptime SLOs” camp, Fly.io also backs this with SOC2 Type 2, SSO, and guaranteed support response times; talk to sales for those details.)
Frequently Asked Questions
Can I use .internal DNS to connect to apps in different regions?
Short Answer: Yes. .internal works across all regions in your Fly org’s private network.
Details:
When you resolve api-service.internal from web-app, you get private addresses for Machines running that app, regardless of region. Traffic then routes over Fly.io’s private network between regions (e.g., iad → lhr) without touching the public internet. You still want to place Machines sensibly (e.g., keep latency‑sensitive pairs in the same region), but the DNS layer doesn’t care; it’s org‑wide.
Can I reach .internal names from my laptop or on‑prem network?
Short Answer: Yes—if you connect using WireGuard.
Details:
By default, .internal only resolves inside Fly apps. To use it from your local machine or your data center, you:
-
Create a WireGuard peer for your org:
fly wireguard create --org my-org -
Configure the peer on your laptop/server.
-
Bring up the tunnel and verify:
dig api-service.internal
You should now see private Fly addresses in the response. Any TCP/UDP traffic you send to those addresses goes through the WireGuard tunnel onto Fly.io’s private network.
Summary
Fly.io’s .internal DNS is your built‑in service discovery for private networking. Apps in your org get stable private names that resolve to private IPs, traffic flows over Fly.io’s encrypted network instead of the public internet, and you avoid juggling IPs or bolting on your own discovery system. Combine .internal with WireGuard and you get a single, private address space you can reach from your apps, your laptops, and your own infrastructure.