Ofelia
A docker-friendly job scheduler written in Go
Ofelia is a modern, lightweight job scheduler for Docker environments, intended as a replacement for cron. It can run jobs inside containers, in new containers or on the host on a schedule.
Key features
- Cron replacement for Docker
- Run jobs in existing or new containers
- Configuration via labels or INI
- Slack and email notifications
Pros & cons
Strengths
- Very lightweight
- Docker-native scheduling
Trade-offs
- No web UI
Ofelia replaces
Last reviewed Sep 13, 2026 · 748 words
Ofelia exists to delete one specific file: the host crontab full of docker exec lines that nobody remembers writing. You put a schedule label on the container that owns the job, Ofelia watches the Docker socket, and the job runs inside that container on time. It is a 32 MB Go binary with no database and no web UI, and for the 90% of homelab scheduling that is "run this command in that container every night", it is the right size.
The label pattern is the whole product
Ofelia reads its configuration either from an INI file or from labels on other containers. Labels win in a Compose setup because the schedule lives next to the service it belongs to. A nightly database dump looks like this:
services:
ofelia:
image: mcuadros/ofelia:latest
command: daemon --docker
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
restart: unless-stopped
postgres:
image: postgres:16
labels:
ofelia.enabled: "true"
ofelia.job-exec.pgdump.schedule: "@daily"
ofelia.job-exec.pgdump.command: "sh -c 'pg_dumpall -U postgres > /backups/all.sql'"
Two things trip people on the first run. The target container needs ofelia.enabled=true or its other labels are ignored, and the --docker flag on the daemon is what turns label scanning on at all. The schedule syntax follows the Go cron library rather than classic crontab, so @daily, @hourly and @every 6h work, and a copied five-field line may not parse the way you expect. Check the README's examples before pasting one in.
Four job types cover the real cases
job-exec runs a command inside an already running container, which is the common one. job-run starts a fresh container from an image, runs the command, and removes it, which suits one-shot tools like a restic backup image you do not want idling all day. job-local runs on the host where the Ofelia daemon lives, and job-service-run targets Swarm services, which almost nobody outside Swarm will touch. The overlap guard (no-overlap: true) stops a slow job from stacking up behind itself, and I set it on anything that touches a database.
No web UI means you must monitor it yourself
This is the con in the catalogue and it is real. Ofelia will email or post to Slack when a job fails, and it can save each run's stdout to a folder with save-folder, but nothing shows you a calendar of what ran and whether it succeeded. The fix costs 5 minutes: append a curl to a Healthchecks ping URL at the end of each command and let that service tell you when a nightly job silently stopped firing. Without that, a mistyped label fails quietly and you find out when you need the backup.
The socket is root, treat it that way
Ofelia needs the Docker socket, and anything holding the socket can start privileged containers on the host. Mount it read-only as above, keep the Ofelia container off any network it does not need, and do not run it on a box where untrusted containers live. This is the same trade you already make with Watchtower or a Compose dashboard; it is fine on a single-owner homelab and worth a harder look on anything shared. The Compose patterns post covers how I isolate socket-holding services.
When a bigger scheduler is the safer pick
Ofelia stops being the answer around 20 to 30 jobs, or the moment more than one person needs to see what is scheduled. At that point you want history, retries and a screen: Cronicle gives you all three in a single Node.js process, and Kestra goes further into proper workflow orchestration with a much heavier footprint. For the broader field see the automation category. For jobs that need branching logic or webhooks, Ofelia is the wrong tool entirely; it runs commands, nothing more.
What I'd do
One Ofelia container per Docker host, socket mounted read-only, every job defined as labels in the same Compose file as its service. no-overlap on anything stateful, a Healthchecks ping on anything that matters, Slack notifications on failure. Keep it to the boring cron-shaped jobs; if you find yourself wanting a job graph, move to Cronicle rather than bending Ofelia. Run that way it has needed zero attention from me in years, which is the highest compliment a scheduler gets.
Compare Ofelia
7 head-to-head comparisons.
Similar automation & workflows apps
n8n
Automation & WorkflowsWorkflow automation with a node-based editor
Replaces Zapier, Make
Firecrawl
Automation & WorkflowsTurn websites into clean data for AI applications
Replaces Apify
Crawl4AI
Automation & WorkflowsOpen-source web crawler built for LLM data pipelines
Replaces Firecrawl
Huginn
Automation & WorkflowsAgents that monitor and act on your behalf
Replaces IFTTT, Zapier
Apache Airflow
Automation & WorkflowsProgrammatically author, schedule and monitor workflows
Replaces AWS Step Functions, Azure Data Factory
ToolJet
Automation & WorkflowsLow-code platform for building and automating business tools
Replaces Retool, Microsoft Power Apps