QuotaGuard and Fly.io Integration Guide
Table of contents
- QuotaGuard and Fly.io Integration Guide
- Why Fly.io Apps Need Static IPs
- Native Fly.io Static Egress vs QuotaGuard
- Getting Started
- Configuring Your Application
- Database Connections (SOCKS5)
- fly.toml Configuration
- Testing Your Implementation
- Latency Considerations
- Multi-Region Deployments
- Troubleshooting
- QuotaGuard Static vs QuotaGuard Shield
- Ready to Get Started?
QuotaGuard and Fly.io Integration Guide
QuotaGuard Static IPs allow your Fly.io applications to send outbound traffic through a load-balanced pair of static IP addresses. Once set up, you can use QuotaGuard’s IPs to connect to firewalled databases and APIs that require IP allowlisting.
You do not need QuotaGuard for internal Fly.io private networking. Fly.io’s 6PN (IPv6 private network) handles communication between your apps within the same organization. QuotaGuard is for connecting to external services that require a known, static source IP address.
Why Fly.io Apps Need Static IPs
Fly.io uses an Anycast network designed for global distribution. Machines are ephemeral and scale automatically. This architecture means outbound traffic originates from shared IP pools that change without notice.
Fly.io’s own documentation states they discourage using outbound IPs to bypass firewalls because a machine’s outbound IP is liable to change.
Common scenarios where you need a static IP:
- MongoDB Atlas: Requires IP allowlisting in Network Access settings
- Supabase/Neon: Database connection restrictions
- Amazon RDS: Security groups with IP-based rules
- Payment Gateways: Banking APIs that only accept requests from registered IPs
- Partner APIs: Corporate firewalls that require known source addresses
- Legacy Systems: On-premise databases and ERPs with strict firewall policies
Native Fly.io Static Egress vs QuotaGuard
Fly.io launched app-scoped static egress IPs in late 2024. Here’s how the two options compare:
| Feature | Fly.io Native Egress | QuotaGuard |
|---|---|---|
| Pricing | $3.60/month per IPv4 per region | Starting at $19/month |
| Multi-region | Requires separate IP per region | One proxy serves all regions |
| Machine limit | 64 machines per IP | No machine limit |
| Protocol support | HTTP/HTTPS (TCP requires extra work) | HTTP/HTTPS + SOCKS5 for databases |
| Database connections | Requires additional configuration | Built-in SOCKS5 support |
| HIPAA/PCI compliance | No compliance features | Shield product for regulated data |
| Reliability | Some intermittent issues reported | Load-balanced with failover |
Use Fly.io Native Egress if you have a single-region deployment, only need HTTP/HTTPS, have fewer than 64 machines, and budget is the primary concern.
Use QuotaGuard if you need multi-region support with a single IP identity, database connections (MongoDB, PostgreSQL), HIPAA/PCI compliance, or production reliability with dedicated support.
Getting Started
After creating a QuotaGuard account, you will be redirected to your dashboard where you can find your proxy credentials and static IP addresses.
Choose the right proxy region: Match your QuotaGuard region to your primary Fly.io deployment region to minimize latency.
| Fly.io Region | Location | Recommended QuotaGuard Region |
|---|---|---|
| iad | Ashburn, Virginia | US-East |
| lax | Los Angeles, California | US-West |
| sea | Seattle, Washington | US-West |
| yyz | Toronto, Canada | Canada (Montreal) |
| lhr | London, UK | EU-West (Ireland) |
| fra | Frankfurt, Germany | EU-Central (Frankfurt) |
| ams | Amsterdam, Netherlands | EU-West (Ireland) |
| nrt | Tokyo, Japan | AP-Northeast (Tokyo) |
| sin | Singapore | AP-Southeast (Singapore) |
| syd | Sydney, Australia | Australia (Sydney) |
To check your Fly.io app’s region, run:
fly status -a your-app-name
Look for the REGION column in the Machines list.
Configuring Your Application
Step 1: Add Your Proxy URL as a Secret
Store your QuotaGuard credentials securely using Fly.io secrets:
fly secrets set QUOTAGUARDSTATIC_URL="http://username:password@us-east-static-01.quotaguard.com:9293" -a your-app-name
Fly.io secrets are encrypted and only exposed to your application instances at runtime. This keeps your credentials out of your Git repository.
To verify the secret was set:
fly secrets list -a your-app-name
You should see QUOTAGUARDSTATIC_URL in the list (the value is hidden).
Step 2: Configure Your HTTP Client
Python (requests)
import os
import requests
proxy_url = os.environ.get('QUOTAGUARDSTATIC_URL')
proxies = {
'http': proxy_url,
'https': proxy_url
}
response = requests.get('https://api.example.com/data', proxies=proxies)
print(response.json())
Python (httpx)
import os
import httpx
proxy_url = os.environ.get('QUOTAGUARDSTATIC_URL')
with httpx.Client(proxy=proxy_url) as client:
response = client.get('https://api.example.com/data')
print(response.json())
Node.js (axios with https-proxy-agent)
const axios = require('axios');
const { HttpsProxyAgent } = require('https-proxy-agent');
const proxyUrl = process.env.QUOTAGUARDSTATIC_URL;
const agent = new HttpsProxyAgent(proxyUrl);
axios.get('https://api.example.com/data', { httpsAgent: agent })
.then(response => console.log(response.data))
.catch(error => console.error(error));
Install the proxy agent: npm install https-proxy-agent
Node.js (fetch with undici)
import { ProxyAgent, fetch } from 'undici';
const proxyUrl = process.env.QUOTAGUARDSTATIC_URL;
const dispatcher = new ProxyAgent(proxyUrl);
const response = await fetch('https://api.example.com/data', { dispatcher });
const data = await response.json();
console.log(data);
Go
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
)
func main() {
proxyURL, err := url.Parse(os.Getenv("QUOTAGUARDSTATIC_URL"))
if err != nil {
panic(err)
}
client := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyURL),
},
}
resp, err := client.Get("https://api.example.com/data")
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
Ruby (Net::HTTP)
require 'net/http'
require 'uri'
proxy_uri = URI.parse(ENV['QUOTAGUARDSTATIC_URL'])
http = Net::HTTP.new(
'api.example.com',
443,
proxy_uri.host,
proxy_uri.port,
proxy_uri.user,
proxy_uri.password
)
http.use_ssl = true
response = http.get('/data')
puts response.body
Ruby (Faraday)
require 'faraday'
conn = Faraday.new(url: 'https://api.example.com') do |f|
f.proxy = ENV['QUOTAGUARDSTATIC_URL']
f.adapter Faraday.default_adapter
end
response = conn.get('/data')
puts response.body
Elixir (HTTPoison)
proxy_url = System.get_env("QUOTAGUARDSTATIC_URL")
uri = URI.parse(proxy_url)
[username, password] = String.split(uri.userinfo, ":")
options = [
proxy: {String.to_charlist(uri.host), uri.port},
proxy_auth: {String.to_charlist(username), String.to_charlist(password)}
]
{:ok, response} = HTTPoison.get("https://api.example.com/data", [], options)
IO.inspect(response.body)
Elixir (Req)
proxy_url = System.get_env("QUOTAGUARDSTATIC_URL")
Req.get!("https://api.example.com/data",
connect_options: [proxy: proxy_url]
)
Elixir (Finch)
# In your application supervision tree
proxy_url = System.get_env("QUOTAGUARDSTATIC_URL")
uri = URI.parse(proxy_url)
[username, password] = String.split(uri.userinfo, ":")
Finch.start_link(
name: MyFinch,
pools: %{
:default => [
conn_opts: [
proxy: {:http, uri.host, uri.port, []},
proxy_headers: [
{"proxy-authorization", "Basic " <> Base.encode64("#{username}:#{password}")}
]
]
]
}
)
# Making a request
Finch.build(:get, "https://api.example.com/data")
|> Finch.request(MyFinch)
Rust (reqwest)
use reqwest::Proxy;
use std::env;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let proxy_url = env::var("QUOTAGUARDSTATIC_URL")?;
let proxy = Proxy::all(&proxy_url)?;
let client = reqwest::Client::builder()
.proxy(proxy)
.build()?;
let response = client.get("https://api.example.com/data")
.send()
.await?;
println!("{}", response.text().await?);
Ok(())
}
Add to Cargo.toml:
[dependencies]
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }
Database Connections (SOCKS5)
For non-HTTP protocols like PostgreSQL, MySQL, or MongoDB, use QuotaGuard’s SOCKS5 proxy on port 1080.
When to Use SOCKS5
Standard HTTP proxies only handle HTTP/HTTPS traffic. If your Fly.io application connects to:
- PostgreSQL
- MySQL
- MongoDB
- Redis
- SFTP
- Any raw TCP connection
You need SOCKS5 proxying.
Step 1: Add SOCKS5 Credentials as a Secret
Your SOCKS5 proxy URL is available in your QuotaGuard dashboard. Add it as a separate secret:
fly secrets set QUOTAGUARD_SOCKS_HOST="us-east-static-01.quotaguard.com" -a your-app-name
fly secrets set QUOTAGUARD_SOCKS_PORT="1080" -a your-app-name
fly secrets set QUOTAGUARD_SOCKS_USER="your-username" -a your-app-name
fly secrets set QUOTAGUARD_SOCKS_PASS="your-password" -a your-app-name
Step 2: Configure Your Database Client
Python (PostgreSQL with psycopg2 and PySocks)
import os
import socks
import socket
import psycopg2
# Configure SOCKS proxy
socks.set_default_proxy(
socks.SOCKS5,
os.environ.get('QUOTAGUARD_SOCKS_HOST'),
int(os.environ.get('QUOTAGUARD_SOCKS_PORT')),
username=os.environ.get('QUOTAGUARD_SOCKS_USER'),
password=os.environ.get('QUOTAGUARD_SOCKS_PASS')
)
socket.socket = socks.socksocket
# Connect to database
conn = psycopg2.connect(
host='your-database.example.com',
database='mydb',
user='dbuser',
password='dbpass'
)
Install dependencies: pip install PySocks psycopg2-binary
Node.js (MongoDB with socks)
const { MongoClient } = require('mongodb');
const SocksConnection = require('socksjs');
const proxyHost = process.env.QUOTAGUARD_SOCKS_HOST;
const proxyPort = parseInt(process.env.QUOTAGUARD_SOCKS_PORT);
const proxyUser = process.env.QUOTAGUARD_SOCKS_USER;
const proxyPass = process.env.QUOTAGUARD_SOCKS_PASS;
const client = new MongoClient('mongodb+srv://your-cluster.mongodb.net/', {
proxyHost: proxyHost,
proxyPort: proxyPort,
proxyUsername: proxyUser,
proxyPassword: proxyPass
});
await client.connect();
Using QGTunnel with Fly.io (Docker)
For applications deployed as Docker containers, you can include QGTunnel in your image to transparently proxy database connections.
Step 1: Download QGTunnel and add it to your project:
curl https://s3.amazonaws.com/quotaguard/qgtunnel-latest.tar.gz | tar xz
This creates bin/qgtunnel and vendor/nss_wrapper/.
Step 2: Modify your Dockerfile to include QGTunnel:
FROM node:18-slim
WORKDIR /app
# Copy application files
COPY package*.json ./
RUN npm install
COPY . .
# Copy QGTunnel
COPY bin/qgtunnel /app/bin/qgtunnel
COPY vendor /app/vendor
# Make qgtunnel executable
RUN chmod +x /app/bin/qgtunnel
# Use qgtunnel as entrypoint
ENTRYPOINT ["/app/bin/qgtunnel"]
CMD ["node", "server.js"]
Step 3: Configure your tunnel in the QuotaGuard dashboard:
Navigate to Settings > Setup > Tunnel > Create Tunnel.
Example PostgreSQL configuration:
| Setting | Value |
|---|---|
| Remote Destination | tcp://your-database.example.com:5432 |
| Local Port | 5432 |
| Transparent | true |
| Encrypted | false |
Transparent mode overrides DNS so your app can connect to the original hostname while traffic routes through the tunnel.
Step 4: Deploy with the QUOTAGUARDSTATIC_URL secret set. QGTunnel reads this automatically.
fly.toml Configuration
You can also set non-sensitive environment variables in your fly.toml file:
[env]
PROXY_REGION = "us-east"
# Do NOT put QUOTAGUARDSTATIC_URL here - use secrets for credentials
For the proxy URL, always use fly secrets set to keep credentials out of your repository.
Testing Your Implementation
Verify your static IP configuration by requesting ip.quotaguard.com:
From Your Local Machine
curl -x $QUOTAGUARDSTATIC_URL https://ip.quotaguard.com
From a Running Fly.io Machine
Use the Fly.io SSH console to test from inside your application:
fly ssh console -a your-app-name
Then from inside the machine:
curl -x $QUOTAGUARDSTATIC_URL https://ip.quotaguard.com
Expected response:
{"ip":"52.34.188.175"}
The returned IP should match one of your two static IPs shown in the QuotaGuard dashboard. Run it multiple times to see both IPs (load-balanced).
Application-Level Test
Add a test endpoint to your application:
# Python/Flask example
import os
import requests
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/test-proxy')
def test_proxy():
proxy_url = os.environ.get('QUOTAGUARDSTATIC_URL')
proxies = {'http': proxy_url, 'https': proxy_url}
response = requests.get('https://ip.quotaguard.com', proxies=proxies)
return jsonify({
'static_ip': response.json()['ip'],
'proxy_configured': True
})
Latency Considerations
Using QuotaGuard adds a network hop to your requests:
| Configuration | Added Latency |
|---|---|
| Same region (Fly.io iad + QuotaGuard US-East) | 10-20ms |
| Cross-region | 50-100ms |
Fly.io apps can run in multiple regions simultaneously. All instances will use the same QuotaGuard static IPs regardless of which edge region serves the request. This is an advantage over native egress, which requires separate IPs per region.
For latency-sensitive applications, select a QuotaGuard region that matches your primary Fly.io deployment region.
Multi-Region Deployments
If your Fly.io app runs in multiple regions (e.g., iad, fra, sin), QuotaGuard provides a significant advantage over native egress.
With Native Egress: You need a separate static IP for each region ($3.60/month × 3 regions = $10.80/month), and partners must allowlist all of them.
With QuotaGuard: All regions route through the same proxy. Partners allowlist just two IPs (your load-balanced pair). Machines in any region connect to the same QuotaGuard endpoint.
To minimize latency in multi-region setups, you can configure region-specific QuotaGuard endpoints using Fly.io’s PRIMARY_REGION environment variable:
import os
region = os.environ.get('FLY_REGION', 'iad')
# Map Fly.io regions to QuotaGuard endpoints
proxy_map = {
'iad': os.environ.get('QUOTAGUARD_US_EAST'),
'fra': os.environ.get('QUOTAGUARD_EU'),
'sin': os.environ.get('QUOTAGUARD_APAC'),
}
proxy_url = proxy_map.get(region, proxy_map['iad'])
Troubleshooting
407 Proxy Authentication Required
Your credentials are incorrect. Verify your QUOTAGUARDSTATIC_URL secret:
fly ssh console -a your-app-name
echo $QUOTAGUARDSTATIC_URL
Check that the username and password match your QuotaGuard dashboard.
Connection Timeout
- Verify your Fly.io app can reach external networks
- Check that the QuotaGuard proxy hostname is correct
- Ensure no firewall rules block outbound connections to port 9293
Wrong IP Address Returned
The proxy URL may not be configured correctly:
- Verify the environment variable is being read in your code
- Check that you’re applying the proxy to the specific request, not just setting an environment variable
- Some HTTP clients require explicit proxy configuration per-request
Secrets Not Loading
Fly.io secrets are injected at runtime, not build time. If your application reads environment variables during the Docker build, they won’t be available.
Move configuration loading to runtime:
# Wrong - runs at import time
PROXY_URL = os.environ['QUOTAGUARDSTATIC_URL'] # May fail during build
# Right - runs at request time
def get_proxy():
return os.environ.get('QUOTAGUARDSTATIC_URL')
QGTunnel Not Starting
- Verify
bin/qgtunnelis included in your Docker image and is executable - Check that QUOTAGUARDSTATIC_URL is set as a secret
- Enable debug logging by setting
QGTUNNEL_DEBUG=true:
fly secrets set QGTUNNEL_DEBUG="true" -a your-app-name
Then check logs:
fly logs -a your-app-name | grep qgtunnel
IPv6 vs IPv4 Issues
Fly.io’s internal networking is primarily IPv6. Some applications may attempt IPv6 connections first. QuotaGuard provides dedicated static IPv4 addresses for allowlisting.
If you’re seeing IPv6 addresses instead of your static IPv4:
- Ensure your HTTP client is using the proxy for the specific request
- Check that the destination service supports IPv4 (most do)
- Verify the proxy URL uses the correct hostname format
QuotaGuard Static vs QuotaGuard Shield
QuotaGuard offers two products for static IPs:
| Feature | QuotaGuard Static | QuotaGuard Shield |
|---|---|---|
| Protocol | HTTP/SOCKS5 | HTTPS/SOCKS5 over TLS |
| Encryption | Standard proxy | SSL Passthrough (E2EE) |
| Best for | General API access | HIPAA, PCI-DSS, regulated data |
| Starting price | $19/month | $69/month |
For most Fly.io applications, QuotaGuard Static provides everything you need. Choose Shield if you’re handling protected health information (PHI), payment card data, or have specific compliance requirements.
Ready to Get Started?
Get in touch or create a free trial account.
View Fly.io Integration Features
Read: How Do I Get a Static IP for Fly.io Apps?