Deployment Topology & Proxy Conflict Resolution
smo1 intermediate 5 min read
ELI5
SMO1 lives on three different hosting platforms, like a band with members in three different cities. Cloudflare handles the front door (the edge worker), Railway runs the brain (the API), and Vercel runs the stage show (the websites). There is a tricky problem: the front door is also supposed to show the stage show, but if the doorman tries to fetch the show from the same address, he ends up talking to himself in a mirror. The fix is a secret back entrance called origin.smo1.io that bypasses the doorman entirely.
Technical Deep Dive
Production Deployment Map
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#e8f4f8', 'primaryTextColor': '#2d3748', 'primaryBorderColor': '#90cdf4', 'lineColor': '#718096', 'secondaryColor': '#f0fff4', 'tertiaryColor': '#fefcbf'}}}%%flowchart TB subgraph DNS["DNS / Cloudflare"] CF[Cloudflare Proxy<br/>smo1.io] end subgraph Edge["Edge Layer"] Z[zoomies-edge<br/>Cloudflare Worker] end subgraph API["API Layer"] P[purr-api<br/>Railway] end subgraph Web["Web Layer"] W[whiskers-landing<br/>Vercel : origin.smo1.io] M[meow-web<br/>Vercel : app.smo1.io] end
U[User] -->|smo1.io/*| CF CF -->|slug routes| Z Z -->|API calls| P Z -->|landing proxy| W U -->|app.smo1.io/*| M U -->|purr-api...railway.app| PPlatform Responsibilities
| Project | Platform | Production URL | Deploy Command |
|---|---|---|---|
zoomies-edge | Cloudflare Workers | smo1.io/* | wrangler deploy --env lion |
purr-api | Railway | purr-api-production.up.railway.app | Docker push / Railway CLI |
meow-web | Vercel | app.smo1.io | vercel --prod |
whiskers-landing | Vercel | origin.smo1.io | vercel --prod |
The Proxy Conflict
Problem: Cloudflare proxies smo1.io to the Worker. If the Worker tries to fetch("https://smo1.io/about"), it calls itself → infinite loop → 502 error.
Solution: Deploy whiskers-landing to origin.smo1.io with Cloudflare proxy disabled (grey cloud in DNS). The Worker fetches origin.smo1.io directly, bypassing Cloudflare.
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#e8f4f8', 'primaryTextColor': '#2d3748', 'primaryBorderColor': '#90cdf4', 'lineColor': '#718096', 'secondaryColor': '#f0fff4', 'tertiaryColor': '#fefcbf'}}}%%flowchart LR A[Worker fetches smo1.io] --> B[Cloudflare Proxy] B --> C[Worker] C --> B B --> D[Infinite loop ❌]
E[Worker fetches origin.smo1.io] --> F[DNS only<br/>no proxy] F --> G[Vercel] G --> H[Success ✅]DNS Configuration
| Record | Type | Target | Proxy Status |
|---|---|---|---|
smo1.io | A / CNAME | Cloudflare Worker | 🟠 Proxied |
origin.smo1.io | CNAME | cname.vercel-dns.com | ⚪ DNS only |
app.smo1.io | CNAME | cname.vercel-dns.com | 🟠 Proxied (ok, no Worker) |
airlock.devarno.cloud | CNAME | Airlock service | 🟠 Proxied |
Environment Configuration
zoomies-edge uses Wrangler environments to switch origins:
[env.development]LANDING_ORIGIN = "http://localhost:3001"PURR_API_URL = "http://localhost:8080"
[env.cat]LANDING_ORIGIN = "https://origin.smo1.io"PURR_API_URL = "https://purr.smo1.io"
[env.lion]LANDING_ORIGIN = "https://origin.smo1.io"PURR_API_URL = "https://purr-api-production.up.railway.app"route = { pattern = "smo1.io/*", zone_name = "smo1.io" }SSL / TLS
- Cloudflare handles SSL termination for
smo1.ioandapp.smo1.io - Railway provides automatic TLS for
purr-api-production.up.railway.app - Vercel provides automatic TLS for
origin.smo1.ioandapp.smo1.io - Internal service-to-service calls (Worker → API) use HTTPS with certificate validation
Rollback Strategy
| Layer | Rollback method | Time to revert |
|---|---|---|
| Edge (Worker) | wrangler deploy --env lion with previous git commit | 30–60 seconds |
| API (Railway) | Railway dashboard → redeploy previous deployment | 1–2 minutes |
| Web (Vercel) | Vercel dashboard → promote previous deployment | 30–60 seconds |
Local Development (kitten)
All services run locally:
litter-box/docker-compose up -dstarts Postgres, Redis, ClickHouse, MinIO, Mailpitpurr-api:make runon:8080meow-web:pnpm devon:3000whiskers-landing:pnpm devon:3001zoomies-edge:pnpm dev(Wrangler local dev) on:8787
The local Worker proxies to localhost:3001 and localhost:8080 instead of production URLs.
Key Terms
- Proxy conflict → When a Cloudflare Worker tries to fetch its own proxied domain, causing an infinite loop
- Origin → The unproxied deployment of a site that the Worker can safely fetch from
- Grey cloud → Cloudflare DNS setting where proxying is disabled; only DNS resolution occurs
- Wrangler → Cloudflare’s CLI tool for deploying and managing Workers
- SSL termination → Decrypting HTTPS traffic at the edge so backend services receive plaintext HTTP
Q&A
Q: Why not put everything on one platform? A: Each platform excels at different things. Cloudflare Workers are fastest for edge routing. Railway is simplest for Go backend hosting. Vercel is optimal for Next.js frontend deployment. Using best-of-breed services for each layer maximises performance and developer experience.
Q: What happens if origin.smo1.io is accidentally proxied? A: The Worker would enter an infinite loop for all non-slug requests, causing 502 errors across the entire site. The fix is to toggle the DNS record back to grey cloud (DNS only) in the Cloudflare dashboard.
Q: Can users access origin.smo1.io directly? A: Technically yes, but there is no reason to. It serves the same content as smo1.io for non-slug paths. Direct access bypasses the edge worker’s bot detection and analytics, so it is not encouraged.
Q: How does the Worker know which environment it is running in?
A: Wrangler injects environment variables based on the --env flag. The Worker reads LANDING_ORIGIN and PURR_API_URL from its bindings at runtime.
Examples
Think of the deployment topology like a restaurant chain:
- Cloudflare is the franchise headquarters that handles all customer-facing branding and routes calls to the right location
- Railway is the central kitchen that prepares all the food (API responses)
- Vercel is the local dining room where customers sit (websites)
- The proxy conflict is the headquarters phone system routing a call back to itself when someone asks for “the manager” — the fix is a direct internal line (origin.smo1.io) that bypasses the switchboard
- Wrangler environments are the different menus for breakfast, lunch, and dinner — same kitchen, different configurations
neighbors on the map
- Request Routing & Edge Resolution debugging why a slug returns 404 instead of redirecting
- Edge Redirect Flow debugging why a slug does not redirect