Thanks to visit codestin.com
Credit goes to deepwiki.com

Menu

Application Architecture

Relevant source files

This document describes the architecture of the Trigger.dev web application, which serves both the dashboard UI and API endpoints. The webapp is a Remix 2.1.0 application running on an Express server, built as a containerized service that can run standalone or in a clustered configuration.

For information about the navigation system and routing patterns, see Navigation and Routing. For details on real-time updates and SSE, see Realtime Updates and SSE. For Socket.IO and WebSocket communication, see Socket.IO and WebSocket Communication.


Technology Stack

The web application is built on the following core technologies:

ComponentTechnologyVersionPurpose
FrameworkRemix2.1.0Server-side rendering, routing, data loading
HTTP ServerExpress4.20.0HTTP server, middleware pipeline
RuntimeNode.js≥18.19.0 or ≥20.6.0JavaScript runtime
UI LibraryReact18.2.0Component rendering
Database ORMPrisma6.14.0Database access via @trigger.dev/database
Build Toolesbuild0.15.10+TypeScript compilation, bundling
Monorepopnpm + Turborepo8.15.5 + 1.10.3Package management, build orchestration
ContainerDockerMulti-stageProduction deployment

Sources: apps/webapp/package.json1-289 package.json1-95


Server Architecture

Express + Remix Integration

The web application combines Express as the HTTP server with Remix as the application framework. This integration is managed in apps/webapp/server.ts1-269 which:

  1. Configures Express middleware
  2. Sets up static asset serving
  3. Integrates Socket.IO and WebSocket servers
  4. Delegates dynamic requests to Remix request handler

Sources: apps/webapp/server.ts91-269

Server Startup Sequence

The server initialization follows this sequence:

  1. Import Sentry - Load error tracking (apps/webapp/server.ts1)
  2. Configure Express - Set up middleware (apps/webapp/server.ts91-107)
  3. Load Build Artifacts - Require compiled Remix app (apps/webapp/server.ts114-115)
  4. Extract Services - Get Socket.IO, WebSocket, rate limiters from build (apps/webapp/server.ts120-124)
  5. Configure Middleware - Set up request pipeline (apps/webapp/server.ts126-186)
  6. Start HTTP Server - Listen on configured port (apps/webapp/server.ts187-199)
  7. Attach Real-time Services - Connect Socket.IO and WebSocket (apps/webapp/server.ts225-263)

Sources: apps/webapp/server.ts1-269


Request Handling Pipeline

Middleware Stack

Requests flow through the following middleware in order:

Middleware Configuration:

MiddlewarePurposeConfigurableFile Reference
compression()Gzip compressionDISABLE_COMPRESSIONserver.ts93-95
Static assetsServe /build and /publicCache headersserver.ts101-105
morgan()Request loggingAlways enabledserver.ts107
Security headersXSS, robots, HSTSBased on hostnameserver.ts126-143
Request IDUnique ID per requestAlways enabledserver.ts145-153
Route filteringRestrict to /realtimeALLOW_ONLY_REALTIME_APIserver.ts156-167
API rate limiterPublic API throttlingSee API Authenticationserver.ts169
Engine rate limiterInternal API throttlingConfiguration in envserver.ts170
Remix handlerApplication logicN/Aserver.ts172-179

Sources: apps/webapp/server.ts93-186

Request Context Tracking

Each request receives a unique request ID and HTTP context stored in async local storage:

This context is accessible throughout the request lifecycle via httpAsyncStorage. The context includes:

  • requestId: Unique identifier (generated by nanoid())
  • path: Request URL path
  • host: Request hostname
  • method: HTTP method

Sources: apps/webapp/server.ts145-153 apps/webapp/app/services/httpAsyncStorage.server.ts (referenced)


Clustering and Horizontal Scaling

The webapp supports optional clustering to utilize multiple CPU cores. When enabled, it runs in a primary-worker model.

Cluster Architecture

Cluster Configuration

