Self-hosted service
mcuadros/ofelia avatar
mcuadros/ofelia

mcuadros/ofelia: a Docker job scheduler driven by container labels

A docker job scheduler (aka. crontab for docker)

3,999 stars209 forksGoMIT

At a glance

What is it?
Ofelia runs cron-style jobs inside running containers, in throwaway containers, on the host, or as swarm run-once services. It is a small Go binary aimed at people who already run Docker and want scheduling to live next to the workloads.
Who is it for?
Adopt Ofelia if your scheduled work already lives in containers and you want the schedule declared as labels on those containers, filtered per compose project. Do not adopt it if you need a scheduler with a web UI, job dependency graphs, or a stable non-beta release line, since the newest published releases are v0.4.0 beta builds.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 14 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Ofelia replaces, and for whom

The README frames the project as a replacement for Vixie's cron, arguing that cron is not extensible and is hard to debug when something goes wrong. That is the pitch, and it points at a specific audience: teams already running Docker who want scheduled commands to execute inside containers rather than on the host. The four job types map onto four different intents. job-exec runs a command inside an already running container. job-run starts a new container from a given image, runs the command, and destroys it at the end. job-local runs the command on the host running Ofelia. job-service-run creates a run-once service for swarm deployments. If none of those four shapes describes your work, Ofelia is probably not the tool you want. If your nightly job is a script that must run inside the same container as your application, job-exec is the one that matters, and it is the feature the README calls the main one: Ofelia emulates docker exec through Docker's API.

How the scheduler reaches into containers

Ofelia is a Go program that talks to the Docker daemon. For job-exec it calls the equivalent of docker exec against a running container, so the process runs inside that container's namespace with its filesystem and environment. For job-run it creates a container from the specified image and removes it after the command finishes. Scheduling uses the Go cron library, and the README points at robfig/cron/v3 for the expression format, so @every 10s and 0 1 * * * both work. There is a version trap worth knowing about. robfig/cron v1 accepted a leading seconds field, and Ofelia 0.3.x still requires it, while starting with 0.4.x the seconds field is optional and the README says it is not recommended for new configurations. Copying a schedule between a 0.3.x image and a 0.4.x image can therefore shift when the job fires. Configuration comes from two sources. An INI file is passed with ofelia daemon --config=/path/to/config.ini. Alternatively, Ofelia reads Docker labels, which requires access to the Docker socket. The label format is ofelia.<JOB_TYPE>.<JOB_NAME>.<JOB_PARAMETER>=<PARAMETER_VALUE>, and the README states that this form supports everything the INI files can express. Label discovery scans all containers by default; the --docker-filter flag, or -f, narrows that the same way docker ps filters work.

Installing Ofelia and scheduling your first job

The published image is mcuadros/ofelia, and the repository Dockerfile sets the entrypoint to tini followed by the ofelia binary, with a default command of daemon --config /etc/ofelia/config.ini. The quickest path is the label-driven mode from the README: start a target container with ofelia.enabled=true plus a job-exec label, then start Ofelia with daemon --docker. The target container needs the extra ofelia.enabled label for job-exec to be picked up, which is easy to miss on a first attempt.

bash
docker run -it --rm \
    --label ofelia.enabled=true \
    --label ofelia.job-exec.test-exec-job.schedule="@every 5s" \
    --label ofelia.job-exec.test-exec-job.command="uname -a" \
        nginx

With that container running, start Ofelia itself. The socket is mounted read-only in the README example, and the labels on the Ofelia container define a local job that runs date every five seconds.

bash
docker run -it --rm \
    -v /var/run/docker.sock:/var/run/docker.sock:ro \
    --label ofelia.job-local.my-test-job.schedule="@every 5s" \
    --label ofelia.job-local.my-test-job.command="date" \
        mcuadros/ofelia:latest daemon --docker

The README says this pair of containers produces two jobs: the local date job and the exec job running uname -a inside nginx. The same setup in compose form adds a depends_on between the Ofelia service and nginx. If you want Ofelia to see only the containers of the current compose project, the README shows a filter on the compose project label:

bash
daemon --docker -f label=com.docker.compose.project=${COMPOSE_PROJECT_NAME}

For an INI-based setup, the equivalent is a single job-exec block passed with the config flag. The README example writes a file into a running container named my-container once an hour.

