Skip to main content

Scheduled Jobs (App Cron)

Scheduled jobs run a command on a cron schedule inside your application's own container — with its image, its environment variables, and its code. It's the app-level equivalent of Heroku Scheduler or Render Cron Jobs, ideal for things like php artisan schedule:run, a nightly cleanup, a digest email, or a periodic import.

Scheduled jobs vs. daemons vs. server cron

Scheduled jobsBackground workers (daemons)Server cron jobs
Runs inApp container (one-shot per fire)App container (long-running)Host server
LifecycleFires on a schedule, exitsAlways running (restart policy)Host crontab entry
Has your app's env❌ (host only)
Good forschedule:run, cleanups, reportsqueues, schedulers, websocketsDocker/host maintenance

Add a scheduled job

  1. Open your app → the Scheduled tab.
  2. Add job and fill in:
    • Name — a label (e.g. nightly-cleanup).
    • Command — what to run, e.g. php artisan schedule:run. It runs inside the container via bash -lc, so your runtime (php/artisan, python, node) resolves exactly as it does for the app.
    • Schedule — a standard 5-field cron expression, in UTC. Use a preset or type your own.
  3. Save. The job runs automatically at its scheduled times. Use the play icon to run it once immediately (handy for testing).

Schedule format

Schedules are standard 5-field cron, evaluated in UTC:

ExpressionRuns
* * * * *Every minute
*/5 * * * *Every 5 minutes
0 * * * *Every hour, on the hour
0 3 * * *Every day at 03:00 UTC
0 3 * * 0Every Sunday at 03:00 UTC
0 3 1 * *The 1st of each month at 03:00 UTC
note

Times are UTC, not your local timezone. 0 3 * * * is 03:00 UTC — convert from your zone when picking a time (e.g. WIB is UTC+7, so 10:00 WIB = 0 3 * * *).

How it runs

Every minute, Vessl checks which enabled jobs are due and runs each one with docker exec inside the app's currently running container. The command's exit code and output tail are recorded and shown per job (success / failed / skipped).

  • A run is skipped (not failed) when the app is stopped or mid-deploy — there's no running container to exec into. It runs normally at the next scheduled time once the app is active again.
  • A run is failed when the command exits non-zero. The last output tail is saved so you can see why.
  • The command runs inside your container (its own filesystem, env, and code) — it cannot reach the host or other tenants.

Notes & limits

  • The app must be deployed and running for a job to fire (a stopped app skips its jobs).
  • The image needs a shell (bash) for the command — standard language images (PHP, Node, Python) have one; minimal scratch/distroless images do not.
  • Schedules are UTC. Overlap protection: a slow run won't stack on top of the next minute's fire.

See also: Background Workers · Server Cron Jobs · Run a Command / Console.