Environment VariableDefaultDescription
ENABLE_CLUSTER"0"Enable clustering mode
CLUSTER_WORKERSCPU countNumber of worker processes
WEB_CONCURRENCYCPU countAlternative worker count variable
GRACEFUL_SHUTDOWN_TIMEOUT30000 msMax time to wait for graceful shutdown

Cluster Behavior:

  1. Worker Count Determination:

    • Uses CLUSTER_WORKERS or WEB_CONCURRENCY if set
    • Falls back to os.availableParallelism() (CPU count)
  2. Primary Process Responsibilities:

  3. Worker Process Responsibilities:

    • Run Express + Remix application
    • Handle HTTP requests
    • Set process title: "node webapp-worker-{id}" (server.ts109-111)
  4. Graceful Shutdown:

    • Primary forwards SIGTERM/SIGINT to all workers
    • Waits for workers to exit cleanly (max timeout)
    • Primary exits once all workers have stopped

Sources: apps/webapp/server.ts15-90


Build and Deployment Pipeline

Multi-Stage Docker Build

The production build uses a multi-stage Dockerfile optimized for layer caching and minimal image size.

Build Stage Purposes:

StageBase ImagePurposeKey Files
goose_buildergolang:1.23-alpineBuild Goose migration toolClickHouse migrations
prunerNodeUse Turborepo to prune workspaceturbo prune --scope=webapp
baseNodeSet up base dependencies.gitignore, package.json, lock file
dev-depsbaseInstall dev dependenciesFor building TypeScript
production-depsbaseInstall production deps onlyFor runtime
builderbase + dev-depsBuild webapp, generate PrismaCompiled assets
runnerbase + production-depsFinal runtime imageBuilt artifacts only

Build Commands:

Sources: docker/Dockerfile1-116 apps/webapp/package.json7-11

Turborepo Build Pipeline

The monorepo uses Turborepo to orchestrate builds with dependency tracking:

Sources: turbo.json1-147 apps/webapp/package.json7-11


Environment Configuration System

Centralized Configuration with Zod

All environment variables are validated and typed using Zod schemas in apps/webapp/app/env.server.ts1-685 This provides:

  • Type-safe access to environment variables
  • Runtime validation on server startup
  • Default values and transformations
  • Clear documentation of required vs. optional variables

Configuration Pattern:

Environment Variable Categories

CategoryExample VariablesPurpose
Core ApplicationNODE_ENV, APP_ORIGIN, LOGIN_ORIGINBasic app configuration
DatabaseDATABASE_URL, DIRECT_URL, DATABASE_READ_REPLICA_URLPostgreSQL connections
Redis (Segmented)REDIS_HOST, CACHE_REDIS_HOST, PUBSUB_REDIS_HOSTDifferent Redis instances
AuthenticationSESSION_SECRET, MAGIC_LINK_SECRET, ENCRYPTION_KEYAuth and encryption
External ServicesRESEND_API_KEY, OPENAI_API_KEY, DEPOT_TOKENThird-party integrations
DeploymentDEPLOY_REGISTRY_HOST, DEPOT_ORG_IDDocker registry config
ObservabilityINTERNAL_OTEL_TRACE_EXPORTER_URL, POSTHOG_PROJECT_KEYTelemetry and analytics
Run EngineRUN_ENGINE_WORKER_COUNT, RUN_ENGINE_TIMEOUT_EXECUTINGTask execution tuning
Rate LimitingAPI_RATE_LIMIT_MAX, API_RATE_LIMIT_REFILL_RATEAPI throttling
Feature FlagsMARQS_WORKER_ENABLED, V2_MARQS_ENABLEDFeature toggles

Redis Segmentation:

The system uses multiple logical Redis instances for different purposes:

  • Primary Redis: REDIS_HOST, REDIS_PORT - Default instance
  • Rate Limiting: RATE_LIMIT_REDIS_HOST - Token bucket rate limiting
  • Caching: CACHE_REDIS_HOST - Application caching
  • Real-time Streams: REALTIME_STREAMS_REDIS_HOST - SSE event streams
  • Pub/Sub: PUBSUB_REDIS_HOST - Event bus communication
  • Run Engine: RUN_ENGINE_WORKER_REDIS_HOST - Run engine coordination
  • Marqs: MARQS_REDIS_HOST - Message queue system