ini
[job-exec "job-executed-on-running-container"]
schedule = @hourly
container = my-container
command = touch /tmp/example

Where label-driven configuration gets awkward

Reading every container's labels to build the schedule is convenient until the daemon hosts more than one workload. The default is to read labels of all Docker containers, so without --docker-filter an Ofelia instance can pick up jobs belonging to unrelated projects on the same host. The compose project filter shown in the README is the intended answer, and it depends on com.docker.compose.project being present, which it will not be for containers started outside compose. There is a second constraint in the same area: label configuration needs access to the Docker socket, and the README's examples mount it read-only. A read-only socket still exposes the daemon's API surface to the Ofelia container, so the security posture of the scheduler container is the posture of the Docker socket itself. The README does not document a rollback path for a job that fails mid-run, nor does it describe retry behaviour. If a scheduled command exits non-zero, the visible feedback is whatever Ofelia logs; the README does not describe a retry policy, so do not assume one. Finally, the newest releases listed are v0.4.0 beta builds, with the most recent stable line being 0.3.x, which is the tag the README refers to as latest in the seconds-field note. Teams that will not run beta software are effectively on the older syntax.

Ofelia against a plain cron container

The obvious alternative is a containerized cron: an image bundling crond plus your scripts, running as a long-lived service. The difference is where the schedule lives. A cron container can only run what is inside it, or reach into other containers through the Docker CLI if you install it and mount the socket anyway. Ofelia inverts that: the schedule is metadata on the workload, and the execution happens in the workload's own container via the Docker API. That means no second copy of your application's runtime, and no need to keep a cron image in sync with the image it is supposed to run inside. The cost is a hard dependency on the Docker daemon being reachable and on the labels being correct. A cron container keeps working if the Docker socket is unavailable; an Ofelia job-exec cannot run at all in that situation. Ofelia also supports connecting to a different daemon through DOCKER_HOST, DOCKER_TLS_VERIFY, DOCKER_CERT_PATH and DOCKER_API_VERSION, which the README lists for socket proxies, remote daemons and Docker over TCP. A cron container has no equivalent concept, because it does not talk to the daemon to do its work.

Maintenance, licence and upgrade cost

The repository is MIT licensed, which permits commercial use and modification with the usual requirement to keep the licence text; that is a description of the licence, not legal advice. The last push to the default branch was on 2026-09-18, so the repository is being touched, though the release list shows only beta tags in the 0.4.0 line and nothing newer than v0.4.0-beta.5 from 2026-06-07. The build is Go: go.mod declares go 1.25.0 with toolchain go1.26.6, and the Dockerfile builds from golang:1.26-alpine and ships on alpine:3.24 with ca-certificates, tini and tzdata. That is a small runtime footprint, and it also means the toolchain floor moves with the project. The upgrade cost concentrates in the cron syntax change: a config written for 0.3.x carries a leading seconds field that 0.4.x treats as optional, so every schedule should be re-read after a version bump rather than copied. The Makefile builds packages for darwin and linux on amd64, which is what the repository's own packaging targets cover.

Editorial conclusion

Adopt Ofelia if your scheduled work already lives in containers and you want the schedule declared as labels on those containers, filtered per compose project. Do not adopt it if you need a scheduler with a web UI, job dependency graphs, or a stable non-beta release line, since the newest published releases are v0.4.0 beta builds. Before committing, verify that your Ofelia image tag matches your cron syntax (the seconds field is required in 0.3.x and optional in 0.4.x), and confirm the Docker socket permissions your setup grants, because label-based configuration requires access to the Docker socket.

Frequently asked questions

What is the meaning of Ofelia?

The question is about the name rather than the software. In this repository the name is used for a job scheduler for Docker environments, described in its README as a replacement for the old fashioned cron.

What is Ofelia?

It is a job scheduler for Docker environments, written in Go. Its main feature is executing commands directly on Docker containers through the Docker API, emulating the behaviour of docker exec.

What is Ofelia Plads?

That is a place name and has nothing to do with this project. The only Ofelia discussed here is mcuadros/ofelia, the Docker job scheduler.

Official sources

  1. Issues
  2. License: MIT
  3. mcuadros/ofelia on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mcuadros-ofelia.svg)](https://hysenlabs.com/projects/mcuadros-ofelia)