{"id":"IL-0354","org":"Fly.io","date":"2026-09-23","docs":[],"title":{"en":"Fly.io private networking (6PN) broke for some machines after edges received gateway /80 routes","ko":"Fly.io 일부 머신의 사설 네트워크 6PN 이 끊겼고 다음 날 잠깐 다시 끊겼다"},"domain":["networking"],"impact":{"scope":"Private networking (6PN) for the subset of machines whose host IDs had been reused by edges","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Not everyone at Fly.io knew that a worker's host ID could be reused by a host that is not a worker, because host ID assignment was not well documented, and the gateways had not been touched for a long time, so the problem stayed hidden until edges became gateways. All existing 6PN checks used machines on their original host, so they could not catch any problem with host ID reuse or stable 6PN addresses; host IDs need to stop influencing 6PN routing in general for this kind of incident not to recur.","ko":"Fly.io 는 워커의 host ID 가 워커가 아닌 호스트에 다시 쓰일 수 있다는 점이 문서로 잘 남아 있지 않아 처음에 모두가 떠올린 것은 아니었다고 봤다. 게이트웨이를 오래 손대지 않았기 때문에 에지를 게이트웨이로 쓰기 전까지 문제가 드러나지 않았다. 더 깊은 문제는 6PN 라우팅의 알림과 자동 테스트였다. 기존 점검은 모두 원래 호스트에 있는 머신을 써서 host ID 재사용으로 생기는 충돌이나 고정 6PN 주소 구현과 얽힌 문제를 잡을 수 없었다. Fly.io 는 알림이나 큰 장애로 이어지지 않은 문제가 더 있었을 수 있다고 했다. 6PN 주소의 host ID 가 경로 결정에 아무 역할도 하지 않아야 이런 장애가 되풀이되지 않는다고 결론지었다."},"related":[],"sources":[{"url":"https://fly.io/infra-log/gateways-broke-6pn/","kind":"blog","archived":"https://web.archive.org/web/20261022131441/https://fly.io/infra-log/gateways-broke-6pn/"}],"summary":{"en":"A Fly.io machine's 6PN address has the form fdaa:netid:netid:hostid:hostid:machid:machid:2, with the host ID taken from the physical host where the machine was created. Since 6PN addresses became stable in 2025, a machine keeps that host ID wherever it runs, and when a worker is decommissioned its internal IP and host ID can be given to a new host of another kind. In 2026 Fly.io was moving 6PN routing from its custom eBPF program to standard Linux routing tables, routing machines by where they are rather than by their address. Gateway peers are the exception: interactive peers used by flyctl, for example for fly ssh console, have no identity in the routing state and can only be routed by the gateway's host ID, so Fly.io added a Linux route sending the gateway's whole /80 prefix per netid to the gateway host. When a separate project made the edges act as gateways for non-static peers, edges that had reused decommissioned workers' host IDs got /80 routes, which overrode the per-machine routing code because that code relies on machines not yet accessed locally having no route. Machines still carrying those host IDs lost 6PN.\n\nFly.io deleted the bad /80 routes from the routing tables on all affected hosts and rolled back the changes to s6pnforwarderd, the daemon that manages 6PN routes, within about 10 minutes. A follow-up fix added interactive peers only as /128 routes, but the real problem was the priority between the gateway logic and the worker logic, and the failure briefly recurred the next day. Fly.io then fixed it by always looking first for machines that carry a host's ID, whether or not the host is known to be a worker.","ko":"Fly.io 의 6PN 주소는 fdaa:netid:netid:hostid:hostid:machid:machid:2 꼴이다. hostid 부분은 머신을 만들 때 머신이 있던 물리 호스트에서 정해지고, 2025년 5월 무렵 이전에는 머신을 옮길 때도 다시 정해졌다. 2025년에 6PN 주소를 고정한 뒤에도 6PN 라우팅용 맞춤 eBPF 프로그램은 한 번도 옮기지 않은 머신의 경로를 기본적으로 hostid 만 보고 정했다. 2026년에 Fly.io 는 디버깅과 변경이 어려운 맞춤 라우팅 구현을 줄이고 표준 Linux 라우팅 테이블에 기대도록 6PN 을 바꾸고 있었다. 이 작업의 핵심은 host ID 를 6PN 라우팅에서 떼어 내는 일이었다. 그러면 머신의 경로는 주소가 아니라 머신이 지금 있는 위치로 정해진다.\n\n게이트웨이 피어에는 static 과 interactive 두 종류가 있다. fly wireguard create 로 만드는 static 피어는 플랫폼의 라우팅 상태에 고정된 신원이 있다. flyctl 이 fly ssh console 같은 일시적 작업에 쓰는 interactive 피어는 그런 신원이 없어서, 게이트웨이의 host ID 를 보고만 경로를 정할 수 있다. 주소에 게이트웨이 피어와 머신을 가를 다른 표시가 없어서, Fly.io 는 netid 마다 게이트웨이의 /80 접두사 전체를 게이트웨이 호스트로 보내는 규칙을 Linux 라우팅 테이블에 넣었다. 기존 게이트웨이에서는 이 방식에 문제가 없었다.\n\n물리 워커 호스트를 퇴역시키면 그 내부 IP 가 풀리고, 내부 IP 가 host ID 를 정한다. 새 호스트는 새 IP 와 host ID 를 받을 수도 있고 퇴역한 호스트의 값을 다시 받을 수도 있다. 6PN 주소가 고정된 뒤로는 머신이 워커가 아닌 호스트에 다시 배정된 host ID 를 계속 가질 수 있었다. 최근 용량과 연결성을 늘리려고 새 에지를 많이 들이면서 에지들이 퇴역한 워커의 host ID 를 다시 썼다. 에지를 non-static 피어의 게이트웨이로 쓰기 시작하자 에지에 /80 경로가 들어갔고, 이 경로가 머신별 라우팅 코드를 덮어썼다. 그 코드는 로컬에서 아직 접근한 적이 없는 머신에는 경로가 없다는 전제에 기대고 있었다. 그 host ID 를 가진 머신들은 에지가 아니라 워커에 있었기 때문에 6PN 이 끊겼다.\n\nFly.io 는 약 10분 만에 영향을 받은 모든 호스트의 라우팅 테이블에서 잘못된 /80 경로를 지우고, 6PN 경로를 관리하는 데몬 s6pnforwarderd 의 변경을 롤백했다. 이어 interactive 피어를 /128 로만 넣도록 고쳤지만, 문제는 /80 경로만이 아니라 게이트웨이 로직과 워커 로직 사이의 우선순위였기 때문에 다음 날 잠깐 다시 일어났다. 그 뒤 호스트가 워커인지와 상관없이 그 호스트의 ID 를 가진 머신이 있는지부터 찾도록 고쳐 근본 원인을 일단 막았다."},"trigger":{"en":"Fly.io changed its 6PN routing so that edges act as gateways for non-static peers. Every edge was then treated as a gateway and got a /80 route for its prefix in the Linux routing table, which overrode the per-machine routing for machines that had kept a reused host ID.","ko":"에지가 static 이 아닌 피어의 게이트웨이 역할도 하도록 바꾸면서, 모든 에지에 게이트웨이처럼 netid 마다 /80 접두사 전체를 그 호스트로 보내는 라우팅 규칙이 들어갔다."},"severity":"unknown","symptoms":["network-unreachable","requests-misrouted"],"timeline":{"detected_after":null,"resolved_after":"10m"},"date_kind":"incident","detection":null,"components":["host-routing","network-control-plane"],"root_cause":"deploy-rollout","remediation":[{"text":{"en":"Deleted the bad /80 routes from the routing table on all affected hosts","ko":"영향을 받은 모든 호스트의 라우팅 테이블에서 잘못된 /80 경로를 지웠다."}},{"text":{"en":"Rolled back the changes to s6pnforwarderd, the daemon that manages 6PN routes","ko":"6PN 경로를 관리하는 데몬 s6pnforwarderd 의 변경을 롤백했다."}},{"text":{"en":"Shipped a fix that adds interactive peers only as /128 routes, which did not prevent a brief recurrence the next day","ko":"interactive 피어를 /128 로만 넣도록 고쳐 배포했다. 이 수정은 근본 원인을 고치지 못해 다음 날 잠깐 같은 문제가 다시 났다."}},{"text":{"en":"Changed routing to always look first for machines that carry a host's ID, whatever kind of host it is","ko":"호스트가 워커로 알려져 있는지와 상관없이 그 호스트의 ID 를 가진 머신이 있는지부터 찾도록 고쳐 근본 원인을 일단 막았다."}}],"action_items":[{"text":{"en":"Give the machines used to test and alert on 6PN non-local 6PN addresses","ko":"6PN 문제를 시험하고 알림을 내는 데 쓰는 머신에 당분간 로컬이 아닌 6PN 주소를 배정한다."}},{"text":{"en":"Make host IDs in 6PN addresses carry no weight in routing decisions, for gateway peers as well as machines","ko":"중장기적으로 6PN 주소의 host ID 가 경로 결정에 아무 역할도 하지 않게 한다. 머신은 지금도 대체로 그렇지만 게이트웨이 피어는 아직 그렇지 않다."}},{"text":{"en":"Move new peers and machines across the board to a better model than the current hack kept for existing gateway peers","ko":"기존 게이트웨이 피어를 살리려고 둔 지금의 임시 방식을 끝내고, 새 피어와 머신 전체를 더 나은 모델로 옮긴다."}}],"contributing":["assumption-unverified","test-coverage-gap"],"recurrence_of":null,"symptoms_text":{"en":"On 23 September 2026, private networking (6PN) stopped working for a subset of Fly.io machines. Fly.io mitigated it in about 10 minutes, and the problem came back briefly the following day.\n\nThe affected machines were those whose 6PN address carries the host ID of a worker host that had since been decommissioned, where a newly provisioned edge had picked up that worker's internal IP address and so its host ID. When edges started to be treated as gateways, each edge received a route for its whole /80 gateway prefix, and traffic for those machines followed the edge's route, although the machines run on workers and never on an edge.\n\nFly.io's existing 6PN checks did not catch it, because all of them use machines that still sit on their original host.","ko":"9월 23일 일부 머신의 사설 네트워크인 6PN 이 끊겼다. 다음 날에도 같은 문제가 잠깐 다시 일어났다.\n\n영향을 받은 머신은 6PN 주소에 담긴 host ID 가 나중에 에지 호스트에 다시 배정된 머신이었다. 에지에 새로 들어간 /80 경로가 머신별 라우팅을 덮어써, 이 머신들의 경로가 머신이 실제로 있는 워커가 아니라 에지 쪽으로 잡혔다.\n\n처음에는 워커의 host ID 가 워커가 아닌 호스트에 다시 쓰일 수 있다는 점을 모두가 바로 떠올리지는 못했다. 기존 점검은 모두 원래 호스트에 있는 머신을 썼기 때문에 이 충돌을 잡지 못했다."},"updated_at":"2026-11-14T12:34:00.846972Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0354","org_slug":"fly-io"}