Each Redis instance supports separate reader/writer hosts for read replica configurations.

Sources: apps/webapp/app/env.server.ts1-685


Middleware Stack Details

Security Headers

Security headers are set on all responses:

Additional headers from root.tsx:

Sources: apps/webapp/server.ts126-143 apps/webapp/app/root.tsx23-28

Compression

Gzip compression is enabled by default for all responses (unless DISABLE_COMPRESSION=1):

Sources: apps/webapp/server.ts93-95

Trailing Slash Handling

Trailing slashes are removed via 301 redirects:

Sources: apps/webapp/server.ts136-142


Real-time Communication Infrastructure

WebSocket Upgrade Handling

The server handles WebSocket upgrades for both Socket.IO and plain WebSocket connections:

Upgrade Logic:

Sources: apps/webapp/server.ts225-263

Socket.IO Integration

Socket.IO is attached to the HTTP server and configured with Redis adapter for multi-instance support:

  1. Attachment: socketIo?.io.attach(server) (server.ts225)
  2. Redis Adapter: Configured in separate module (see Socket.IO and WebSocket Communication)
  3. Upgrade Prevention: Remove duplicate upgrade listeners (server.ts226)

Sources: apps/webapp/server.ts120-226 apps/webapp/app/v3/handleSocketIo.server.ts (referenced)


Static Asset Management

Asset Caching Strategy

The webapp uses different caching strategies for different asset types:

