Cron workers
The crons/ directory holds scheduled jobs that run independently of the API server. Each is its own Bun program with its own Dockerfile and Railway configuration, scheduled with a cronSchedule in railway.json.
The jobs
analyticsCache
Warms the analytics cache so dashboards stay fast. It acquires a Redis lock (so overlapping runs don't collide), queries the read replica through AnalyticsService, and writes the results into Redis.
- Schedule:
*/15 0,1,2,12-23 * * *— every 15 minutes during active hours, skipping the quiet overnight window.
postgresBackup
Takes a logical backup of the primary database: it runs pg_dump, gzips the output, and uploads it to a private S3 backups bucket.
- Schedule:
0 5 * * *— once daily.
fileStorageBackup
Mirrors the primary file-storage bucket to a separate backup bucket — copying new or changed objects and removing orphans — so uploaded files have a second, independent copy.
- Schedule:
0 5 * * *— once daily.
recurringSchedules
A placeholder for future scheduled work. It has no implementation yet.
How they run
Each cron is deployed to Railway as its own service with restartPolicyType: NEVER — the job runs to completion on its schedule rather than staying resident. They build from their own Dockerfiles (crons/<name>/Dockerfile) using the monorepo as the build context, so they can import the same shared packages the API server uses.
Because they reuse @surplus/services, @surplus/postgres, and @surplus/redis, the cron jobs benefit from the same domain logic and database access patterns as the rest of the platform — they just inject their own clients and run headless.
Related
- Backups and recovery — the backup jobs from a resilience and audit perspective.
- Build and deploy — how Railway builds and schedules these services.