This job is executed inside a running container, similar to docker exec.
schedule: string- When the job should be executed. E.g. every 10 seconds or every night at 1 AM.
command: string- Command you want to run inside the container.
container: string- Name of the container you want to execute the command in.
user: string =nobody- User as which the command should be executed, similar to
docker exec --user <user> - If not set, uses the global
default-user(defaultnobody); set todefaultto use the container's default user
- User as which the command should be executed, similar to
tty: boolean =false- Allocate a pseudo-tty, similar to
docker exec -t. See this Stack Overflow answer for more info.
- Allocate a pseudo-tty, similar to
console-height: uint =0- Initial pseudo-TTY console size in rows. Only honored by the Docker daemon when
tty = true; otherwise silently ignored.0means "use Docker's default size". Useful for jobs that render TUIs or formatted text that expects a specific terminal geometry (htop,vim, tables). Requires Docker API v1.42+ (Docker Engine 20.10+). See #235.
- Initial pseudo-TTY console size in rows. Only honored by the Docker daemon when
console-width: uint =0- Initial pseudo-TTY console size in columns. Same TTY-gating and Docker API floor as
console-height. Either field can be set independently; the daemon uses its default for any dimension left at0.
- Initial pseudo-TTY console size in columns. Same TTY-gating and Docker API floor as
environment- Environment variables you want to set in the running container. Note: only supported in Docker API v1.30 and above
- Same format as used with
-eflag withindocker run. For example:FOO=bar- INI config:
Environmentsetting can be provided multiple times for multiple environment variables. - Labels config: multiple environment variables has to be provided as JSON array:
["FOO=bar", "BAZ=qux"]
- INI config:
working-dir: string- Working directory for the command execution, similar to
docker exec --workdir <dir> - Backward compatibility: Requires Docker API v1.35+ (Docker Engine 17.09+). On older Docker versions, this parameter is silently ignored and the exec runs in the container's default working directory
- If not specified, uses the working directory defined in the container image
- Working directory for the command execution, similar to
privileged: boolean =false- Run the exec in privileged mode, similar to
docker exec --privileged
- Run the exec in privileged mode, similar to
no-overlap: boolean =false- Prevent that the job runs concurrently
history-limit: integer =10- Number of past executions kept in memory
run-on-startup: boolean =false- Run the job once immediately when the scheduler starts, before regular cron-based scheduling begins. Startup executions are dispatched in non-blocking goroutines so they do not delay scheduler startup.
[job-exec "flush-nginx-logs"]
schedule = @hourly
container = nginx-proxy
command = /bin/bash /flush-logs.sh
user = www-data
tty = false
[job-exec "backup-logs"]
schedule = @daily
container = web-app
command = tar czf /backups/logs.tar.gz .
working-dir = /var/logdocker run -it --rm \
--label ofelia.enabled=true \
--label ofelia.job-exec.flush-nginx-logs.schedule="@hourly" \
--label ofelia.job-exec.flush-nginx-logs.command="/bin/bash /flush-logs.sh" \
--label ofelia.job-exec.flush-nginx-logs.user="www-data" \
--label ofelia.job-exec.flush-nginx-logs.tty="false" \
nginx
docker run -it --rm \
--label ofelia.enabled=true \
--label ofelia.job-exec.backup-logs.schedule="@daily" \
--label ofelia.job-exec.backup-logs.command="tar czf /backups/logs.tar.gz ." \
--label ofelia.job-exec.backup-logs.working-dir="/var/log" \
web-appWhen specifying exec jobs via labels, Ofelia prefixes the job name to keep jobs from different containers with the same label from clashing. By default the prefix is the Docker Compose service name (falling back to the container name), so the example above yields nginx.flush-nginx-logs. This default is collision-prone when one daemon watches several Compose projects that reuse a service name; the [global] job-exec-label-scope option (service / container / container-service) selects a collision-safe scheme. See Cross-Container Job References for details.
This job can be used in 2 situations:
- To run a command inside of a new container, using a specific image, similar to
docker run - To start a stopped container, similar to
docker start
schedule: string (1, 2)- When the job should be executed. E.g. every 10 seconds or every night at 1 AM.
command: string = default container command (1)- Command you want to run inside the container.
image: string (1)- Image you want to use for the job.
- If left blank, Ofelia assumes you will specify a container to start (situation 2).
entrypoint: string (1)- Override the image entrypoint. Use an empty value to remove it.
user: string =nobody(1)- User as which the command should be executed, similar to
docker run --user <user> - If not set, uses the global
default-user(defaultnobody); set todefaultto use the container's default user
- User as which the command should be executed, similar to
network: string (1)- Connect the container to this network
hostname: string (1)- Define the hostname of the instantiated container, e.g.
test-server
- Define the hostname of the instantiated container, e.g.
container-name: string (1)- Name assigned to the created container. Defaults to the job name. If left empty, Docker will choose a random name.
delete: boolean =true(1)- Delete the container after the job is finished. Similar to
docker run --rm
- Delete the container after the job is finished. Similar to
stop-signal: string =""(image'sSTOPSIGNAL, falling back toSIGTERM) (1, 2)- Signal sent to the main process when Ofelia stops the container after execution. Accepts the canonical name (
SIGINT,SIGUSR1) or the bare suffix (INT,USR1). Useful for apps with signal handlers (Node.js →SIGINT, Java thread-dump →SIGQUIT, custom cleanup →SIGUSR1/SIGUSR2). Pair withstop-timeoutto give the application enough time to flush state beforeSIGKILL. Requires Docker API v1.42+ (Docker Engine 20.10+). See #234.
- Signal sent to the main process when Ofelia stops the container after execution. Accepts the canonical name (
stop-timeout: duration =0s(preserves the legacy 10s default incleanupOnDeadline) (1, 2)- Grace period between the stop signal (see
stop-signal) and the Docker daemon's escalation toSIGKILL. Honored only by Ofelia's deadline-cleanup path today (when a job exceeds its execution deadline); other shutdown paths inherit the daemon's default. Accepts a Go duration string (30s,2m,1m30s). Zero leaves the previous 10s default in effect so unconfigured jobs see no behavior change. See #234.
- Grace period between the stop signal (see
container: string (2)- Name of the container you want to start.
- Required field in case parameter
imageis not specified, no default.
tty: boolean =false(1, 2)- Allocate a pseudo-tty, similar to
docker exec -t. See this Stack Overflow answer for more info.
- Allocate a pseudo-tty, similar to
volume:- Mount host machine directory into container as a bind mount
- Same format as used with
-vflag withindocker run. For example:/tmp/test:/tmp/test:ro- INI config:
Volumesetting can be provided multiple times for multiple mounts. - Labels config: multiple mounts has to be provided as JSON array:
["/test/tmp:/test/tmp:ro", "/test/tmp:/test/tmp:rw"]
- INI config:
environment- Environment variables you want to set in the running container.
- Same format as used with
-eflag withindocker run. For example:FOO=bar- INI config:
Environmentsetting can be provided multiple times for multiple environment variables. - Labels config: multiple environment variables has to be provided as JSON array:
["FOO=bar", "BAZ=qux"]
- INI config:
working-dir: string (1)- Working directory inside the container, similar to
docker run --workdir <dir> - If not specified, uses the working directory defined in the container image
- Working directory inside the container, similar to
annotations- Container annotations for metadata tracking, audit trails, and observability. Unlike labels, annotations don't affect Docker behavior.
- Format:
key=valuestrings. For example:team=platform,cost-center=12345- INI config:
Annotationssetting can be provided multiple times for multiple annotations. - Labels config: multiple annotations must be provided as JSON array:
["team=platform", "env=prod"]
- INI config:
- Auto-populated annotations: Ofelia automatically adds metadata to all containers:
ofelia.job.name- The job nameofelia.job.type- Job type (run/service)ofelia.execution.time- Execution timestamp (RFC3339)ofelia.scheduler.host- Hostname running Ofeliaofelia.version- Ofelia version
- User annotations take precedence over auto-populated ones
- API requirement: Docker API 1.43+ (Docker Engine 20.10.9+). On older versions, annotations are silently ignored.
no-overlap: boolean =false- Prevent that the job runs concurrently
history-limit: integer =10- Number of past executions kept in memory
max-runtime: duration =24h- Maximum time the container is allowed to run before it is killed
run-on-startup: boolean =false- Run the job once immediately when the scheduler starts, before regular cron-based scheduling begins. Startup executions are dispatched in non-blocking goroutines so they do not delay scheduler startup.
[job-run "print-write-date"]
schedule = @every 5s
image = alpine:latest
command = sh -c 'date | tee -a /tmp/test/date'
volume = /tmp/test:/tmp/test:rw
environment = FOO=bar
environment = BAZ=qux
[job-run "backup-database"]
schedule = @daily
image = postgres:15
command = pg_dump mydb
annotations = team=platform
annotations = project=core-infra
annotations = environment=production
annotations = cost-center=12345Then you can check output in host machine file /tmp/test/date
docker run -it --rm \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--label ofelia.enabled=true \
--label ofelia.job-run.print-write-date.schedule="@every 5s" \
--label ofelia.job-run.print-write-date.image="alpine:latest" \
--label ofelia.job-run.print-write-date.volume="/tmp/test:/tmp/test:rw" \
--label ofelia.job-run.print-write-date.environment="FOO=bar" \
--label ofelia.job-run.print-write-date.command="sh -c 'date | tee -a /tmp/test/date'" \
ghcr.io/netresearch/ofelia:latest daemon
# Example with annotations for tracking and observability
docker run -it --rm \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--label ofelia.enabled=true \
--label ofelia.job-run.backup.schedule="@daily" \
--label ofelia.job-run.backup.image="postgres:15" \
--label ofelia.job-run.backup.command="pg_dump mydb" \
--label ofelia.job-run.backup.annotations='["team=platform", "project=core-infra", "env=prod"]' \
ghcr.io/netresearch/ofelia:latest daemonRuns the command on the host running Ofelia.
Note: In case Ofelia is running inside a container, the command is executed inside the container. Not on the Docker host.
schedule: string- When the job should be executed. E.g. every 10 seconds or every night at 1 AM.
command: string- Command you want to run on the host.
dir: string =$(pwd)- Base directory to execute the command.
environment- Environment variables you want to set for the executed command.
- Same format as used with
-eflag withindocker run. For example:FOO=bar- INI config:
Environmentsetting can be provided multiple times for multiple environment variables. - Labels config: multiple environment variables has to be provided as JSON array:
["FOO=bar", "BAZ=qux"]
- INI config:
no-overlap: boolean =false- Prevent that the job runs concurrently
history-limit: integer =10- Number of past executions kept in memory
run-on-startup: boolean =false- Run the job once immediately when the scheduler starts, before regular cron-based scheduling begins. Startup executions are dispatched in non-blocking goroutines so they do not delay scheduler startup.
[job-local "create-file"]
schedule = @every 15s
command = touch test.txt
dir = /tmp/This job can be used to:
- To run a command inside a new
run-onceservice, for running inside a swarm.
schedule: string (1, 2)- When the job should be executed. E.g. every 10 seconds or every night at 1 AM.
command: string = default container command (1, 2)- Command you want to run inside the container.
image: string (1)- Image you want to use for the job.
- If left blank, Ofelia assumes you will specify a container to start (situation 2).
network: string (1)- Connect the container to this network
delete: boolean =true(1)- Delete the container after the job is finished.
user: string =nobody(1, 2)- User as which the command should be executed.
- If not set, uses the global
default-user(defaultnobody); set todefaultto use the container's default user
tty: boolean =false(1, 2)- Allocate a pseudo-tty, similar to
docker exec -t. See this Stack Overflow answer for more info.
- Allocate a pseudo-tty, similar to
environment- Environment variables passed to the service container.
- Same format as used with
-eflag withindocker run. For example:FOO=bar- INI config:
Environmentsetting can be provided multiple times for multiple environment variables. - Labels config: multiple environment variables must be provided as JSON array:
["FOO=bar", "BAZ=qux"]
- INI config:
hostname: string- Hostname for the service container.
dir: string- Working directory inside the service container.
volume- Mount host directories or named volumes into the service container.
- Format:
source:target[:ro|rw]. For example:/host/path:/container/path:roormyvolume:/data- INI config:
Volumesetting can be provided multiple times for multiple mounts. - Labels config: multiple mounts must be provided as JSON array:
["/host:/container:ro", "data:/data"]
- INI config:
annotations- Service annotations for metadata tracking and observability. Stored as service labels in Docker Swarm.
- Format:
key=valuestrings. For example:team=platform,environment=staging- INI config:
Annotationssetting can be provided multiple times for multiple annotations. - Labels config: multiple annotations must be provided as JSON array:
["team=platform", "env=staging"]
- INI config:
- Auto-populated annotations: Ofelia automatically adds metadata:
ofelia.job.name- The job nameofelia.job.type- Always "service" for service-run jobsofelia.execution.time- Execution timestamp (RFC3339)ofelia.scheduler.host- Hostname running Ofeliaofelia.version- Ofelia version
- User annotations take precedence over auto-populated ones
no-overlap: boolean =false- Prevent that the job runs concurrently
history-limit: integer =10- Number of past executions kept in memory
max-runtime: duration =24h- Maximum time the service task may run before it is removed
run-on-startup: boolean =false- Run the job once immediately when the scheduler starts, before regular cron-based scheduling begins. Startup executions are dispatched in non-blocking goroutines so they do not delay scheduler startup.
[job-service-run "service-executed-on-new-container"]
schedule = 0,20,40 * * * *
image = ubuntu
network = swarm_network
command = touch /tmp/example
[job-service-run "swarm-backup"]
schedule = @daily
image = postgres:15
network = swarm_network
command = pg_dump mydb
environment = PGPASSWORD=secret
environment = PGHOST=db.internal
hostname = backup-worker
dir = /var/backups
volume = /backups:/backups:rw
annotations = team=data-platform
annotations = environment=staging
annotations = service-tier=backendThis job triggers commands via Docker Compose. Set exec = true to run commands
in an existing service container using docker compose exec. By default, a new
container is started with docker compose run --rm.
schedule: string- When the job should be executed. E.g. every 10 seconds or every night at 1 AM.
file: string- Path to the compose file, defaults to
compose.yml.
- Path to the compose file, defaults to
service: string- Service name to run or exec.
exec: boolean =false- Use
docker compose execinstead ofrun.
- Use
command: string- Command passed to the service (optional).
run-on-startup: boolean =false- Run the job once immediately when the scheduler starts, before regular cron-based scheduling begins. Startup executions are dispatched in non-blocking goroutines so they do not delay scheduler startup.
[job-compose "backup"]
schedule = @daily
file = docker-compose.yml
service = db
command = pg_dumpall -U postgres