A B2B integration goes live with a payment processor that allow-lists IPs. The team puts the integration behind an ALB, fronts the ALB with a NAT gateway, and gives the partner the NAT gateway IP. Six months later they replace the NAT gateway during a VPC restructure. The new NAT has a new Elastic IP. The partner allow-list is not updated for 48 hours because the change request goes through their compliance team. The integration is offline for two days. The lesson is that NAT gateways are not the right tool for the “give a partner a stable IP” job — they were never designed to be public ingress endpoints. NLB with Elastic IPs is. The IPs are allocated at NLB creation, pinned per AZ, and never rotate for the life of the NLB.
What’s happening
NLB supports two IP modes on the public side. The default is AWS-managed IPs that can rotate (rare, but technically possible during AZ events). The mode you want for partner allow-lists is Elastic IPs, one per Availability Zone the NLB lives in. The Elastic IP is allocated separately, attached to the NLB at creation, and stays attached until you delete the NLB. The IP cannot be changed in place — to give the partner a new IP, you build a new NLB.
The properties this gives you:
-
No-rotate. The Elastic IPs do not change for any operational reason short of NLB replacement. Target instances can come and go, target groups can be rewired, security groups can be updated — the IP the partner sees is constant.
-
One IP per AZ. A three-AZ NLB has three Elastic IPs. The partner allow-list needs all three. DNS resolves to all three round-robin; the client picks one and connects.
-
No SNAT for client IPs. NLB preserves the client source IP end-to-end by default. The partner sees the client IP, not the NLB IP, when the connection arrives at the target. This is the inverse of the static-egress problem — for egress, the NLB IP is what the partner’s firewall sees as the source.
-
Cross-zone load balancing is optional. With cross-zone off, traffic landing on the IP in
us-east-1aonly reaches targets inus-east-1a. With cross-zone on, the IP inus-east-1acan reach any target in any AZ. The default is off; turn it on for most application workloads.
The use cases this enables:
-
Partner allow-lists. B2B integrations where the partner’s firewall only allows traffic from a specific set of source IPs. You give them the NLB Elastic IPs at integration time and never touch them again.
-
On-prem firewall rules. Your application is reachable from an on-prem network over a Direct Connect or VPN, and the on-prem firewall has a rule keyed to the destination IP. NLB Elastic IPs make that rule stable.
-
IP-based geo controls. Some compliance regimes require traffic to land at a specific IP that is documented in the system of record. NLB’s IP lives in that document and does not move.
-
DNS-bypass integrations. A few legacy clients connect by IP rather than hostname. NLB Elastic IPs let them keep working without forcing a code change at the client.
The thing this does not give you: HTTP-layer features. NLB is L4. If the partner integration is HTTPS and you also need WAF, OIDC, or path routing, the pattern is NLB → ALB chain — the NLB is the public layer with Elastic IPs, and forwards to an ALB as a target by IP. ALB does the L7 work; the partner allow-list points at the NLB IPs.
How to verify
Provisioning the Elastic IPs first, then the NLB:
# Allocate one Elastic IP per AZ the NLB will use
EIP_A=$(aws ec2 allocate-address --domain vpc --query 'AllocationId' --output text)
EIP_B=$(aws ec2 allocate-address --domain vpc --query 'AllocationId' --output text)
EIP_C=$(aws ec2 allocate-address --domain vpc --query 'AllocationId' --output text)
# Create the NLB with those EIPs attached to specific subnets
aws elbv2 create-load-balancer \
--name partner-nlb-prod \
--type network \
--scheme internet-facing \
--subnet-mappings \
SubnetId=subnet-aaa,AllocationId=$EIP_A \
SubnetId=subnet-bbb,AllocationId=$EIP_B \
SubnetId=subnet-ccc,AllocationId=$EIP_C
Confirming the IPs on the live NLB (this is what you send the partner):
aws ec2 describe-addresses \
--allocation-ids "$EIP_A" "$EIP_B" "$EIP_C" \
--query 'Addresses[].{IP:PublicIp,Allocation:AllocationId}' \
--output table
Verifying that those IPs are what the partner actually sees, from outside AWS:
# Resolve the NLB hostname from outside AWS and check that every answer is one of the EIPs
dig +short partner-nlb-prod-abc123.elb.us-east-1.amazonaws.com
The output should be the three Elastic IPs. If DNS returns a different IP, the NLB is using AWS-managed IPs instead of EIPs — the subnet-mapping at create time was wrong.
For the egress case (partner sees your traffic as coming from these IPs), the NLB does SNAT to its own IPs when the target initiates a return connection to the client. The source IP the partner sees is one of the EIPs. Confirm with a test connection from a target to the partner endpoint while watching the partner’s logs.
For the ingress case (partner traffic lands at your NLB), the client IP is preserved through the NLB by default. Targets see the real client IP in their server logs. If the targets are EC2 with security groups, the security group rules must permit traffic from the client IP range, not from the NLB IP — this is a frequent source of “the NLB is up but my targets reject the connection” tickets.
The fix
The deployment runbook for a new partner integration:
-
Allocate the Elastic IPs first, as a separate step before the NLB. The allocation ID is a long-lived artifact; the NLB is a shorter-lived artifact. Tag the EIPs with the partner name and the environment.
-
Build the NLB with explicit subnet-mappings. Do not let Terraform or CloudFormation default to AWS-managed IPs.
-
Confirm the EIPs are attached and DNS resolves to them. Send the three IPs to the partner before any other integration step.
-
Pin the partner side. The partner allow-list goes in both directions: ingress (partner traffic from their IPs to your NLB) and egress (your traffic from the NLB IPs to their endpoint). The egress side is often forgotten on the first round and discovered the first time a target reaches out.
-
Document the IPs as production artifacts. A README in the deployment repo, a row in the integration runbook, a tag on the resources. The IPs survive your tenure; the institutional memory of why they exist needs to too.
The migration playbook for when you have to swap the underlying NLB (account migration, region change, or a major architectural shift):
-
Build the new NLB with new Elastic IPs in parallel. Do not touch the old one yet.
-
Pre-warn the partner with the new IPs. Their allow-list change has a lead time — usually 5 to 30 business days for a regulated partner. Submit the request well before the migration date.
-
Run both NLBs in parallel during the cutover window. DNS at the partner’s end determines which one they hit; with both allow-listed, the connection succeeds regardless. Use weighted DNS or a CNAME flip to shift traffic.
-
Decommission the old NLB only after the partner confirms the allow-list change is live. A dangling allow-list entry on the old IPs is not a problem; a missing allow-list entry on the new IPs is an outage.
-
Release the old Elastic IPs only after a quarantine period. Once released, an EIP returns to the AWS pool and can be reallocated to someone else. Keeping the old IPs allocated for a few weeks after the cutover is cheap insurance against rollback.
Three rules we hold:
EIPs allocated, never auto-managed. A partner-facing NLB that does not have Elastic IPs is a future outage. Make the EIP allocation a hard gate in the deployment template.
Three IPs per partner, not one. Sending the partner one IP and not the other two means the partner allow-list does not cover an AZ. Connection lands in that AZ, partner rejects it, intermittent outage.
Document the IPs in the integration contract. When the integration is renegotiated or the partner is acquired, the IPs are part of the contract artifact. Treat them like any other long-lived production identifier.
For the L7 features you may also want behind the NLB, see ALB listener rules — host and path routing. For the comparison of when NLB is the right choice versus ALB, see ALB vs NLB vs GWLB.
Stack Harbor designs ALB + NLB topologies and the operating model around them as part of Managed AWS onboarding and ongoing Managed Cloud Ops. If your partner integrations are pinned to a NAT gateway that was never meant to be ingress, start with an infrastructure consult.