Network graph
End-to-end traffic topology for production. All Railway services communicate over Railway’s private network except where shown as public HTTPS.
graph TD
subgraph Users
CU[Customer]
BO[Business owner]
ADM[Admin / ops]
end
subgraph CF["Cloudflare (DNS + proxy)"]
CF_DNS[innerlight.dev zone]
end
subgraph Railway["Railway (single production environment)"]
subgraph Public["Public-facing services"]
SA_PUB["scheduler-api :4000\nmy.stage.innerlight.dev"]
PBW_PUB["portal-business-web\nCaddy :80\nadmin.stage.innerlight.dev"]
end
subgraph Internal["Internal services (private network)"]
PB["portal-business\nFastify BFF :3043"]
PORT["portal\nTanStack Start :3030"]
VS["vendure-server\nGraphQL :3001"]
D["directus\nCMS :8055"]
SW["scheduler-worker\nno HTTP"]
VW["vendure-worker\nno HTTP"]
DOCS["docs\nCaddy static :4330"]
end
subgraph Temporal["Temporal cluster (Railway private)"]
TS["temporal-server\ntemporal-server.railway.internal:7233"]
TUI["temporal-ui :8080"]
TPDB[(temporal-postgres)]
end
end
subgraph Managed["Managed services"]
NS[(Neon\nscheduler DB)]
NV[(Neon\nvendure DB)]
ND[(Neon\ndirectus DB)]
RD[(Redis\nBullMQ + sessions)]
R2[(Cloudflare R2\nassets)]
end
subgraph Ext["External APIs"]
WOS[WorkOS]
STR[Stripe]
PBK[Porkbun]
Resend[Resend\nemail]
GCAL[Google Calendar]
MSCAL[Microsoft 365]
end
%% User → Cloudflare
CU -->|HTTPS| CF_DNS
BO -->|HTTPS admin.stage.innerlight.dev| CF_DNS
ADM -->|HTTPS| CF_DNS
%% Cloudflare → Railway
CF_DNS -->|CNAME| SA_PUB
CF_DNS -->|CNAME| PBW_PUB
CF_DNS -->|CNAME docs.stage.innerlight.dev| DOCS
%% Portal-business-web (Caddy)
PBW_PUB -->|"/api/* reverse_proxy"| PB
PBW_PUB -->|"SPA HTML (try_files)"| BO
%% BFF → scheduler-api
PB -->|REST /api/admin/*| SA_PUB
%% Admin portal
ADM --> PORT
PORT -->|tRPC /trpc/*| SA_PUB
%% scheduler-api internal
SA_PUB -->|Drizzle ORM| NS
SA_PUB -->|BullMQ jobs| RD
SA_PUB -->|gRPC workflows| TS
SA_PUB -->|GraphQL| VS
SA_PUB -->|REST API| D
SA_PUB -->|JWT validation| WOS
SA_PUB -->|webhooks + API| STR
SA_PUB -->|domain registration| PBK
SA_PUB -->|email jobs → worker| SW
%% scheduler-worker
SW -->|dequeue| RD
SW -->|send| Resend
SW -->|calendar sync| GCAL
SW -->|calendar sync| MSCAL
%% Temporal
TS --- TPDB
TS -->|activity callbacks| SA_PUB
%% Vendure
VS -->|TypeORM| NV
VS -->|BullMQ| RD
VS -->|S3 assets| R2
VS -->|payments| STR
VW -->|dequeue| RD
%% Directus
D -->|SQL| ND
D -->|S3 assets| R2
D -->|OIDC| WOS
Port reference
Section titled “Port reference”| Service | Internal port | Public hostname (stage) |
|---|---|---|
| scheduler-api | 4000 | my.stage.innerlight.dev |
| portal-business-web | 80 (Caddy) | admin.stage.innerlight.dev |
| portal-business | 3043 | — (Railway internal) |
| portal | 3030 | — (Railway internal) |
| vendure-server | 3001 | — (Railway internal) |
| directus | 8055 | — (Railway internal) |
| temporal-server | 7233 | — (temporal-server.railway.internal) |
| temporal-ui | 8080 | — (Railway internal) |
| docs | 4330 | docs.stage.innerlight.dev |
Local dev differences
Section titled “Local dev differences”In development, all services run on localhost and talk to Docker-hosted
infrastructure:
| Infra | Local | Production |
|---|---|---|
| PostgreSQL | Docker (3 instances: 5432/5433/5434) | Neon (3 separate projects) |
| Redis | Docker :6379 | Managed Redis (Railway) |
| Object storage | MinIO Docker :9000 | Cloudflare R2 |
| Temporal | — (usually skipped locally) | Railway private network |
See Local setup for the full bootstrap sequence.