Appearance
dtask
dtask is a lightweight Docker task runner designed for Docker Compose.
WARNING
run=host and run=compose rely on Linux host primitives (chroot, host mount layout, and Docker metadata paths). On Docker Desktop (macOS/Windows), these modes can behave unexpectedly due to the Linux VM abstraction. Prefer run=container there unless you have validated your setup end-to-end.
dtask arrose from a desire to solve the co-location problem of scheduling tasks on the host system outside of container context. For instance, running docker compose up --build --pull always -d within the current compose stack every night or schedule other host-level tasks.
One could alternativly solve this using cron or systemd timers, but that splits the location of defining the schedule for the task and the actual script to run, far away from each other. Alternativly, a bit of configuration of existing docker scheduling containers such as mcuadros/ofelia could solve this, but that would require mounting the current directory, make sure paths are correct when mounting or hardcoding the current directory path, which breaks if you copy the compose file to a different location or want to reuse a section.
This problem, can however, easily be solved with the example below:
yaml
# compose.yml
services:
dtask:
image: ghcr.io/kristianvld/dtask
restart: unless-stopped
volumes:
- /:/host
environment:
run: compose # run on host in the compose stack directory (requires `/:/host` mount)
update.schedule: 02:00-04:00 # run every night at a random time between 02AM and 04AM
update.cmd: docker compose up -d --build --pull alwaysWant to schedule a quick ad-hoc bun script to run every hour, without writing your own custom container or start configuring cron in a far away location?
yaml
# example file structure:
# .
# ├── script.ts
# └── compose.yml
services:
dtask-bun-script:
image: ghcr.io/kristianvld/dtask
volumes:
- /:/host:ro # read-only host mount for compose-mode execution
environment:
run: compose
task.schedule: 1h
task.cmd: bun run script.ts # Uses `bun` from the host filesystemDesign Goals
- Keep task configuration compact and readable in
compose.yml. - Easily define scheduled tasks within the host or container context.
- Keep defaults predictable and practical.
- Keep runtime lightweight.
Quick Start
The following example is constructed to showcase all available options and possible values.
yaml
# compose.yml
services:
dtask:
image: ghcr.io/kristianvld/dtask
restart: unless-stopped
volumes:
# this gives access to the host filesystem, the container uses it to access docker, identify
# the compose stack, current working directory, and gives access to any host installed tools
# or commands, e.g. `docker compose` or `bun`
- /:/host
environment:
# <key>: <value> - these are set for all tasks
# <task>.<key>: <value> - these are set for the task <task>
# Define how this task will run. Possible values are:
# `host` will run the task on the host using chroot. All paths are relative to `/` on the host.
# `compose` will run the task on the host, but change the current working directory to the compose stack directory.
# `container` will run the task inside the container. All paths are relative to `/` in the container.
# Both `host` and `compose` will throw an error if `/:/host` is not mounted under `volumes:`.
# Default is `container`
run: host
# Set the working current working directory for the task. Supports absolute and relative paths. Paths are resolved relative to the `run`.
# Default is `.`
cwd: .
# Set the timezone for scheduling and logging. `auto` will identify and use the host timezone if `/:/host` is mounted, otherwise it will use the default timezone of the container..
# Other values are IANA timezones, e.g. `Europe/Amsterdam`, `America/New_York`, `Asia/Tokyo`, etc.
# See https://www.iana.org/time-zones for timezone database information.
# Default is `auto`
tz: auto
# The shell to use to parse the cmd. When in host/compose mode, this shell must exist on the host.
# Default is `/bin/bash -lc`
shell: /bin/bash -lc
# Maxmimum timeout for a single task to run before it is considered failed and retried (if retries are defined).
# Uses Go duration format: https://pkg.go.dev/time#ParseDuration
# e.g. `10s`, `1h20m3s`, `2h30m`
# Default is `0`
timeout: 5m
# Number of attempts after the first failed attempt. Mubst be a whole number. `-1` means infinite retries, `0` means no retries.
# Default is `0` - no retries.
retry: 3
# Backoff strategy for retries. Supported values: `fixed:<duration>` or `exp:<initial>:<max>:<factor>:<jitter>`.
# `fixed:<duration>` means the duration to wait before the next retry, same delay each time.
# `exp:<initial>:<max>:<factor>:<jitter>` means the duration to wait before the next retry, exponential growth.
# Duration values use Go format: https://pkg.go.dev/time#ParseDuration
# `initial` is the initial duration to wait before the first retry.
# `max` is the maximum duration to wait before the retry.
# `factor` is the factor to multiply the duration by after each retry.
# `jitter` is the jitter to add to the duration, as a percentage of the duration.
# delay = min(max, initial * factor^retry * (1 + jitter * random(0, 1)))
# Default is `exp:10s:5m:2:0.1`
backoff: exp:10s:5m:2:0.1
# Notification policy for the task.
# Supported values:
# - `never` - no notifications
# - `fail` - notifications when a task fails
# - `retry` - notifications when a task is retried and fails
# - `always` - notifications when a task is successful, retried or fails
# - `success` - notifications when a task is successful (either first attempt or after retries)
# Default is `fail`
notify: fail
# Apprise URL to send notifications to. Must be a valid Apprise URL or empty and will otherwise throw an error.
# See https://github.com/caronc/apprise/wiki for supported URLs.
# Default is empty - meaning no notifications will be sent
notify_url: mailto://user:pass@example.com
# When to attach the log of the stdout/stderr file as a file upload to the notification
# Supported values: `never`, `fail`, `always`
# Default is `fail`
notify_attach_log: fail
# Same as `backoff`, but for notifications that could not be delivered
# Supported values: `fixed:<duration>` or `exp:<initial>:<max>:<factor>:<jitter>`
# Duration values use Go format: https://pkg.go.dev/time#ParseDuration
# Default is `exp:10s:10m:2:0.1`
notify_backoff: exp:10s:10m:2:0.1
# Same as `retry`, but for notifications that could not be delivered
# Supported values: `-1` for infinite retries, `0` for no retries
# Default is `-1` - infinite retries
notify_retry: -1
# A task named `update` that runs every hour
# `schedule` is required for all tasks and cannot be defined globally
# Supported formats:
# `HH:MM` - Run at the given time every day, e.g. `02:00` meaning 2:00 AM or `14:00` meaning 2:00 PM
# `HH:MM-HH:MM` - Run once between the given times, e.g. `02:00-04:00` meaning 2:00 AM to 4:00 AM, a random time will be picked.
# `<duration>` - Go duration format: https://pkg.go.dev/time#ParseDuration, e.g. `1h`, `1h30m`, `1h30m30s`, `30s`
# `<cron expression>` - Same format as `cron`, e.g. `0 14 * * 0` for 2:00 PM on Sundays
update.schedule: 02:00-04:00 # Randomly run once between 02:00 and 04:00
# The command to run when a task is scheduled
# The command will be parsed by the `shell`. Shell parsing depends on the `shell` specified. Path is relative to `cwd`.
# `cmd` is required for all tasks and cannot be defined globally
update.cmd: docker compose up -d --build --pull always
# Example override, overrides `retry` for this task only.
update.retry: 0
dtask-backup-example:
image: ghcr.io/kristianvld/dtask
restart: unless-stopped
volumes:
- ./backups:/backups
- ./app:/app:ro
environment:
notify_url: mailto://user:pass@example.com
# Task: incremental backup every hour
incremental.schedule: 1h
incremental.timeout: 10m
incremental.retry: 1
incremental.notify_attach_log: fail
# Default image has busybox installed, so we can use `tar` and `date` commands.
incremental.cmd: tar czf /backups/incr-$(date +%F-%H%M%S).tar.gz --listed-incremental=/backups/backup.snar /app/data
# Task: full backup every Sunday at 2:00 AM
full.schedule: 0 2 * * 0
full.timeout: 30m
full.retry: 3
full.notify: always
full.notify_attach_log: fail
# Full backup with --listed-incremental new file to reset the base
full.cmd: tar czf /backups/full-$(date +%F-%H%M%S).tar.gz --listed-incremental=/backups/backup.snar.new /app/data && mv /backups/backup.snar.new /backups/backup.snarConfiguration Model
Key Notation
Use these key formats in environment:
- Root-scoped option:
<key> - Task-scoped option:
<task>.<key> - Task names (
<task>) must match^[a-z0-9_]+$
Example:
retry: 3stack_update.retry: 0hourly_backup.notify: always
Invalid task names (for example Stack-Update.schedule or my.task.cmd) fail startup validation.
Precedence
Configuration is applied in this order:
- hardcoded defaults in
dtask - global options (
<key>) - task options (
<task>.<key>)
Task options override globals for that task only.
Required Task Keys
Each task must define:
<task>.schedule<task>.cmd
At least one task must be defined. A service with no <task>.schedule + <task>.cmd pairs is invalid.
Environment Validation
dtask validates container environment keys at startup and fails fast on invalid configuration.
- unknown keys are rejected
- non-
dtaskkeys are only allowed for this fixed allowlist:PATHHOSTNAMEHOMEPWDTERMSHLVL_
This strict mode avoids silently ignored typos such as retrry=3 or notifyy_url=....
Runtime Semantics
- no overlap: if a task is already running when its next schedule tick fires, that tick is skipped
- no catch-up: after restart, dtask schedules from "now" and does not backfill missed runs
- startup emits one
startup_completelog line with task count and notification sender state - startup emits one
task_scheduledlog line per task with run mode, user, timezone, cwd, schedule, and next run timestamp
Compose Variable Interpolation
dtask config is plain Compose environment values, so Docker Compose interpolation works as usual.
- You can reference shell or
.envvalues with${VAR}. - You can provide defaults with
${VAR:-default}. - You can fail fast on missing values with
${VAR:?message}.
This is useful for:
- reusing shared values across multiple options or tasks
- keeping secrets (tokens/webhooks) out of committed compose files
Important:
- interpolation sources are shell environment and
.env, not other keys in the same Composeenvironmentblock.
Example .env:
dotenv
DTASK_RUN=compose
DTASK_TZ=Europe/Amsterdam
DAILY_WINDOW=02:00-04:00
DTASK_NOTIFY_URL=mailto://user:pass@example.comExample:
yaml
services:
dtask:
image: ghcr.io/kristianvld/dtask
volumes:
- /:/host
environment:
run: ${DTASK_RUN:-compose}
tz: ${DTASK_TZ:-auto}
update.schedule: ${DAILY_WINDOW}
update.cmd: docker compose up -d --build --pull always
cleanup.schedule: ${DAILY_WINDOW}
cleanup.cmd: docker image prune -af
# Secret sourced from shell/.env
notify_url: ${DTASK_NOTIFY_URL:-}Option Index
Use this section to jump directly to any option.
All options follow one model.
| Option | Scope | Required | Default | Purpose |
|---|---|---|---|---|
run | global or task | no | container | execution context |
user | global or task | no | empty | execution user (chroot --userspec for host/compose; numeric uid[:gid] for container) |
cwd | global or task | no | . | working directory |
tz | global or task | no | auto | timezone for scheduling and logging |
shell | global or task | no | /bin/bash -lc | shell used to execute task.cmd |
timeout | global or task | no | 0 | max runtime per attempt |
retry | global or task | no | 0 | task retry attempts |
backoff | global or task | no | exp:10s:5m:2:0.1 | retry delay strategy |
notify | global or task | no | fail | notification policy |
notify_url | global or task | no | empty | Apprise target URL |
notify_attach_log | global or task | no | fail | log attachment policy |
notify_backoff | global or task | no | exp:10s:10m:2:0.1 | notification delivery backoff |
notify_retry | global or task | no | -1 | notification delivery retries |
task.schedule | task only | yes | none | trigger schedule |
task.cmd | task only | yes | none | command to execute |
Options
Options can be declared in two scopes:
- global scope:
<key> - task scope:
<task>.<key>
Override model:
- task-scoped values (
<task>.<key>) override global values (<key>) for that task. - all options in this section support both scopes unless explicitly marked task-only.
task.scheduleandtask.cmdare required and task-only.
Example snippets in this section only show the relevant keys. ... means omitted lines.
run
- Default:
container - Allowed values:
container,host,compose - Behavior:
- selects where the command process is executed and where binaries/files are resolved from.
container: command runs inside the dtask container context.host: command runs in host context via the/hostmount using chroot semantics, with relative paths resolved from host root (/).compose: command runs in host context via the/hostmount, with relative paths resolved from the compose stack directory.- when
useris configured:host/compose: command runs as that host user viachroot --userspec.container: command runs with that Linux credential (uid[:gid]).
- stdout/stderr are streamed to the dtask container logs in all modes.
- Validation:
run=hostandrun=composerequire/:/hostmount.run=hostandrun=composerequire running as root in the container (chroot).run=composeauto-detects the compose stack directory from Docker container metadata labelcom.docker.compose.project.working_dir; unresolved compose directories fail startup.- on Docker Desktop (macOS/Windows),
host/composecan behave unexpectedly because they depend on Linux host semantics; validate carefully or preferrun=container. - invalid enum values fail configuration.
Example:
The following example configures all tasks to run in compose mode, so host binaries are used and relative paths are anchored to the compose stack directory. The lint task is an explicit exception and runs inside the dtask container.
yaml
# ...
environment:
# ...
run: compose
user: 1000:1000
# ...
lint.run: containeruser
- Default: empty (default process user for the selected run mode)
- Type:
run=host/run=compose: Linuxchroot --userspecvalue (for example1000,1000:1000,backup,backup:backup)run=container: numericuidoruid:gid(for example1000,1000:1000)
- Behavior:
run=host/run=compose: dtask passes this value through aschroot --userspec=<value>.run=container: dtask executes the command with the provided numeric Linux credential.
- Validation:
- empty values fail configuration.
- whitespace in the value fails configuration.
- for
run=container, non-numeric users fail startup (must beuidoruid:gid).
Example:
The following example runs host tasks with named users and a container task as numeric UID/GID.
yaml
# ...
environment:
run: host
backup.user: 1000:1000
backup.schedule: 6h
backup.cmd: tar czf /var/backups/app-$(date +%F-%H%M%S).tar.gz /srv/app
weekly.user: backup
weekly.schedule: 0 2 * * 0
weekly.cmd: /usr/local/bin/weekly-maintenance
lint.run: container
lint.user: 1000:1000
lint.schedule: 1h
lint.cmd: go test ./...cwd
- Default:
. - Type: path string
- Behavior:
- sets the working directory for command execution.
- absolute path: used directly.
- relative path resolution depends on
run:run=container: relative to container root (/)run=host: relative to host root (/)run=compose: relative to compose stack directory on the host
- Validation:
- if the resolved working directory does not exist or is not accessible, task execution fails.
Example:
The following example uses one dtask service and demonstrates both global and task-level options. Global defaults are run: container and cwd: /app, so tasks inherit container execution in a bind-mounted app directory. host_cleanup overrides execution to run: host with an absolute host path (/var/backups), while stack_update overrides to run: compose with cwd: ., which resolves to the compose stack directory on the host.
yaml
services:
dtask:
image: ghcr.io/kristianvld/dtask
volumes:
- /:/host
- ./app:/app:ro
- ./backups:/backups
environment:
run: container
cwd: /app
integrity_check.schedule: 1h
integrity_check.cmd: ls -la
app_snapshot.schedule: 6h
app_snapshot.cwd: /backups
app_snapshot.cmd: tar czf app-$(date +%F-%H%M%S).tar.gz /app
host_cleanup.run: host
host_cleanup.cwd: /var/backups
host_cleanup.schedule: 1d
host_cleanup.cmd: find . -type f -mtime +14 -delete
stack_update.run: compose
stack_update.cwd: .
stack_update.schedule: 02:00-04:00
stack_update.cmd: docker compose pull && docker compose up -dtz
- Default:
auto - Type:
autoor IANA timezone - Behavior:
- controls both schedule interpretation and log timestamp timezone.
auto: use host timezone when available (/:/hostmounted), otherwise container timezone.
- Validation:
- invalid timezone values fail configuration.
Example:
The following example auto-detects timezone for scheduling and log timestamps on all tasks. The weekly_report task is pinned to Europe/Amsterdam, so its schedule is evaluated in that timezone even if host/container timezone differs.
yaml
# ...
environment:
# ...
tz: auto
# ...
weekly_report.tz: Europe/Amsterdamshell
- Default:
/bin/bash -lc - Type: shell command string
- Behavior:
task.cmdis executed through this shell.- shell parsing rules apply to the command string (quoting, pipes, operators).
- in
run=hostorrun=compose, this shell path must exist on host.
- Validation:
- invalid/empty shell values fail configuration.
- if the shell binary is missing in the selected execution context, task execution fails.
Example:
The following example uses Bash as the default command parser, which is useful when tasks rely on Bash features. The cleanup task overrides this and uses /bin/sh -lc for POSIX shell behavior.
yaml
# ...
environment:
# ...
shell: /bin/bash -lc
# ...
cleanup.shell: /bin/sh -lctimeout
- Default:
0 - Type: Go duration
- Behavior:
- max runtime for one attempt (including the initial run and each retry).
0disables timeout.- on timeout, the attempt is marked failed and normal retry/backoff policy applies.
- Validation:
- invalid duration values fail configuration.
- negative durations fail configuration.
Example:
The following example enforces a 10-minute runtime limit for all task attempts, so hanging tasks fail and enter normal retry handling. The full_backup task overrides this with a 2h30m limit because that workflow is expected to run longer.
yaml
# ...
environment:
# ...
timeout: 10m
# ...
full_backup.timeout: 2h30mretry
- Default:
0 - Type: integer
- Behavior:
- number of additional attempts after the first failed attempt.
- effective max attempts are
1 + retry(except-1, which is unbounded). -1: infinite retries.0: no retries.
- Validation:
- must be an integer.
- values below
-1fail configuration.
Example:
The following example retries failed tasks up to 3 additional times by default (max 4 attempts total). The network_check task overrides this and retries indefinitely, which is useful for eventually-available dependencies.
yaml
# ...
environment:
# ...
retry: 3
# ...
network_check.retry: -1backoff
- Default:
exp:10s:5m:2:0.1 - Type: strategy string
- Behavior:
- controls the delay between a failed attempt and the next retry.
- applies only when a retry is going to happen.
- this same format is used by
notify_backofffor notification delivery retries.
Allowed formats:
fixed:<duration>where<duration>is Go durationexp:<initial>:<max>:<factor>:<jitter>initialandmaxare Go durationsfactoris the exponential multiplier per retry (for example2)jitteris random spread from0tojitter(for example0.1for 10%)
Formula:
text
delay = min(max, initial * factor^attempt * (1 + jitter * random(0, 1)))- Validation:
- invalid format strings fail configuration.
- invalid duration parts fail configuration.
- invalid
factor/jittervalues fail configuration.
The following example configures all tasks to use a fixed 20-second retry delay, giving a predictable retry cadence. The db_migration task overrides this and uses exponential backoff that starts at 10 seconds, doubles each retry, adds random 0 to 10% jitter, and caps at 5 minutes to reduce repeated load during persistent failures.
Example:
yaml
# ...
environment:
# ...
backoff: fixed:20s
# ...
db_migration.backoff: exp:10s:5m:2:0.1notify
- Default:
fail - Allowed values:
never,fail,retry,success,always - Behavior:
- notifications are emitted after an attempt finishes (never before start).
never: no notifications.fail: one notification when the task reaches terminal failure (after retries are exhausted).retry: one notification after each failed attempt that will be retried, and one final failure notification if retries are exhausted.success: one notification when the task eventually succeeds (first attempt or after retries).always: same asretryfor failures, plus success notification when the task succeeds.
- Validation:
- invalid enum values fail configuration.
Attempt semantics example (retry: 3, command exits non-zero on all attempts):
never: 0 notificationsfail: 1 notification (after final failed attempt)retry: 4 notifications (3 retry failures + 1 terminal failure)success: 0 notifications (no successful attempt occurred)always: 4 notifications (same asretryin this always-failing scenario)
Example:
The following example sends notifications only when a task reaches terminal failure by default. The network_check task overrides this to notify only on successful runs, while backup notifies on all outcomes (retry/fail/success).
yaml
# ...
environment:
# ...
notify: fail
# ...
network_check.notify: success
backup.notify: alwaysnotify_url
- Default: empty
- Type: Apprise URL string
- Behavior:
- destination URL for notifications emitted by the
notifypolicy. - if empty, notifications are skipped regardless of
notifyvalue. - if set, it must be a valid Apprise URL.
- value is passed directly to Apprise;
dtaskdoes not rewrite or normalize it.
- destination URL for notifications emitted by the
- Validation:
- invalid URLs fail configuration.
- Notes:
- this can be set globally (
notify_url) or per task (task.notify_url). - dtask uses Python
appriseCLI inside the container to deliver notifications.
- this can be set globally (
Example:
The following example configures one default notification destination for all tasks. The backup task overrides notify_url, so backup notifications are routed to a separate destination.
yaml
# ...
environment:
# ...
notify_url: mailto://user:pass@example.com
# ...
backup.notify_url: discord://webhook_id/webhook_tokenThe following example routes notifications to an ops email channel by default. The weekly_full_backup task overrides the destination and routes only that task to Discord-compatible webhook syntax supported by Apprise.
yaml
# ...
environment:
# ...
# route specific tasks to different channels/services
notify_url: mailto://ops@example.com
# ...
weekly_full_backup.notify_url: discord://webhook_id/webhook_tokennotify_attach_log
- Default:
fail - Allowed values:
never,fail,always - Behavior:
- controls when stdout/stderr log is attached to an emitted notification.
- this option only affects emitted notifications; if
notify=nevernothing is sent. - attachment support is service-dependent in Apprise backend providers.
- Validation:
- invalid enum values fail configuration.
- if this option is
failoralways, configurednotify_urlproviders must support attachments or startup fails.
Example:
The following example attaches logs only for failed runs by default, which keeps successful notifications lighter. The backup task overrides this and always attaches logs for audit/debug visibility.
yaml
# ...
environment:
# ...
notify_attach_log: fail
# ...
backup.notify_attach_log: alwaysnotify_backoff
- Default:
exp:10s:10m:2:0.1 - Type: strategy string
- Behavior:
- retry delay strategy for failed notification deliveries.
- uses the same format as
backoff, but applies to notification delivery retries.
Allowed formats:
fixed:<duration>where<duration>is Go durationexp:<initial>:<max>:<factor>:<jitter>- Validation:
- invalid format strings fail configuration.
- invalid duration parts fail configuration.
- invalid
factor/jittervalues fail configuration.
The following example configures notification delivery retries for all tasks to use a fixed 15-second delay. The backup task overrides this and uses exponential delays that start at 10 seconds, double per retry, include random 0 to 10% jitter, and cap at 10 minutes.
Example:
yaml
# ...
environment:
# ...
notify_backoff: fixed:15s
# ...
backup.notify_backoff: exp:10s:10m:2:0.1notify_retry
- Default:
-1 - Type: integer
- Behavior:
- retry count for failed notification deliveries.
- independent from
retry(task execution retries). -1: infinite retries.0: no retries.
- Validation:
- must be an integer.
- values below
-1fail configuration.
Example:
The following example retries failed notification deliveries indefinitely by default, so transient outages do not drop alerts. The backup task overrides this and limits delivery retries to 3.
yaml
# ...
environment:
# ...
notify_retry: -1
# ...
backup.notify_retry: 3The following two options are required on every task.
task.schedule
- Default: none (required)
- Type: schedule string
- Behavior:
- defines when task is triggered.
- interpreted using
tz. - each task must define its own
schedule; there is no globalschedule. - if a previous run is still active, the next schedule tick is skipped (no overlap).
- supported formats:
HH:MMdaily fixed timeHH:MM-HH:MMdaily random window (one run per day in the window)- duration interval (for example
30s,1h,1h30m) using Go duration - 5-field cron expression (for example
0 2 * * 0)
- Validation:
- required per task.
- invalid schedule strings fail configuration.
Example:
The following example demonstrates three schedule modes together: a daily random window (stack_update), a fixed interval (hourly_backup), and a weekly cron schedule (sunday_full_backup).
yaml
# ...
environment:
# ...
stack_update.schedule: 02:00-04:00
hourly_backup.schedule: 1h
sunday_full_backup.schedule: 0 2 * * 0task.cmd
- Default: none (required)
- Type: command string
- Behavior:
- executed through configured
shell. - evaluated in context defined by
runandcwd. - should contain the full command to execute for that task.
- exit code determines task result (
0success, non-zero failure/retry path).
- executed through configured
- Validation:
- required per task.
- empty command values fail configuration.
Example:
The following example shows task.cmd in realistic contexts. integrity_check uses the default run: container with a mounted /app directory. stack_update explicitly switches to run: compose for Docker Compose commands in the stack directory, and host_script switches to run: host to execute a host-installed Bun script from an absolute host path.
yaml
services:
dtask:
image: ghcr.io/kristianvld/dtask
volumes:
- /:/host
- ./app:/app:ro
environment:
run: container
cwd: /app
integrity_check.schedule: 1h
integrity_check.cmd: ls -la
stack_update.run: compose
stack_update.schedule: 02:00-04:00
stack_update.cmd: docker compose pull && docker compose up -d
host_script.run: host
host_script.cwd: /srv/myapp
host_script.schedule: 1h
host_script.cmd: bun run scripts/cleanup.tsAdditional Examples
Host Stack Update at Night
yaml
services:
dtask:
image: ghcr.io/kristianvld/dtask
restart: unless-stopped
volumes:
- /:/host
environment:
run: compose
stack_update.schedule: 02:00-04:00
stack_update.cmd: docker compose up -d --build --pull alwaysContainer Backup Every Hour
yaml
services:
dtask:
image: ghcr.io/kristianvld/dtask
restart: unless-stopped
volumes:
- ./backups:/backups
- ./app:/app:ro
environment:
notify_url: mailto://user:pass@example.com
hourly_backup.schedule: 1h
hourly_backup.timeout: 10m
hourly_backup.retry: 1
hourly_backup.notify_attach_log: fail
hourly_backup.cmd: tar czf /backups/incr-$(date +%F-%H%M%S).tar.gz --listed-incremental=/backups/backup.snar /app/data
weekly_full_backup.schedule: 0 2 * * 0
weekly_full_backup.timeout: 30m
weekly_full_backup.retry: 3
weekly_full_backup.notify: always
weekly_full_backup.notify_attach_log: fail
weekly_full_backup.cmd: tar czf /backups/full-$(date +%F-%H%M%S).tar.gz --listed-incremental=/backups/backup.snar.new /app/data && mv /backups/backup.snar.new /backups/backup.snar