Static IPs for GitHub Actions Runners
Static egress IPs for GitHub Actions runners are a BYOC capability on AWS, GCP, and Azure. Here is the setup, the address math, and the per-GB cost.
Last verified:
A stable outbound IP address for GitHub Actions jobs comes from running the runner inside your own cloud account, because GitHub-hosted runners egress from a large shared pool of public addresses. On WarpBuild, static IPs are a BYOC capability on AWS, GCP, and Azure, switched on per custom runner, and they are unavailable on WarpBuild Cloud runners.
This guide covers how to tell a real allowlist requirement from a workaround, how to turn static IPs on, how many addresses your partner ends up trusting, and what the private subnet path costs per gigabyte on your cloud bill.
Diagnosis
Three requests produce this ticket. Only one of them genuinely needs a fixed egress address, so separate them before you build anything.
Partner and vendor allowlists
A payment processor, an SFTP drop, or an enterprise customer's API gateway accepts connections from an approved list of source addresses. The security review that produced that list will reject a range that rotates. The symptom is an integration test that passes from a laptop on the office network and fails from GitHub Actions with a 403 or a refused TCP handshake.
Database and warehouse firewalls
Managed database security rules and warehouse network policies are address based. A migration job, a seed job, or a nightly export that runs on every merge has to arrive from an address the rule already trusts. Teams usually discover this when the staging rule was opened wide during a hackathon and the production rule was written properly.
Internal services behind an address rule
Legacy internal endpoints often sit behind an address rule rather than an identity check. Deploy jobs, smoke tests, and cache warmers that call them fail in the same way, and the failure is intermittent enough that it gets retried for weeks before anyone reads the firewall logs.
Why GitHub-hosted runners cannot satisfy the requirement
GitHub publishes the address ranges its hosted runners use through the meta endpoint of its REST API. Checked on 2026-08-13, the actions key of the GitHub meta endpoint listed 7,280 CIDR ranges. GitHub republishes that document as ranges change, so an allowlist built from it needs a sync job of its own and covers a very large public surface in the meantime. Most security reviews decline both properties, which is how the ticket reaches the runner team.
The availability fact
Static egress IPs are a BYOC capability. BYOC runs on AWS, GCP, and Azure, and the addresses come from your own account. WarpBuild Cloud runners have no static egress address and no label that grants one. A workflow that needs a fixed source address moves to a custom runner in your cloud; every other job in the repository can stay where it is.
Check the requirement before you build for it
Ask the endpoint owner whether an identity-based control clears the same review: OIDC federation with short-lived credentials, mTLS client certificates, or a signed request header. Address allowlists carry operational cost on both sides, and the party maintaining the list is usually happier without one. When the answer is that the address rule is contractual or comes from a compliance control, static IPs are the fix, and the rest of this guide is about applying them with a small blast radius.
Fix
1. Connect the cloud account
Connecting an account creates the IAM role or service account that WarpBuild uses to create and manage runners in your infrastructure. The flow starts in the BYOC section of the dashboard and is documented in the BYOC setup documentation.
2. Create a stack with public and private subnets
A stack is the region, VPC, subnets, and object storage that runners use. The VPC needs at least one public subnet and one private subnet, both with internet connectivity. Private subnets route outbound traffic through NAT gateways, and the addresses attached to those gateways become the external addresses your partner sees.
Two configuration recommendations from the docs matter here. Use three public and three private subnets across three availability zones, which keeps instance type availability high. Size each subnet for the peak number of concurrent runners, with 250 addresses per subnet as the recommended floor.
3. Create a custom runner with static IPs enabled
Static IPs are a property of the custom runner, alongside instance types, disk, and fallback instances. Enabling the option places that runner's instances in the private subnets. The runner registers under a Runner ID prefixed with warp-custom-, and that ID is what runs-on references.
4. Collect the addresses and hand them over
On AWS the addresses are the elastic IPs attached to the NAT gateways, one per availability zone, so a three-zone stack yields three addresses. GCP and Azure follow the same shape with their regional NAT addresses. The list stays stable across job counts, so the partner allowlist holds three entries whether you run 10 jobs a day or 10,000.
5. Keep static IPs on one runner
Create a second custom runner in the same stack with static IPs disabled and point everything else at it. Those instances launch in public subnets, skip the per-GB data processing charge, and behave identically otherwise. The cost model below shows what this split is worth per month.
6. Put the definition in code
Terraform support exists for BYOC on AWS, so stacks and runners live in the same module as the rest of your infrastructure. Setup details per cloud are in the AWS BYOC documentation and the guide to running GitHub Actions runners in your own AWS account.
Configuration
Split the workflow by which job needs the address
The build job below runs on a WarpBuild Cloud Linux runner. Only the job that talks to the allowlisted partner runs on the BYOC runner with static IPs, so the expensive network path carries the smallest possible number of gigabytes.
name: integration
on:
push:
branches: [main]
jobs:
build:
runs-on: warp-ubuntu-latest-x64-4x
steps:
- uses: actions/checkout@v4
- run: make build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
partner-sync:
needs: build
runs-on: warp-custom-partner-use1
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Confirm the egress address
run: curl -sS --fail https://api.ipify.org
- name: Push the build to the partner endpoint
env:
PARTNER_TOKEN: ${{ secrets.PARTNER_TOKEN }}
run: |
curl -sS --fail \
-H "Authorization: Bearer ${PARTNER_TOKEN}" \
-F "bundle=@dist/bundle.tar.gz" \
https://partner.example.com/v1/ingestThe Confirm the egress address step is worth keeping permanently. When a partner reports a rejected request, that one line tells you within a single job run whether the address changed or the allowlist did.
Cloud quotas the static IP path consumes
Quotas apply per stack in the stack's region, and they assume a peak concurrent job count. Generally available Linux and Windows runners do not have plan-level concurrency caps; on BYOC, your cloud quota is the relevant infrastructure ceiling. At 1,000 concurrent jobs the address quota below asks for 1,003 addresses.
| Cloud | Resource | Quota per stack | What drives it |
|---|---|---|---|
| AWS | Elastic IPs | 3 plus one per concurrently running job | 3 for the NAT gateways across 3 availability zones, 1 attached to each running job |
| AWS | NAT gateways | 3 | One per availability zone |
| AWS | VPCs | 1 | One VPC per stack |
| GCP | In-use regional external IPv4 addresses | 3 plus one per concurrently running job | Cloud NAT addresses plus one per running job |
| GCP | Cloud NAT | 3 | One per stack |
| Azure | In-use regional external IPv4 addresses | 3 plus one per concurrently running job | NAT addresses plus one per running job |
| Azure | NAT | 3 | One per stack |
Quota values transcribed from the WarpBuild BYOC documentation on 2026-08-13. Compute and disk quotas scale with vCPU per job and disk per job and are listed on the same pages. Raise them before the first large run, because a quota rejection surfaces as a queued job rather than an error.
Runner labels and rates used above
The Linux x64 labels referenced in the workflow, with per-minute rates from the pricing page:
| Runner label | OS | vCPU | RAM | Storage | Price per minute |
|---|---|---|---|---|---|
| warp-ubuntu-latest-x64-2x | Ubuntu 24.04 | 2 | 8 GB | 150GB SSD | $0.004 |
| warp-ubuntu-latest-x64-4x | Ubuntu 24.04 | 4 | 16 GB | 150GB SSD | $0.008 |
| warp-ubuntu-latest-x64-8x | Ubuntu 24.04 | 8 | 32 GB | 150GB SSD | $0.016 |
| warp-ubuntu-latest-x64-16x | Ubuntu 24.04 | 16 | 64 GB | 150GB SSD | $0.032 |
BYOC Linux and Windows runners are billed at $0.002 per minute in WarpBuild fees, with the compute billed by your own cloud account at your own rates.
Cost or Time Model
Assumptions
Every input is stated so you can substitute your own. Cloud rates are AWS list prices for US East (N. Virginia).
| Input | Value | Source |
|---|---|---|
| Jobs per month that touch the partner endpoint | 66 | Your workflow run history |
| Bytes processed per partner job | 0.4 GB | Your job logs |
| All other jobs per month | 1,800 | Your workflow run history |
| Bytes processed per other job | 2.0 GB | Package installs, image pulls, cache restores |
| Outbound bytes to the internet per job | 0.15 GB | Your job logs |
| Average job duration | 8 minutes | Your workflow run history |
| NAT data processing | $0.045 per GB, both directions | Amazon VPC pricing, checked on 2026-08-13 |
| NAT hourly charge | $0.045 per gateway-hour | Amazon VPC pricing, checked on 2026-08-13 |
| Data transfer out to the internet | $0.09 per GB after 100 GB free per month | Amazon EC2 On-Demand pricing, checked on 2026-08-13 |
| WarpBuild BYOC fee | $0.002 per minute | Pricing page |
The arithmetic
Fixed gateway charge: 3 gateways times 730 hours times $0.045 equals $98.55 per month. This line exists as soon as the stack has private subnets with gateways, whether one job uses them or every job does.
Data processed by the partner jobs: 66 times 0.4 equals 26.4 GB. At $0.045 per GB that is $1.19.
Data processed by every other job, if you point them at the static IP runner too: 1,800 times 2.0 equals 3,600 GB, plus the 26.4 GB above equals 3,626.4 GB. At $0.045 per GB that is $163.19.
Data transfer out to the internet: 1,866 jobs times 0.15 GB equals 279.9 GB, less the 100 GB monthly allowance, leaves 179.9 GB billable at $0.09 equals $16.19. This charge applies in public subnets as well, so static IPs do not create it.
WarpBuild fee: 1,866 jobs times 8 minutes equals 14,928 minutes, at $0.002 per minute equals $29.86.
| Monthly line | Public subnets, no static IPs | Static IPs scoped to partner jobs | Static IPs for the whole fleet |
|---|---|---|---|
| NAT hourly charge, 3 gateways | $0.00 | $98.55 | $98.55 |
| NAT data processing | $0.00 | $1.19 | $163.19 |
| Data transfer out | $16.19 | $16.19 | $16.19 |
| WarpBuild BYOC fee | $29.86 | $29.86 | $29.86 |
| Total | $46.05 | $145.79 | $307.79 |
EC2 and EBS charges for the runner instances are billed by your cloud account and sit outside this table, because instance choice varies by workload.
What the model says
Scoping static IPs to the jobs that need them saves $162.00 a month in this shape, all of it data processing on gigabytes that never needed a fixed address. The saving scales with fleet size and image size, since the per-GB line is the only one that grows.
The fixed $98.55 is the price of admission. Three gateways cost the same whether they carry 26 GB or 3,600 GB, so the decision is binary at the stack level and granular at the runner level.
Two details from the BYOC documentation belong in the same calculation. Data processing is charged in both directions, so a job that pulls 2 GB of packages and uploads 20 MB pays on the full 2 GB. Transfer between private subnets and gateways in different regions adds its own charge, so keep the subnets and the gateways inside the stack's region. The documentation states the conclusion plainly: static IPs should be used minimally.
Where WarpBuild fits
Every cost figure on this page carries a source link and a checked-on date, and cloud list prices change. Re-check the two AWS pricing links before quoting these totals internally, and re-run the arithmetic against your own job counts and image sizes.
On the enterprise tier, egress costs from your cloud drop to zero when runners pull large artifacts from ECR, S3, and similar stores during deployments, on BYOC and on WarpBuild-hosted runners alike; the scope is on the zero egress page.
The BYOC capacity and quotas guide covers the fleet-wide version of the numbers above, and hosted runners compared with BYOC runners covers the placement decision itself. For the network design around the stack, see GitHub Actions runners inside your own VPC.
FAQ
Can WarpBuild Cloud runners have a static egress IP?
No. Static egress IPs are a BYOC capability, available when runners run in your own AWS, GCP, or Azure account. WarpBuild Cloud runners egress from shared addresses, and no runner label or configuration flag changes that.
How many addresses does a partner have to allowlist?
Three per stack. The addresses belong to the NAT gateways, one per availability zone, so the allowlist holds three entries no matter how many jobs run. The elastic IP quota is separate: 3 plus one address per concurrently running job.
What do static IPs cost?
They move runners into private subnets, which adds charges on your own cloud bill. In US East (N. Virginia) AWS charges $0.045 per hour per gateway plus $0.045 per GB of data processed in both directions, so three gateways cost $98.55 a month before the first gigabyte moves. Amazon VPC pricing, checked on 2026-08-13.
Should every runner use static IPs?
No. Keep one custom runner with static IPs for the jobs that hit an allowlisted endpoint and a second runner without them for everything else. The second runner lands in public subnets and skips the per-GB data processing charge entirely.
Start with $10 in free credits
Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.