Runtime status: production scheduling now runs from exactly one cron
container in the OVHcloud VM Compose stack. The Vercel Cron setup and commands
retained below document the previous architecture and a legacy recovery path;
they are not normal production deployment instructions. See
Deployment and the
Production VM Runbook.
Overview
The same authenticated Next.js cron routes execute scheduled sync, backup, and
reconciliation work in every supported runtime. Current production invokes
them from the VM cron container; the previous architecture invoked them with
Vercel Cron Jobs.
The cron route modules also export maxDuration = 300. Keep the route-level
exports and vercel.json aligned; scheduled launch work runs in after(), and
reconciliation treats mature jobs with missing operation IDs as incomplete
launch failures.
Implementation Details
Sync Job Execution Flow
Cron Trigger: Vercel triggers the cron endpoint every minute
Authentication: Request is authenticated via Authorization: Bearer ${CRON_SECRET}
Job Discovery: Query database for jobs where status = 'scheduled' and nextRunAt <= now()
Queue Launch Work:
Update due jobs to running.
Queue executeSyncJob() in after() callback.
Launch Phase (executeSyncJob):
Validate payload + resolve paths.
Launch rclone sync/bisync operations on Fly.io.
Persist operation IDs to sync_jobs.operationIds.
Return without waiting for terminal completion.
Reconciliation Phase:
reconcile-stale-jobs polls Fly.io for each operation ID.
Marks jobs/items completed or failed when terminal state is reached.
Updates activity logs with terminal outcomes.
Response: Cron launch endpoint returns summary immediately after queueing launch work
Key Functions
executeSyncJob(jobId: string, userId: string)
Located in src/lib/sync/execute-sync-job.ts
Executes a single sync job:
Retrieves job and items from database.
Parses job payload.
Launches sync operation(s) for each item on Fly.io.
Generates cron expressions for all schedule types:
Daily: 30 14 * * * (14:30).
Weekly: 30 14 * * 1 (Monday 14:30).
Monthly: 30 14 1 * * (1st of month 14:30).
Hourly: 0 */2 * * * (every 2 hours).
Minutely: */15 * * * * (every 15 minutes).
Database Schema
sync_jobs Table
CREATETABLE sync_jobs ( id TEXTPRIMARYKEY, userId TEXTNOTNULL,statusTEXTNOTNULL,-- scheduled | running | completed | failed | cancelled schedule TEXTNOTNULL,-- daily | weekly | monthly | hourly-N | minutely-N scheduledTime TEXT,-- HH:mm format timezone TEXTDEFAULT'UTC', nextRunAt TIMESTAMP,-- When job should run next lastRunAt TIMESTAMP,-- When job last ran startedAt TIMESTAMP,-- When current execution started finishedAt TIMESTAMP,-- When current execution finished lastError TEXT,-- Error message if failed...);CREATEINDEX sync_jobs_next_run_idx ON sync_jobs(nextRunAt);CREATEINDEX sync_jobs_status_idx ON sync_jobs(userId,status);
Security
Authentication
All cron endpoints require CRON_SECRET environment variable.
Request must include Authorization: Bearer ${CRON_SECRET} header.
Unauthorized requests return 401 Unauthorized.
Environment Variables
CRON_SECRET=your-secret-key-here
Monitoring & Troubleshooting
Vercel Logs
# View production logsvercel logs --prod --follow
# Filter for cron jobsvercel logs --prod |grep"execute-sync-jobs"# View logs from last hourvercel logs --prod --since 1h
Manual Testing
# Test sync jobs croncurl -X GET https://stratofusion.io/api/cron/execute-sync-jobs \ -H "Authorization: Bearer your-cron-secret"
Local Windows Testing
Use PowerShell on Windows 11. Vercel cron does not run automatically on localhost.