Skip to main content

Run a cron job as an app

A cron job is an app that runs on a schedule instead of serving on a port. You create it from a git repository, a folder on your computer or a Docker image, exactly like any other app, and pick a schedule such as "every day at 03:00". On every run Cloider starts your app's image, runs it until it exits, and keeps its status and output. Between runs nothing is running.

A cron job keeps everything an app has: builds and deploys, environment variables, build variables and build arguments, the deploy hook of an image app, logs, the web terminal, cloider app exec, insights, pause, resume and delete.

Cron jobWeb service
RunsOn its schedule, until the command exitsAll the time
Port, subdomain, domainsNoneOne port, served on your domains
What runsThe start command, or the image's own ENTRYPOINT and CMDThe same
New versionA build or deploy, picked up by the next runA build or deploy, rolled out at once
LogsPer run, for the last 5 succeeded and 5 failed runsThe running app's
TerminalA shell in a running run, from the Runs tabA shell in the running app

How runs work​

  • The schedule is a standard cron expression and is always in UTC.
  • Each run is a fresh container of the version the app has deployed, with the app's environment variables. A run that exits with code 0 has Succeeded, any other exit code is Failed. A failed run is not retried.
  • One run at a time. When a run is still going at the next scheduled time, that scheduled run is skipped.
  • A run is stopped after 12 hours.
  • A run that could not start on time, for example during maintenance, is dropped after 5 minutes instead of running late.
  • The last 5 succeeded and 5 failed runs are kept, with their output, in the Runs tab.

What a run executes is the app's start command, when it has one:

  • Built from source with a buildpack: the start command is built into the image. Left empty, your project's own Procfile web entry or start script runs.
  • Built from your own Dockerfile, or a Docker image: the start command runs with sh -c in place of the image's ENTRYPOINT and CMD. Left empty, those run. This is how one image runs several jobs, for example python manage.py clearsessions in your web app's image, or pg_dump ... in postgres.

Create a cron job​

  1. Open Apps in the sidebar and click Create App.
  2. Pick your code: a git repository, a Docker image, or a folder to upload.
  3. In Configure your app, set Type to Cron job.
  4. Pick a Schedule from the list, or type your own cron expression. The port and subdomain fields go away, a cron job has neither.
  5. Set the Start Command to what each run executes, for example python manage.py clearsessions. Leave it empty to use your project's own Procfile or start script, or the image's own CMD.
  6. Add environment variables if the job needs any, and click Create App.

The first build deploys the job, and it runs on its schedule from then on. Click Run now on the Runs tab to try it right away.

Schedule syntax​

A schedule has five fields, separated by spaces, in UTC:

┌───────────── minute (0 to 59)
│ ┌─────────── hour (0 to 23)
│ │ ┌───────── day of the month (1 to 31)
│ │ │ ┌─────── month (1 to 12, or JAN to DEC)
│ │ │ │ ┌───── day of the week (0 to 6, Sunday is 0, or SUN to SAT)
│ │ │ │ │
* * * * *

Each field takes * (every value), a number, a list (1,15), a range (1-5), a step (*/15, 0-30/10, or 5/20, which is 5-59/20), or the three letter names of months and weekdays. When both the day of the month and the day of the week are set, a day matching either one runs the job.

ScheduleRuns
*/5 * * * *Every 5 minutes
0 * * * *At the start of every hour
30 2 * * *Every day at 02:30 UTC
0 9 * * MON-FRIEvery weekday at 09:00 UTC
0 0 * * 0Every Sunday at midnight UTC
0 0 1 * *On the 1st of every month at midnight UTC
0 */6 * * *Every 6 hours

The shorthands @hourly, @daily (or @midnight), @weekly, @monthly and @yearly (or @annually) work too. A schedule that can never run, like 0 0 30 2 * (February 30th), is refused.

UTC doesn't follow daylight saving time: 0 7 * * * runs at 09:00 in Berlin in summer and at 08:00 in winter.

See runs and run a job now​

The Runs tab lists the runs Cloider keeps, newest first: their status, whether the schedule or Run now started them, when they started and how long they took. A failed run says why, for example Exited with code 1 or Stopped after running for 12 hours, and a run stuck before it starts says The image could not be pulled. Click Logs on a run to read its output.

Run now starts a run immediately, on the version the app has deployed, without changing the schedule. It is refused while a run is still going.

Open the app and the Runs tab. The tab also shows the schedule and the next run in your local time.

Logs​

A cron job has no Logs tab: click Logs on a run in the Runs tab to read its output, followed live while the run is going.

cloider app logs cleanup # the latest run's output
cloider app logs cleanup -f # follow every run as it starts
cloider app logs cleanup --run app-29885940 # one run's output

Only the runs the Runs tab lists still have their output. See Logs.

Debug with the terminal​

While a run is going, click Terminal on it in the Runs tab to open a shell inside that run's container, with its files and processes. The session ends when the run finishes.

Between runs, cloider app shell and cloider app exec open a container with the cron job's image and environment variables, started for them and stopped again once it has been unused for 10 minutes. It is the quickest way to find out why a run fails: run the job's command by hand and watch what it does.

cloider app shell cleanup
cloider app exec cleanup -- python cleanup.py --dry-run

The terminal needs a shell in the image: an image without sh, like a distroless image, has nothing to open. See Terminal and exec.

Change the schedule​

Open the app's Settings tab, pick or type the new Schedule, and click Save changes. The job is redeployed on the new schedule in a few seconds, on the same version.

A cron job stays a cron job: it can't be turned into a web service, or the other way around. Create a new app for that.

Pause, resume and delete​

Pausing a cron job stops its schedule, and stops a run that is going at that moment. Its runs list, version and environment stay. Resuming it puts the schedule back, and the next run is the schedule's next time: runs missed while paused don't run. See Pause, resume and delete.

Billing​

A cron job is billed at the same rates as any app, for the CPU and memory its runs use, and each run is measured on its own:

  • For exactly as long as it ran, from the second its container started to the second it finished. Time spent waiting to start, such as pulling the image, isn't billed, and neither is anything between runs.
  • For the CPU it used, as its container counted it, read every 5 seconds while it runs.
  • For the memory it was using, averaged over those same readings.

A run that is over in a few seconds can end before its first reading. It is billed at what the job's last measured run used per second, and costs nothing while the job has no measured run yet.

Only runs are billed. The container cloider app shell and cloider app exec open between runs is free. See pricing and Insights.

Troubleshooting​

A run shows The image could not be pulled. The run is stuck before it starts. For an image app, check the image still exists and the registry login on the Settings tab still works.

A scheduled run didn't happen. The previous run was still going (one run at a time), the app was paused, or the schedule is in UTC and you read it in your local time. The Runs tab and the Overview show the next run in your local time.

The terminal says the app isn't running. The image has no sh, or its shell container is still starting. Wait a few seconds and reconnect.

A run's terminal says the run isn't running any more. The run finished. Read its output with Logs on the Runs tab.