| name | cliproxyapi-cloudflare-stack |
| description | Self-hosted OpenAI-compatible LLM API gateway with multi-credential pooling, Cloudflare email workers, and tunnel exposure |
| triggers | ["set up a self-hosted OpenAI API gateway","deploy CLIProxyAPI with Cloudflare tunnel","configure multi-credential LLM proxy","create automated email credential system","expose internal API gateway with cloudflared","manage multiple LLM credentials with failover","build OpenAI-compatible proxy on Cloudflare","set up Grok API credential rotation"] |
CLIProxyAPI Cloudflare Stack
Skill by ara.so — Devtools Skills collection.
What It Does
This project provides a complete self-hosted stack for running an OpenAI-compatible LLM API gateway with:
- CLIProxyAPI: Multi-credential pooling with hot-reload and automatic failover
- Cloudflare Worker + D1: Programmatic email receiving for credential verification
- Cloudflare Tunnel: Zero-port-forwarding HTTPS exposure of internal gateway
- Unified Interface: Single
/v1/* endpoint compatible with OpenAI SDK
Use case: Run your own LLM API proxy without sharing credentials with third-party services, supporting multiple provider accounts with automatic rotation.
Architecture Overview
[Client]
↓ HTTPS
[Cloudflare Tunnel] → public domain
↓
[CLIProxyAPI Gateway] → localhost:5000
↓
[Multiple LLM Providers] (Grok, OpenAI, etc.)
[Email] → Cloudflare Email Routing → Worker → D1 Database
Installation
Prerequisites
- Cloudflare account with domain
- VM or server with Docker (for CLIProxyAPI)
- Node.js 18+ (for Wrangler CLI)
cloudflared binary
1. Deploy CLIProxyAPI Gateway
git clone https://github.com/xsser/cliproxyapi-cloudflare-stack.git
cd cliproxyapi-cloudflare-stack
bash scripts/deploy_vm.sh
Expected output:
✓ CLIProxyAPI deployed
✓ API Key: sk-1234567890abcdef
✓ Auth directory: /opt/cpa/auth
✓ Service running on localhost:5000
Save the sk- key — this is your gateway authentication token.
2. Set Up Email Worker
npm install -g wrangler
wrangler login
cp templates/wrangler.toml.template wrangler.toml
nano wrangler.toml
wrangler.toml configuration:
name = "email-receiver"
main = "src/index.js"
compatibility_date = "2024-01-01"
[[d1_databases]]
binding = "DB"
database_name = "email_codes"
database_id = "YOUR_D1_DATABASE_ID"
[vars]
ALLOWED_DOMAINS = "x.ai,openai.com"
Create D1 database:
wrangler d1 create email_codes
wrangler d1 execute email_codes --file=src/schema.sql
Deploy worker:
wrangler deploy
Configure Cloudflare Email Routing:
- Go to Cloudflare Dashboard → Email Routing
- Add catch-all route:
*@yourdomain.com → Worker email-receiver
- Verify domain email routing is enabled
3. Add Credentials
Credentials are JSON files in the auth directory with provider-specific format.
Example Grok credential (grok_account1.json):
{
"provider": "grok",
"api_key": "grok-xxxxxxxxxxxxx",
"email": "your-email@domain.com",
"status": "active",
"rate_limit": {
"requests_per_minute": 60
}
}
Example OpenAI credential (openai_account1.json):
{
"provider": "openai",
"api_key": "sk-proj-xxxxxxxxxxxxx",
"organization_id": "org-xxxxxxxxxxxxx",
"status": "active"
}
Sync credentials to VM:
SRC=./auth_local VM_HOST=user@yourvm.com bash scripts/sync_credentials.sh
scp credentials/*.json user@yourvm:/opt/cpa/auth/
CLIProxyAPI hot-reloads credentials automatically (checks every 30s).
4. Expose with Cloudflare Tunnel
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb
cloudflared tunnel login
cloudflared tunnel create cpa-gateway
sudo cp templates/cloudflared-cpa.service.template /etc/systemd/system/cloudflared-cpa.service
sudo nano /etc/systemd/system/cloudflared-cpa.service
cloudflared-cpa.service configuration:
[Unit]
Description=Cloudflare Tunnel for CPA Gateway
After=network.target
[Service]
Type=simple
User=cloudflared
ExecStart=/usr/local/bin/cloudflared tunnel --no-autoupdate run --token YOUR_TUNNEL_TOKEN
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Create tunnel config:
mkdir -p ~/.cloudflared
nano ~/.cloudflared/config.yml
config.yml:
tunnel: YOUR_TUNNEL_ID
credentials-file: /home/user/.cloudflared/YOUR_TUNNEL_ID.json
ingress:
- hostname: api.yourdomain.com
service: http://localhost:5000
- service: http_status:404
Start tunnel:
sudo systemctl daemon-reload
sudo systemctl enable cloudflared-cpa
sudo systemctl start cloudflared-cpa
sudo systemctl status cloudflared-cpa
Add DNS record in Cloudflare:
Type: CNAME
Name: api
Target: YOUR_TUNNEL_ID.cfargotunnel.com
Proxy: Yes (orange cloud)
Usage
API Endpoint
Your gateway is now available at: https://api.yourdomain.com/v1
Using with OpenAI SDK
Python:
from openai import OpenAI
client = OpenAI(
base_url="https://api.yourdomain.com/v1",
api_key=os.environ.get("CPA_API_KEY")
)
response = client.chat.completions.create(
model="grok-beta",
messages=[
{"role": "user", "content": "Hello, Grok!"}
]
)
print(response.choices[0].message.content)
JavaScript/TypeScript:
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://api.yourdomain.com/v1',
apiKey: process.env.CPA_API_KEY
});
const response = await client.chat.completions.create({
model: 'grok-beta',
messages: [
{ role: 'user', content: 'Hello, Grok!' }
]
});
console.log(response.choices[0].message.content);
cURL:
curl https://api.yourdomain.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $CPA_API_KEY" \
-d '{
"model": "grok-beta",
"messages": [{"role": "user", "content": "Hello!"}]
}'
Key Commands
CLIProxyAPI Management
docker logs -f cpa-gateway
docker restart cpa-gateway
docker pull cliproxyapi/cliproxyapi:latest
docker restart cpa-gateway
curl http://localhost:5000/admin/credentials \
-H "Authorization: Bearer $CPA_API_KEY"
curl http://localhost:5000/health
Credential Sync Script
SRC=./credentials VM_HOST=user@vm.com bash scripts/sync_credentials.sh
DRY_RUN=1 SRC=./credentials VM_HOST=user@vm.com bash scripts/sync_credentials.sh
SRC=./credentials VM_HOST=user@vm.com AUTH_DIR=/custom/path bash scripts/sync_credentials.sh
Email Worker Queries
wrangler d1 execute email_codes --command="SELECT * FROM verification_codes ORDER BY received_at DESC LIMIT 10"
wrangler d1 execute email_codes --command="DELETE FROM verification_codes WHERE received_at < datetime('now', '-1 hour')"
wrangler tail
Tunnel Management
sudo systemctl status cloudflared-cpa
sudo journalctl -u cloudflared-cpa -f
sudo systemctl restart cloudflared-cpa
cloudflared tunnel list
cloudflared tunnel delete cpa-gateway
Configuration
CLIProxyAPI Environment Variables
CPA_PORT=5000
CPA_AUTH_DIR=/opt/cpa/auth
CPA_LOG_LEVEL=info
CPA_POOL_SIZE=10
CPA_RETRY_ATTEMPTS=3
CPA_HEALTH_CHECK_INTERVAL=60
Worker Environment Variables
Edit wrangler.toml:
[vars]
ALLOWED_DOMAINS = "x.ai,openai.com,anthropic.com"
MAX_CODE_AGE_MINUTES = 10
AUTO_CLEANUP_ENABLED = "true"
Rate Limiting
Configure per-credential in JSON:
{
"provider": "grok",
"api_key": "...",
"rate_limit": {
"requests_per_minute": 60,
"tokens_per_minute": 100000,
"requests_per_day": 5000
}
}
Common Patterns
Multi-Provider Setup
/opt/cpa/auth/
├── grok_account1.json
├── grok_account2.json
├── openai_personal.json
├── openai_work.json
└── anthropic_main.json
Each credential is automatically pooled. CLIProxyAPI routes based on model name:
grok-* → Grok credentials
gpt-* → OpenAI credentials
claude-* → Anthropic credentials
Automatic Failover
client = OpenAI(
base_url="https://api.yourdomain.com/v1",
api_key=os.environ.get("CPA_API_KEY")
)
response = client.chat.completions.create(
model="grok-beta",
messages=[{"role": "user", "content": "Test"}]
)
Streaming Responses
stream = client.chat.completions.create(
model="grok-beta",
messages=[{"role": "user", "content": "Write a story"}],
stream=True
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end='')
Monitoring Credential Usage
docker logs cpa-gateway 2>&1 | grep "Using credential"
Troubleshooting
Gateway Returns 502 Bad Gateway
Cause: CLIProxyAPI container not running or no active credentials.
Fix:
docker ps | grep cpa-gateway
docker logs cpa-gateway
ls -la /opt/cpa/auth/
docker restart cpa-gateway
"No active credentials for provider" Error
Cause: No valid credential JSON files for requested model's provider.
Fix:
cat /opt/cpa/auth/grok_account1.json
docker restart cpa-gateway
Email Worker Not Receiving Codes
Cause: Email routing not configured or D1 binding missing.
Fix:
echo "test" | mail -s "Test" test@yourdomain.com
wrangler tail
wrangler d1 info email_codes
wrangler deploy
Tunnel Connection Issues
Cause: Tunnel service not running or incorrect credentials.
Fix:
sudo systemctl status cloudflared-cpa
curl http://localhost:5000/health
cat ~/.cloudflared/config.yml
nslookup api.yourdomain.com
sudo systemctl restart cloudflared-cpa
Rate Limit Exceeded
Cause: All credentials in pool exhausted rate limits.
Fix:
scp new_credential.json user@vm:/opt/cpa/auth/
nano /opt/cpa/auth/grok_account1.json
Invalid API Key Response
Cause: Using wrong API key or credential expired.
Fix:
echo $CPA_API_KEY
docker logs cpa-gateway | grep "auth failed"
nano /opt/cpa/auth/expired_credential.json
High Latency
Cause: VM resources constrained or network issues.
Fix:
htop
ping api.x.ai
ping api.openai.com
-e CPA_POOL_SIZE=20
bash scripts/deploy_vm.sh
Security Best Practices
- Never commit credentials: Keep
.json files in .gitignore
- Rotate gateway key: Regenerate
sk- key periodically
- Use environment variables: Store all secrets in env vars, not code
- Enable Cloudflare WAF: Add rate limiting and bot protection to tunnel domain
- Monitor usage: Set up alerts for abnormal API usage patterns
- Restrict tunnel access: Use Cloudflare Access for additional auth layer
Advanced: Custom Credential Providers
Create custom credential format for new providers:
{
"provider": "custom-llm",
"api_key": "...",
"endpoint": "https://api.custom-llm.com/v1",
"model_prefix": "custom-",
"status": "active",
"rate_limit": {
"requests_per_minute": 100
}
}
CLIProxyAPI will route any model starting with custom- to this credential's endpoint.