PathCache DurationImmutablePurpose
/build/*1 yearYesRemix fingerprinted assets (CSS, JS)
/public/*1 hourNoStatic files (favicon, images)

Configuration:

Remix automatically fingerprints assets during build, allowing aggressive caching with immutable flag. This means:

  • CSS/JS bundles have content hashes in filenames: entry.client-ABC123.js
  • Browsers can cache indefinitely without revalidation
  • Cache invalidation is automatic when content changes (new hash)

Sources: apps/webapp/server.ts101-105

Build Output Structure

apps/webapp/
├── build/
│   ├── server.js              # Compiled server entry point
│   ├── server.js.map          # Source map
│   └── index.js               # Remix build output
├── public/
│   ├── build/                 # Fingerprinted assets
│   │   ├── entry.client-*.js
│   │   ├── root-*.css
│   │   └── routes/            # Route-specific bundles
│   └── [static files]         # favicon, images, etc.
└── prisma/
    └── seed.js                # Database seeding script

Sources: docker/Dockerfile85-88 turbo.json8-14


Entry Points and Bootstrapping

Server Entry Point

The server bootstraps in this order:

Application Entry Points

Entry PointPurposeExecution Context
server.tsHTTP server setup, Express configNode.js main process
entry.server.tsxServer-side renderingPer-request (server)
entry.client.tsxClient-side hydrationBrowser
root.tsxRoot Remix route, HTML shellBoth server and client
sentry.server.tsError tracking initializationServer startup

Server-Side Rendering Flow:

Client-Side Hydration:

Sources: apps/webapp/server.ts1-269 apps/webapp/app/entry.server.tsx1-258 apps/webapp/app/entry.client.tsx1-16 apps/webapp/app/root.tsx1-133


Bootstrap and Initialization

Bootstrap Sequence

The webapp performs initialization tasks in apps/webapp/app/entry.server.tsx190-257:

Background Services:

  1. Worker System - Graphile Worker for background jobs (apps/webapp/app/services/worker.server.ts)
  2. Event Bus Handlers - React to run lifecycle events (apps/webapp/app/v3/runEngineHandlers.server.ts)
  3. Event Loop Monitor - Detect event loop blocking (optional) (apps/webapp/app/eventLoopMonitor.server.ts)
  4. Resource Monitor - Track memory and CPU usage (optional) (apps/webapp/app/services/resourceMonitor.server.ts)

Feature Flags on Startup:

Sources: apps/webapp/app/entry.server.tsx190-257

Graceful Shutdown

The server handles graceful shutdown on SIGTERM and SIGINT:

In clustered mode, the primary process:

  1. Forwards signals to all workers
  2. Waits for workers to exit (with timeout)
  3. Exits the primary process

Sources: apps/webapp/server.ts207-223 apps/webapp/server.ts29-71


Error Handling

Server-Side Error Handling

Errors during request handling are caught by:

  1. Sentry Integration - Wraps handleError export
  2. Custom Error Logging - Logs to console with context
  3. Uncaught Exception Handler - Exits process on fatal errors

Sources: apps/webapp/app/entry.server.tsx174-228

Client-Side Error Boundary

The root ErrorBoundary component provides a fallback UI for React errors:

Sources: apps/webapp/app/root.tsx83-106


Deployment Architecture

Container Entrypoint

The Docker container uses a multi-step entrypoint script:

Entrypoint Script Steps:

  1. Database Health Check - Wait for PostgreSQL to be ready
  2. Prisma Migrations - Apply database schema changes
  3. ClickHouse Migrations - Apply event store schema changes (if configured)
  4. Copy Runtime Files - Copy Prisma schema and engine binaries
  5. Set Memory Limits - Configure Node.js heap size
  6. Start Application - Run server.js via dumb-init (PID 1 reaper)

Sources: docker/scripts/entrypoint.sh1-51

Build Arguments and Metadata

The Docker build captures metadata via build arguments:

Build ArgPurposeUsed At Runtime
BUILD_APP_VERSIONApplication versionYes (env var)
BUILD_GIT_SHAGit commit hashYes (env var)
BUILD_GIT_REF_NAMEGit branch/tagYes (env var)
BUILD_TIMESTAMP_SECONDSBuild timestampYes (env var)
SENTRY_RELEASESentry release IDBuild time only
SENTRY_ORGSentry organizationBuild time only
SENTRY_PROJECTSentry projectBuild time only

These values are used for:

  • Version display in UI
  • Sentry error tracking correlation
  • Deployment tracking and debugging

Sources: docker/Dockerfile51-103


Configuration Files

Key Configuration Files

FilePurposeFormat
turbo.jsonTurborepo build pipelineJSON
package.jsonWorkspace and script definitionsJSON
apps/webapp/package.jsonWebapp dependencies and scriptsJSON
remix.config.jsRemix framework configurationJavaScript
tailwind.config.tsTailwind CSS design systemTypeScript
tsconfig.jsonTypeScript compiler optionsJSON
.dockerignoreDocker build exclusionsText
pnpm-workspace.yamlpnpm workspace packagesYAML

Sources: turbo.json1-147 package.json1-95 apps/webapp/package.json1-289 .dockerignore1-50


Performance Considerations

Database Query Monitoring

The webapp includes query performance monitoring:

This monitors both writer and replica connections and logs queries exceeding the threshold defined by VERY_SLOW_QUERY_THRESHOLD_MS.

Sources: apps/webapp/app/utils/queryPerformanceMonitor.server.ts1-65

Server Configuration Tuning

Performance-related configuration options:

SettingEnvironment VariableDefaultPurpose
Keep-alive timeout(hardcoded)65sPrevent premature connection closure
Max headersserver.maxHeadersCount0 (unlimited)Uses maxHeaderSize instead
Max old spaceNODE_MAX_OLD_SPACE_SIZE8192 MBNode.js heap size limit
Worker countCLUSTER_WORKERSCPU countNumber of cluster workers
CompressionDISABLE_COMPRESSIONEnabledGzip response compression

Sources: apps/webapp/server.ts201-205 docker/scripts/entrypoint.sh44-50


Summary:

The Trigger.dev web application is a production-grade Remix application running on Express, designed for containerized deployment with optional clustering support. It provides both the dashboard UI and API endpoints, with comprehensive middleware, real-time communication capabilities, validated environment configuration, and graceful shutdown handling. The architecture supports horizontal scaling through clustering and integrates deeply with the run engine, background job processing, and event streaming systems.