This document details the architecture of the Trigger.dev web application, focusing on the Express + Remix server setup, middleware chain, route handling, server-side rendering, and the integration of real-time communication layers. This page covers the infrastructure and request flow of the web application itself.
For information about real-time updates and Server-Sent Events, see Realtime Updates and SSE. For Socket.IO and WebSocket specifics, see Socket.IO and WebSocket Communication. For authentication details, see Authentication and Authorization. For UI components and routing, see Navigation and Routing.
The web application is built on the following core technologies:
| Component | Technology | Version/Details |
|---|---|---|
| Web Framework | Remix | v2.1.0 |
| HTTP Server | Express | v4.20.0 |
| Runtime | Node.js | ≥18.19.0 or ≥20.6.0 |
| View Layer | React | v18.2.0 |
| Styling | TailwindCSS | v3.4.1 |
| Real-time (Socket.IO) | socket.io | v4.7.4 |
| Real-time (WebSocket) | ws | v8.11.0 |
| ORM | Prisma | v6.14.0 |
| Compression | compression | v1.7.4 |
| Logging | morgan | v1.10.0 |
Sources: apps/webapp/package.json1-286
The web application runs on an Express HTTP server that wraps the Remix application handler. The server is configured in apps/webapp/server.ts1-269 and provides the foundation for all HTTP and WebSocket traffic.
Server Creation and Port Binding:
REMIX_APP_PORT, PORT, or defaults to 3000 apps/webapp/server.ts117maxHeaderSize) apps/webapp/server.ts205Sources: apps/webapp/server.ts91-206
The server supports horizontal scaling via Node.js cluster mode for improved performance under load.
Cluster Configuration:
ENABLE_CLUSTER=1 environment variable apps/webapp/server.ts18WEB_CONCURRENCY, CLUSTER_WORKERS, or CPU count apps/webapp/server.ts19-21node webapp-server primary apps/webapp/server.ts74node webapp-worker-{id} or node webapp-server apps/webapp/server.ts109-111Worker Management:
GRACEFUL_SHUTDOWN_TIMEOUT (default 30s) apps/webapp/server.ts49Sources: apps/webapp/server.ts18-89
Security Configuration:
X-Powered-By header disabled apps/webapp/server.ts98Strict-Transport-Security header with 100-year max-age apps/webapp/server.ts128X-Robots-Tag set to "noindex, nofollow" for non-production domains apps/webapp/server.ts131-133Referrer-Policy, X-Content-Type-Options, Permissions-Policy apps/webapp/app/root.tsx23-28Static Asset Configuration:
/build/* assets cached for 1 year (immutable) apps/webapp/server.ts101/public/* assets cached for 1 hour apps/webapp/server.ts105DISABLE_COMPRESSION=1 apps/webapp/server.ts93-95Request ID Generation:
nanoid() apps/webapp/server.ts147runWithHttpContext() apps/webapp/server.ts149-152Sources: apps/webapp/server.ts93-143 apps/webapp/app/root.tsx23-28
The application has separate entry points for server-side and client-side rendering:
Server Entry (entry.server.tsx):
handleRequest() apps/webapp/app/entry.server.tsx23-68isbot library determines rendering strategy apps/webapp/app/entry.server.tsx49onAllReady) apps/webapp/app/entry.server.tsx70-120onShellReady) apps/webapp/app/entry.server.tsx122-172Context Providers:
LocaleContextProvider: Provides locale based on Accept-Language header apps/webapp/app/entry.server.tsx36-39OperatingSystemContextProvider: Detects Mac vs Windows from User-Agent apps/webapp/app/entry.server.tsx42-44Client Entry (entry.client.tsx):
hydrateRoot() apps/webapp/app/entry.client.tsx6-15Sources: apps/webapp/app/entry.server.tsx1-258 apps/webapp/app/entry.client.tsx1-16
The root route (root.tsx) establishes the application shell and global configuration:
Root Loader:
Set-Cookie header with committed session apps/webapp/app/root.tsx69Revalidation Strategy:
/resources/environment form actions apps/webapp/app/root.tsx75-81Meta Tags:
Error Boundary:
RouteErrorDisplay component apps/webapp/app/root.tsx97Sources: apps/webapp/app/root.tsx1-133
Remix Request Handler:
createRequestHandler({ build, mode }) apps/webapp/server.ts175-179{cwd}/build apps/webapp/server.ts114Conditional Dashboard Access:
DASHBOARD_AND_API_DISABLED=true, only /healthcheck is accessible apps/webapp/server.ts155-185ALLOW_ONLY_REALTIME_API=true, only /realtime/* and /healthcheck are accessible apps/webapp/server.ts156-167Sources: apps/webapp/server.ts113-185
The middleware chain processes requests in the following order:
| Middleware | Purpose | Configuration |
|---|---|---|
| compression | Compress HTTP responses | Disabled via DISABLE_COMPRESSION=1 |
| express.static | Serve static assets | /build (1y), /public (1h) |
| morgan | HTTP request logging | Format: 'tiny' |
| Security Headers | Set security-related headers | HSTS, X-Robots-Tag, etc. |
| Trailing Slash | Redirect trailing slashes | 301 permanent redirect |
| Request ID | Generate unique request identifier | Via nanoid(), stored in async context |
| apiRateLimiter | Rate limit API requests | From build.entry.module.apiRateLimiter |
| engineRateLimiter | Rate limit engine requests | From build.entry.module.engineRateLimiter |
| createRequestHandler | Remix route handler | Main application logic |
Middleware Imports:
Sources: apps/webapp/server.ts93-179
The application supports two parallel real-time communication systems: Socket.IO and raw WebSockets.
The server handles WebSocket upgrades via a custom upgrade listener:
Upgrade Event Listener apps/webapp/server.ts228-263
Path-Based Routing apps/webapp/server.ts235-256
/socket.io/* → Socket.IO engine apps/webapp/server.ts238-244/ws → WebSocket server apps/webapp/server.ts247-262Socket.IO Upgrade apps/webapp/server.ts242
WebSocket Upgrade apps/webapp/server.ts260-262
Module Imports:
build.entry.module.socketIo apps/webapp/server.ts120build.entry.module.wss apps/webapp/server.ts121Sources: apps/webapp/server.ts225-263 apps/webapp/app/entry.server.tsx234-235
The web application follows a layered architecture pattern separating concerns into distinct layers:
Presenters provide a data transformation and aggregation layer between routes and raw data access. They:
Example Presenters:
RunPresenter - Transforms TaskRun data for displayTaskDetailsPresenter - Aggregates task execution detailsRunStreamPresenter - Prepares real-time run streamsSelectBestEnvironmentPresenter - Determines optimal environment selectionFor more details, see Run Monitoring and Visualization and Realtime Updates and SSE.
Services contain business logic and orchestrate complex operations. They:
Key Services:
The data access layer uses specialized clients for each data store:
@unkey/cache for distributed cachingFor database schema details, see sections 6.1 through 6.5.
Sources: Inferred from Diagram 5 and architectural patterns throughout the codebase
The application uses an extensive environment configuration system defined in apps/webapp/app/env.server.ts1-627
Database Configuration:
DATABASE_URL - Primary PostgreSQL connection apps/webapp/app/env.server.ts52-57DIRECT_URL - Direct PostgreSQL connection for migrations apps/webapp/app/env.server.ts61-66DATABASE_READ_REPLICA_URL - Optional read replica apps/webapp/app/env.server.ts67DATABASE_CONNECTION_LIMIT, DATABASE_POOL_TIMEOUT, DATABASE_CONNECTION_TIMEOUT apps/webapp/app/env.server.ts58-60Redis Configuration: Each Redis use case can have separate configuration:
Deployment Configuration:
DEPLOY_REGISTRY_HOST, DEPLOY_REGISTRY_USERNAME, DEPLOY_REGISTRY_PASSWORD apps/webapp/app/env.server.ts293-296DEPOT_TOKEN, DEPOT_ORG_ID, DEPOT_REGION apps/webapp/app/env.server.ts288-290OpenTelemetry Configuration:
Rate Limiting Configuration:
API_RATE_LIMIT_REFILL_INTERVAL, API_RATE_LIMIT_MAX, API_RATE_LIMIT_REFILL_RATE apps/webapp/app/env.server.ts275-277API_RATE_LIMIT_JWT_WINDOW, API_RATE_LIMIT_JWT_TOKENS apps/webapp/app/env.server.ts282-283Run Engine Configuration:
RUN_ENGINE_WORKER_COUNT, RUN_ENGINE_TASKS_PER_WORKER apps/webapp/app/env.server.ts543-544RUN_ENGINE_WORKER_POLL_INTERVAL, RUN_ENGINE_WORKER_IMMEDIATE_POLL_INTERVAL apps/webapp/app/env.server.ts546-547Sources: apps/webapp/app/env.server.ts1-627
The application uses a multi-stage Docker build for optimal image size and layer caching:
Build Stages:
Pruner docker/Dockerfile6-12
turbo prune to extract webapp dependenciesDev Dependencies docker/Dockerfile24-29
Production Dependencies docker/Dockerfile32-41
Builder docker/Dockerfile44-74
pnpm run generate and pnpm run build --filter=webapp...Runner docker/Dockerfile77-114
node userBuild Arguments:
SENTRY_RELEASE, SENTRY_ORG, SENTRY_PROJECT - Sentry configuration docker/Dockerfile51-56BUILD_APP_VERSION, BUILD_GIT_SHA, BUILD_GIT_REF_NAME, BUILD_TIMESTAMP_SECONDS - Build metadata docker/Dockerfile95-102Sources: docker/Dockerfile1-115
The container entrypoint handles initialization and startup:
Initialization Steps:
Database Availability Check docker/scripts/entrypoint.sh4-6
DATABASE_HOST to be reachable if setPrisma Migrations docker/scripts/entrypoint.sh9-11
pnpm --filter @trigger.dev/database db:migrate:deployClickHouse Migrations docker/scripts/entrypoint.sh13-35
CLICKHOUSE_URL is setsecure=true parameter is in connection stringPrisma Client Setup docker/scripts/entrypoint.sh38-39
Node.js Memory Configuration docker/scripts/entrypoint.sh44-50
--max-old-space-size from NODE_MAX_OLD_SPACE_SIZE (default: 8192 MB)Application Start docker/scripts/entrypoint.sh50
dumb-init as PID 1 for proper signal handlingNODE_PATH for pnpm resolutionbuild/server.jsSources: docker/scripts/entrypoint.sh1-51
The application performs several initialization tasks on startup via apps/webapp/app/entry.server.tsx174-258:
Sentry Integration:
Worker Initialization:
Bootstrap Function:
Run Engine Event Bus:
registerRunEngineEventBusHandlers apps/webapp/app/entry.server.tsx241Monitoring:
EVENT_LOOP_MONITOR_ENABLED=1 apps/webapp/app/entry.server.tsx245-247RESOURCE_MONITOR_ENABLED=1 apps/webapp/app/entry.server.tsx255-257Remote Builds Logging:
Uncaught Exception Handling:
Sources: apps/webapp/app/entry.server.tsx174-258
The Trigger.dev web application architecture combines Remix's server-side rendering capabilities with Express's middleware ecosystem to create a robust, scalable platform. Key architectural decisions include:
The architecture prioritizes observability, security, and performance while maintaining clear separation of concerns and flexibility for different deployment scenarios.
Refresh this wiki
This wiki was recently refreshed. Please wait 7 days to refresh again.