Skip to content

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
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

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.