Single-App Architecture - CRITICAL DEPLOYMENT GUIDE
**Date Unified:** 2026-02-24
**Commit:** 50958d329ec7329b68470cf6288ee92b3e5f2788
**Author:** Rushi Parikh
---
⚠️ CRITICAL: WE ARE A SINGLE-APP DEPLOYMENT
**atom-saas-api.fly.dev DOES NOT EXIST AND MUST NEVER BE REFERENCED**
---
Why This Change Was Made
Root Cause: HTTP 431 Errors
**Problem:** Requests to atom-saas-api.fly.dev were failing with HTTP 431 (Request Header Fields Too Large)
**Technical Root Cause:**
- Each proxy hop adds a
Viaheader - Fly.io proxy architecture accumulated 50+ hops
- Request headers exceeded the 64KB limit
- Example header chain:
via: 1.1 fly.io, 1.1 fly.io, 1.1 fly.io...(50+ times)
**Why This Happened:**
- Dual-app setup:
atom-saas(Next.js) →atom-saas-api(Python FastAPI) - Every API request went through multiple proxy layers
- Fly.io's internal routing added hops with each proxy
Solution: Unified Single-App Architecture
**What Changed:**
- **Single Container**: Both Next.js (port 3000) and Python FastAPI (port 8000) run in SAME container
- **Internal Communication**:
PYTHON_BACKEND_URL = "http://localhost:8000"(localhost, not external URL) - **Removed**:
atom-saas-api.fly.devapp completely deleted - **Memory Upgrade**: 1GB → 2GB per process to handle both services
**Process Management:**
supervisordmanages both processesdocker-entrypoint.shstarts both whenROLE=web- Configuration in
supervisord.conf
---
DEPLOYMENT CONFIGURATION
fly.toml
[build.args]
PYTHON_BACKEND_URL = "http://localhost:8000" # ✅ CORRECT
[env]
PYTHON_BACKEND_URL = "http://localhost:8000" # ✅ CORRECT
ROLE = 'web' # Starts both Next.js + Python
[processes]
app = "docker-entrypoint.sh" # ✅ CORRECT (Triggers entrypoint path)
# ❌ WRONG: app = "web" (Fails with "No such file or directory").dockerignore (🔴 CRITICAL for context size)
**Problem:** Including .next/cache or **/venv/ can bloat the build context to >2GB, causing slow deployments and timeouts.
**Solution:** Always exclude these in .dockerignore:
.next/cache/
**/venv/
**/htmlcov*/
**/coverage*/next.config.mjs
// ✅ CORRECT - Use localhost at BUILD time
const pythonBackendUrl = process.env.PYTHON_BACKEND_URL || 'http://localhost:8000';
// ❌ WRONG - Never use this old URL
// const pythonBackendUrl = 'https://atom-saas-api.fly.dev'; // DELETED APPDockerfile
# Both services run in same container
CMD ["./docker-entrypoint.sh", "web"]supervisord.conf
[program:nextjs]
command=node server.js
environment=PORT=3000
[program:python]
command=python3 -m uvicorn main_api_app:app --host 0.0.0.0 --port 8000---
NEVER DO THIS ❌
// ❌ WRONG - This app doesn't exist
const pythonBackendUrl = 'https://atom-saas-api.fly.dev';
// ❌ WRONG - Old proxy URL
rewrite: [{ source: '/api/v1/:path*', destination: 'https://atom-saas-api.fly.dev/api/v1/:path*' }]
// ❌ WRONG - Deploying to wrong app
fly deploy -a atom-saas-api # This app doesn't exist
// ❌ WRONG - Setting environment variable to external URL
PYTHON_BACKEND_URL = "https://atom-saas-api.fly.dev"---
ALWAYS DO THIS ✅
// ✅ CORRECT - Use localhost for internal communication
const pythonBackendUrl = process.env.PYTHON_BACKEND_URL || 'http://localhost:8000';
// ✅ CORRECT - Rewrite to internal backend
rewrite: [{ source: '/api/v1/:path*', destination: 'http://localhost:8000/api/v1/:path*' }]
// ✅ CORRECT - Deploy to the single app
fly deploy -a atom-saas
// ✅ CORRECT - Set to localhost
PYTHON_BACKEND_URL = "http://localhost:8000"---
ARCHITECTURE DIAGRAM
Before (Dual-App - BROKEN)
User Request
↓
atom-saas.fly.dev (Next.js :3000)
↓ (proxy via internet)
atom-saas-api.fly.dev (Python :8000) ← 50+ proxy hops
↓
HTTP 431 Error (headers too large)After (Single-App - WORKING)
User Request
↓
atom-saas.fly.dev (Container)
├── Next.js :3000 (supervisord)
└── Python :8000 (supervisord)
↓
localhost:8000 (internal - no proxy hops)
↓
Success ✅---
VERIFICATION CHECKLIST
When making changes, verify:
- [ ] No references to
atom-saas-api.fly.devin code - [ ]
PYTHON_BACKEND_URLuseshttp://localhost:8000(both build.args and [env]) - [ ]
fly.tomlhasROLE = 'web'for dual-process mode - [ ]
fly.tomlhasapp = "docker-entrypoint.sh"in[processes] - [ ]
.dockerignoreexcludes.next/cacheandvenv(context < 200MB) - [ ]
supervisord.confmanages both Next.js and Python - [ ]
next.config.mjsfallback ishttp://localhost:8000 - [ ] Deploying to
atom-saasapp (NOTatom-saas-api)
---
FILES TO CHECK
Always check these files for atom-saas-api references:
next.config.mjs- Build-time rewritesfly.toml- Build args and environment variablessrc/middleware.ts- Request routing- Any API route files - Backend URL configuration
.envfiles - Local development configuration- Documentation files - Outdated references
---
RELATED COMMITS
- **50958d32** - Initial unification (2026-02-24)
- **58dcc670** - Removed remaining atom-saas-api references (2026-04-07)
- **00d399aa** - Fixed next.config.mjs fallback (2026-04-07)
---
WHATSAPP OAUTH CONFIGURATION
Since you asked about WhatsApp OAuth:
Webhook URL (for Meta/Facebook dashboard)
https://app.atomagentos.com/api/v1/webhooks/whatsappVerify Token
Set in Meta dashboard → WHATSAPP_WEBHOOK_VERIFY_TOKEN environment variable
Default: atom_whatsapp_verify_token_2024Redirect URI (for OAuth flow)
https://app.atomagentos.com/api/integrations/whatsapp/callbackBoth routes work because:
- Next.js receives request on port 3000
- Rewrites to
http://localhost:8000(internal) - Python FastAPI handles webhook/OAuth callback
- No external proxy hops = no HTTP 431 errors
---
TROUBLESHOOTING
If you see "atom-saas-api.fly.dev" anywhere:
- **STOP** - Don't deploy
- **Search**:
grep -r "atom-saas-api" . - **Fix**: Replace with
http://localhost:8000or remove entirely - **Verify**: Check this document for correct pattern
If API requests fail with HTTP 431:
- Check
PYTHON_BACKEND_URLishttp://localhost:8000 - Check no references to external proxy URLs
- Verify deployment is to
atom-saasapp only
If webhooks don't work:
- Verify Next.js rewrites are correct
- Check Python backend is running (
fly ssh console -c "curl http://localhost:8000/health") - Test webhook endpoint directly via SSH
---
SUMMARY
**Single App = atom-saas**
- Next.js :3000 + Python :8000 in same container
- Internal communication via localhost
- No external proxy hops
- No HTTP 431 errors
**NEVER AGAIN:**
atom-saas-api.fly.dev(DELETED)- External proxy URLs for internal communication
- Dual-app deployment
**ALWAYS:**
http://localhost:8000for PYTHON_BACKEND_URL- Single app deployment to
atom-saas - Internal communication via localhost
---
Last Updated: 2026-04-13
Commit: [CURRENT_FIX]