Schedule a job
Run a command on a UTC schedule, inspect its result, and control overlap.
Create a scheduled service for a command that runs and exits. Test it manually before adding a cron schedule. Jobs use the same repository or image sources and variables as runtime services, but have no port, public address, replicas, sleep setting or health checks.
Before you start
- Have a ready environment and a repository with a
Dockerfileor a public image. - Prepare a command that can finish without prompting for input.
- Set the job’s required variables and credentials.
- For MCP, use a product-scoped token with Deployment
writeto create, update, deploy and run jobs, andreadfor history and logs.
1. Create without a schedule
Choose Add service on the environment canvas and set the mode to scheduled. With MCP, call deployment_service_create with mode: "scheduled", your source, environment ID and name.
Supply a command of up to 4096 characters, or omit it to use the image’s entrypoint. Leave schedule unset or null while testing. Service mode is fixed at creation; you cannot later turn a job into a persistent service or the reverse.
2. Deploy and run it
Deploy the job at least once so it has an image to run. Then choose Run now on its panel, or call deployment_job_run with serviceId.
Run now bypasses both the schedule and concurrency policy. Check for an existing run before starting another if duplicate work would be a problem. Manual runs are recorded as manual in the same history as scheduled runs.
3. Set the schedule and limits
Use deployment_service_update to set schedule, jobTimeoutSec and jobConcurrency. Schedules are five-field cron expressions in UTC: minute, hour, day of month, month, day of week.
Schedule | Runs |
|---|---|
0 3 * * * | Daily at 03:00 UTC |
*/15 * * * * | Every 15 minutes |
0 9 * * 1 | Mondays at 09:00 UTC |
0 0 1 * * | First day of each month at 00:00 UTC |
30 6 * * 1-5 | Weekdays at 06:30 UTC |
Invalid expressions are rejected when saved. There is no timezone setting; account for daylight saving changes yourself if the work follows local time.
The timeout defaults to one hour and accepts 60–86,400 seconds. A run still going at the deadline is stopped and marked TimedOut.
Concurrency | If a scheduled tick arrives during a run |
|---|---|
forbid (default) | Skip the new tick and record it as skipped |
allow | Start another run alongside the existing one |
replace | Stop the existing run and start the new one |
Keep forbid unless the work supports overlap or replacement. It prevents overlapping scheduled runs, but does not constrain Run now. Failed runs are not retried automatically; implement any required retry logic in the command.
Verify it works
Open the job’s run history, or call deployment_job_runs_list. Check that the manual test succeeded with exit code 0, then inspect the output and the intended result, such as the exported file or updated records. After adding a schedule, confirm a scheduled run appears at the expected UTC time.
Status | Meaning |
|---|---|
Running | Started and not finished |
Succeeded | Exited with code 0 |
Failed | Exited with a nonzero code |
TimedOut | Stopped at its timeout |
Skipped | A scheduled tick was skipped under forbid |
Logs on a run or deployment_job_run_logs returns a live tail while running and stored output afterward. Stored output contains the last 256 KiB. A polling caller should stop when the returned live value becomes false.
History retains the last 100 runs and nothing older than 30 days; skipped ticks count toward the 100. Send records elsewhere from the job if you need longer retention.
Pause and resume
Pause stops scheduled starts without clearing the schedule. Resume enables them again. MCP sets scheduleSuspended through the service update tool. Pausing does not stop an active run or block manual starts. It survives deployments and is copied when an environment is forked.
Jobs are billed for compute while running, not between runs. A product billing suspension stops scheduled starts and refuses manual runs; clearing it restores scheduling while preserving jobs you had paused.
Jobs have no interactive shell. For another command against their image, use a separate one-off run, or start the job’s configured command with Run now.