docs Scheduled Tasks

Scheduled Tasks

Run cron jobs and one-off commands (migrations, scripts, queries) against your apps, stacks and databases.

Overview

A task is a named, repeatable command on one service, with an optional cron schedule. Use it for things like php artisan schedule:run every minute, a nightly cleanup, or a saved php artisan migrate --force you run by hand after deploys. Unlike the web terminal, every run is recorded with its output, exit code, duration and who started it.

Tasks live in the Tasks tab of every application, Compose stack and database. The tab has three parts: the task list, a Run a command box for one-off commands, and the Run history.

Creating a Task

Click New task and fill in:

Name

A label for the list and the history, e.g. “Run migrations”.

Service

Stacks only — which Compose service the command runs in.

Command

Up to 4,000 characters. Runs through /bin/sh -lc, so &&, pipes and globs work.

Schedule

A preset (every minute, every 5 minutes, hourly, daily at 2:00, weekly), Custom…, or “No schedule (run manually)”.

Time zone

Any IANA zone (e.g. Europe/Madrid). Defaults to UTC.

Timeout (seconds)

1 to 86,400; default 300. The container is killed when it expires.

While you type a schedule the form previews the next run times, so you can check the time zone before saving. Each task in the list has Run now, Edit, Enable/Disable and Delete actions. Disabling stops the schedule without deleting the task; you can still run it manually. Schedule changes take effect immediately — no restart needed.

Cron Syntax

Schedules use standard 5-field cron: minute hour day-of-month month day-of-week. Seconds fields and shortcuts such as @daily are rejected, and the smallest interval is one minute.

bash
* * * * *      # every minute
*/15 * * * *   # every 15 minutes
0 3 * * *      # every day at 03:00 (in the task's time zone)
30 8 * * 1-5   # weekdays at 08:30
0 0 1 * *      # first day of every month at midnight

Where Commands Run

Every run starts a new, temporary container from the service's image and removes it when the command exits. It never executes inside the running container, so a migration works even while the app is stopped, a long job does not steal resources from the container serving traffic, and replicas are never an issue.

TargetHow it runs
ApplicationThe app's current image with its environment variables, volumes, CPU/memory limits and the deploykit-network. The app must have been deployed at least once.
Stack servicedocker compose run --rm --no-deps for the chosen service: environment, volumes and networks come from the stack, and the other services are not started.
DatabaseThe database's client image (psql, mysql, redis-cli, mongosh…) on the shared network. The data volume is not mounted.

Database tasks receive DK_DB_HOST, DK_DB_PORT, DK_DB_USER, DK_DB_NAME and DK_DB_PASSWORD. For PostgreSQL, MySQL/MariaDB and Redis the password is also set in PGPASSWORD, MYSQL_PWD or REDISCLI_AUTH, so the client never needs it on the command line. MongoDB has no such variable — pass $DK_DB_PASSWORD to mongosh yourself.

bash
# PostgreSQL database task
psql -h "$DK_DB_HOST" -U "$DK_DB_USER" -d "$DK_DB_NAME" -c "VACUUM ANALYZE;"

One-Off Commands

Type a command in Run a command, optionally change its timeout, and click Run. It runs exactly like a task (new container, same environment) without being saved, and appears in the Run history. Output streams live into a Command output dialog that you can reopen later from the history.

Run History and Behaviour

  • Each run ends as success (exit code 0), failed, timed_out or skipped, with its exit code, duration and trigger (schedule or manual).
  • No overlap: if the previous run of the same task is still going, the new one is recorded as skipped instead of running. An every-minute job can never pile up.
  • Timeouts kill the container and append “[deploykit] Command timed out and was killed.” to the output.
  • Only the last 64 KB of output is stored — the tail, where errors usually are. Longer output is marked as truncated.
  • The 50 most recent runs are kept per task (and per service for one-off commands). History survives deleting the task.
  • A run captures the command and timeout at start, so editing a task never changes a run already in progress. Runs cannot be cancelled.
  • Up to 3 runs execute at the same time across the instance; further runs wait in the queue.
  • Failed and timed-out runs fire the Task Failed notification event — enable it in your project's notification channels.
  • On remote servers, output arrives when the command finishes rather than line by line.

Permissions

Running a command is as powerful as the terminal, so creating, editing, deleting and running tasks requires the Operator (or Admin) global role and Operator access to the project. Any project member can see the task list and run history, but the command output is only visible to project Operators and Admins, since it can contain secrets such as connection strings. All task changes and runs are written to the audit log.

Gotchas: The image needs a POSIX shell at /bin/sh — scratch and distroless images cannot run tasks. Task containers publish no ports and never restart, so a command that should keep running belongs in a service, not a task. In a stack, the Run a command box always uses the first service in the Compose file; create a task to pick another service.