Since 1 February 2024, AWS bills $0.005/hour for every public IPv4 address in your account - attached or not. That's ~$3.65/address/month, or ~$934/month for a working /24 (256 addresses). Addresses you bring in via BYOIP are exempt, and Elastic IPs allocated from your own pool are free too.
The AWS docs cover the happy path but skip the two things that actually cause tickets: the authorization-context signing step, and whether a leased block even qualifies. This post fills both gaps and gives you the full CLI sequence.
TL;DR: a leased /24 works fine, because AWS validates control via your RIR, not ownership. You just need the registrant to set up the ROA.
What AWS actually checks
When you provision a block, AWS verifies two registry-side records:
- A ROA (Route Origin Authorization) in your RIR's RPKI, authorizing Amazon's ASNs to originate the prefix.
- RDAP records proving you control the range, verified either with a signed X.509 authorization context (the classic path) or a DNS TXT record via IPAM (the newer path).
Neither of these cares who owns the block in a contractual sense - they care who holds the registration. For a leased /24, that's the IP owner. So the block qualifies as long as the registrant creates the ROA and, if you're using the X.509 path, signs the authorization message for you. A decent leasing provider does this as part of onboarding; if you're wiring it up yourself, budget a back-and-forth with whoever holds the registration.
Requirements checklist
- Prefix: /24 is the most specific IPv4 you can bring. No /25 or longer. (IPv6: /48 for publicly advertised.)
- RIR: ARIN, RIPE, or APNIC, registered to a business/institutional entity, not an individual.
- ROA: authorize ASNs 16509 and 14618 (use 8987 for GovCloud). Set maxLength to /24. Allow up to 24h to propagate.
- Quota: 5 ranges per Region; more via a Support ticket.
- Scope: one block, one Region at a time.
If you already advertise the block from your own ASN, create the ROA for your ASN before adding Amazon's, or you risk disrupting your live announcement.
The step everyone trips on: the authorization context
provision-byoip-cidr takes a --cidr-authorization-context with a Message and a Signature. This is where people get stuck, because the docs describe it more than they show it.
The Message is a plaintext string in a fixed format:
1|aws|<account-id>|<cidr>|<expiry-yyyymmdd>|SHA256|RSAPSS
You sign that message with the private key of the X.509 certificate whose public half is published in your RIR's RDAP record. The base64 of that signature becomes Signature:
# message
text_message="1|aws|123456789012|203.0.113.0/24|20261231|SHA256|RSAPSS"
# sign with your BYOIP private key
signed_message=$(echo -n "$text_message" \
| openssl dgst -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 \
-sign private-key.pem -keyform PEM \
| openssl base64 | tr -- '+=/' '-_~' | tr -d '\n')
Two things bite here: the signature must use RSA-PSS, and the base64 needs URL-safe substitution (+=/ → -_~). Get either wrong and provisioning fails validation with an unhelpful message.
The provisioning sequence
1. Provision the block:
aws ec2 provision-byoip-cidr \
--cidr 203.0.113.0/24 \
--cidr-authorization-context Message="$text_message",Signature="$signed_message"
This is asynchronous - it returns immediately, but the block isn't usable yet.
2. Poll until it flips to provisioned:
aws ec2 describe-byoip-cidrs --max-results 5
You'll see State walk from pending-provision → provisioned. If it lands on failed-provision, the StatusMessage tells you why - almost always a ROA that hasn't propagated or a maxLength that isn't /24.
3. Advertise it:
aws ec2 advertise-byoip-cidr --cidr 203.0.113.0/24
State moves to advertised and AWS starts originating the prefix.
4. Allocate an Elastic IP from your pool (free, unlike Amazon-provided EIPs):
aws ec2 allocate-address \
--region eu-central-1 \
--public-ipv4-pool ipv4pool-ec2-xxxxxxxx
5. Associate it with an EC2 instance, NAT gateway, or Network Load Balancer:
aws ec2 associate-address \
--instance-id i-0abc123 \
--allocation-id eipalloc-0abc123
That's it. From here your BYOIP addresses behave like any Elastic IP - minus the hourly charge.
Where BYOIP addresses actually attach
- EC2 / VPC via Elastic IP - instances, NAT gateways, NLBs.
- AWS Global Accelerator - assign IPv4 from your own pool directly (IPv4 only).
⚠️ Amazon SES also has a "BYOIP" feature - it is not this one. SES BYOIP is a paid, per-address monthly feature (256-address minimum) for preserving sending reputation. It does not dodge the $0.005/hr charge. Different mechanism, different billing, different purpose. Don't conflate them.
Timing and gotchas
- No SLA on provisioning. It's async; allow up to 24h for ROA propagation and a few business days end to end once registry records are set.
-
pending-provisionthat won't clear = ROA not propagated, or maxLength ≠ /24. - ROA mismatch = wrong prefix or missing an ASN. Both 16509 and 14618 must be authorized, valid for the whole time the block lives in AWS.
- Signature failures = not RSA-PSS, or non-URL-safe base64.
- Quota surprises = you get 5 ranges/Region before needing Support.
Is it worth it?
For a few addresses with no existing block: no - just use Amazon's and move on. For dozens of addresses, or anywhere IP reputation and migration continuity matter: absolutely. A working /24 is ~$934/month on Amazon-provided addresses versus $0 in AWS address charges on BYOIP. Your only cost is the lease, which runs a fraction of that - and nothing at all if you own the block.
Full version with the cost breakdown, the leased-IPv4 details, and the complete requirements is on our blog: How to Bring Leased IPv4 to AWS.
Top comments (0)