QuotaGuard and Fly.io Integration Guide

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

  1. Verify your Fly.io app can reach external networks
  2. Check that the QuotaGuard proxy hostname is correct
  3. Ensure no firewall rules block outbound connections to port 9293

Wrong IP Address Returned

The proxy URL may not be configured correctly:

  1. Verify the environment variable is being read in your code
  2. Check that you’re applying the proxy to the specific request, not just setting an environment variable
  3. 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

  1. Verify bin/qgtunnel is included in your Docker image and is executable
  2. Check that QUOTAGUARDSTATIC_URL is set as a secret
  3. 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:

  1. Ensure your HTTP client is using the proxy for the specific request
  2. Check that the destination service supports IPv4 (most do)
  3. 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.

Try QuotaGuard Now

View Fly.io Integration Features

Read: How Do I Get a Static IP for Fly.io Apps?


Ready to Get Started?

Get in touch or create a free trial account

Back to top ↑

Copyright © 2009 - 2026 QuotaGuard. All rights reserved.