CHECKING STATUS
I AM LISTENING TO
|

Day 40:  Ofelia – Your Docker Cron Job Buddy – 7 Days of Docker

26. October 2025
.SHARE

Table of Contents

So you’re running Docker containers and need to schedule some tasks? Let me introduce you to Ofelia – think of it as cron’s cooler, Docker-savvy cousin. Built with Go, it’s lightweight and specifically made for containerized environments. In this guide, we’re going to set up Ofelia using Docker Compose with config files (not labels – we’ll talk about why in a sec).

What’s Ofelia All About?

Ofelia is a job scheduler for Docker that lets you run commands inside your containers, spin up temporary containers for quick jobs, or even run stuff on your host machine. Created by mcuadros, it basically wraps Docker’s API to work like docker exec and docker run, but on a schedule. Pretty neat, right?

What Can It Do?

  • Run commands inside your existing containers
  • Spin up brand new containers just for a task (then trash them when done)
  • Execute stuff directly on your host machine
  • Works with Docker Swarm if you’re into that
  • Uses familiar cron scheduling syntax (or handy shortcuts like @hourly)
  • Send notifications via email, save logs to files, or ping Slack
  • Prevent jobs from running on top of each other with the no-overlap feature

Why Config Files Over Labels?

Ofelia can use either labels (stuck right in your docker-compose.yml) or separate config files. Here’s why I prefer config files:

  1. Everything in One Spot: All your scheduled jobs live in one file instead of scattered across multiple container definitions
  2. Git-Friendly: Changes to schedules don’t mess with your docker-compose.yml, making version control cleaner
  3. Keep Things Separate: Your app containers don’t need to know anything about when they’re being scheduled
  4. Way Easier to Debug: Trust me, reading a config.ini is much nicer than hunting through YAML labels
  5. Manage Multiple Environments: Swap config files for dev/staging/prod without touching anything else

Let’s Set Up Docker Compose

Alright, time to get our hands dirty. Here’s a basic Docker Compose setup with Ofelia using a config file.

Basic Docker Compose Configuration

The Important Bits

  • Docker Socket: That /var/run/docker.sock mount is crucial – it’s how Ofelia talks to Docker
  • Config File: We’re mounting our config.ini to /etc/ofelia/config.ini inside the container
  • Read-Only Mounts: The :ro flag means read-only – good security practice
  • Network: Make sure Ofelia’s on the same network as the containers you want to schedule jobs on

How Config Files Work

The config file is just an INI file with sections for each job you want to run. Simple stuff.

The Four Job Types

Ofelia’s got four ways to run jobs:

  1. job-exec: Run commands in containers that are already running
  2. job-run: Spin up a new container, run something, then kill it
  3. job-local: Run commands right on your host machine
  4. job-service-run: For Docker Swarm folks (runs as a service)

Config File Examples (The Good Stuff)

Example 1: Basic Job-Exec

This is probably what you’ll use most – running stuff in containers that are already up and running.

Example 2: Job-Run

Need to spin up a fresh container just for a task? Job-run’s your friend.

Example 3: Job-Local

Sometimes you just need to run something on the host itself.

Example 4: Job-Service-Run

If you’re running Docker Swarm, here’s how you’d set up a service job.

Example 5: Kitchen Sink (Everything at Once)

Here’s what it looks like when you use all the bells and whistles.

What Options Can You Use?

Stuff That Works for All Job Types

  • schedule: Your cron expression or shortcuts like @hourly, @daily, @every 10s
  • no-overlap: Stops a job from running if it’s already running (default: false)

Job-Exec Options

  • container: Which container to run the command in (you gotta specify this)
  • command: What to actually run (also required)
  • user: Run as a specific user (optional)
  • tty: Allocate a pseudo-TTY if you need it (default: false)

Job-Run Options

  • image: Which Docker image to use (required)
  • command: What command to run
  • volume: Mount volumes (you can have multiple of these)
  • network: Which network to connect to
  • environment: Environment variables (multiple allowed)
  • delete: Clean up the container when done (default: true)
  • user: Run as a specific user
  • tty: Allocate a pseudo-TTY (default: false)

Job-Local Options

  • command: What to run on the host (required)
  • dir: Where to run it from (working directory)

Job-Service-Run Options

  • image: Docker image to use (required)
  • network: Swarm network to use (required)
  • command: What to execute
  • delete: Clean up the service after (default: true)

Schedule Formats (Cron Cheat Sheet)

Ofelia uses standard cron format, plus some nice shortcuts to make life easier:

Cool Advanced Stuff

Stop Jobs From Stepping on Each Other

Got a long-running job that might still be going when the next one tries to start? Use no-overlap to prevent that mess:

Passing Environment Variables

For job-run stuff, you can pass environment variables into your containers:

Note: Environment variables from the host can be referenced using ${VAR_NAME} syntax.

Mounting Volumes

Need to share files between your host and the container? Mount some volumes:

Volume format: host-path:container-path:mode where mode is ro (read-only) or rw (read-write).

Running Jobs on Multiple Containers

You can totally schedule different jobs on different containers:

Getting Notified When Stuff Happens

Ofelia’s got three ways to log things, all configured in the [global] section:

File Logging

Email Notifications

Slack Notifications

Full Working Example (Copy-Paste Ready)

Here’s everything put together – a complete setup you can actually use:

docker-compose.yml

ofelia/config.ini

Testing Before You Go Live

Don’t just deploy this straight to production – test it first:

1. Validate Config File Syntax: Start the containers and check logs:

2. Test Individual Jobs: Modify schedules to run more frequently during testing:

3. Check Job Execution: Monitor the logs to ensure jobs are executing:

4. Verify Job Output: Check that your jobs are producing expected results.

Additional Resources

Thoughts

Ofelia’s a solid choice for scheduling jobs in Docker. By using config files instead of labels, you keep your scheduling logic separate and your setup cleaner. Whether you’re backing up databases, clearing caches, or processing data, Ofelia’s got you covered.

The secret sauce is planning your schedules properly, testing everything in dev first, and keeping an eye on those logs. With the examples in this guide, you should be good to go. Happy scheduling!

FAQ

How do I run a cron job inside a Docker container?

To run a cron job inside a Docker container, you need to install cron in your Dockerfile, create a crontab file, and start the cron service. Here’s a basic example:

The crontab file should end with a newline (LF format, not CRLF) for proper functionality.

Why isn’t my cron job running in Docker?

Common reasons include: cron service not started (use service cron start or cron -f), incorrect file permissions (crontab files need 0644 permissions), missing newline at end of crontab file, CRLF line endings instead of LF (Windows vs Unix format), or the container exiting immediately because no foreground process is running.

How do I access environment variables in Docker cron jobs?

Cron doesn’t automatically pass environment variables. The most common solution is to export your environment variables to /etc/environment before starting cron:

Alternatively, you can source environment variables directly in your cron script by reading from /proc/1/environ.

What’s the difference between running cron inside a container vs. on the host?

Running cron inside the container keeps everything containerized and portable, but violates the “one process per container” principle. Running cron on the host system and executing Docker containers provides better resource management and easier monitoring. For example:

How do I keep my Docker container running with cron?

Docker containers need a foreground process to stay running. Use cron -f to run cron in the foreground, or combine cron with another command:

The tail -f command keeps the container alive by continuously reading the log file.

What are the file permission requirements for cron in Docker?

Crontab files in /etc/cron.d/ must be owned by root and have 0644 permissions. Scripts must be executable (chmod +x). Wrong ownership or permissions will cause cron to reject the file with errors like “WRONG FILE OWNER”.

How do I debug cron jobs in Docker containers?

Install rsyslog to get cron logs, use docker logs container_name to view output, redirect cron job output to a log file, and verify cron is running with service cron status. Run cron in foreground with debug flag for detailed output:

Why does my crontab file format matter in Docker?

Cron requires Unix line endings (LF), not Windows line endings (CRLF). Files created on Windows may have CRLF endings that break cron. Additionally, crontab files must end with an empty newline or cron will reject them with “Missing newline before EOF” error.

What’s the syntax difference for cron files in /etc/cron.d/ vs user crontabs?

Files in /etc/cron.d/ require a username field between the schedule and command, while user crontabs don’t:

Should I use Kubernetes CronJobs instead of Docker cron?

For production environments with Kubernetes, Kubernetes CronJobs are generally recommended. They provide better scheduling, scaling, monitoring, and integration with the Kubernetes ecosystem. However, for simple single-container deployments or development, Docker cron works fine.

How do I handle timezone issues with Docker cron?

Docker containers typically use UTC by default. To set a different timezone, link the appropriate timezone file:

Or set the TZ environment variable in your Dockerfile.

Why does Docker’s layered filesystem cause issues with cron?

Docker’s layered filesystem creates multiple hard links to files, and cron has a security policy rejecting files with hard link count greater than 1. The solution is to touch the crontab files before starting cron:

This creates new instances of the files, breaking the hard links.

What’s the best practice for logging cron output in Docker?

Redirect cron job output to stdout/stderr or a log file that you tail to keep the container running. For example:

Which base image should I use for Docker cron – Ubuntu or Alpine?

Ubuntu is more beginner-friendly with standard cron packages, while Alpine is lightweight but may require additional configuration. Alpine uses /var/spool/cron/crontabs/ instead of /etc/cron.d/ and has slightly different cron syntax (no username field in crontabs). Choose based on your size requirements and familiarity.

How do I run multiple services (cron + application) in one Docker container?

While it violates the one-process-per-container principle, you can run multiple services using a shell command:

This starts cron in the background and nginx in the foreground. Consider using supervisord for more complex multi-process setups, or better yet, separate containers for better maintainability.

Let’s Talk!

Looking for a reliable partner to bring your project to the next level? Whether it’s development, design, security, or ongoing support—I’d love to chat and see how I can help.

Get in touch,
and let’s create something amazing together!

RELATED POSTS

mosparo’s rule packages are a powerful way to protect your forms from spam without relying on external services. As an open-source spam protection solution, mosparo puts you in control of your form security through customizable rule sets that analyze submitted content in real-time. One of mosparo’s standout features is its rule package system, which not […]

YOURLS is a free, open-source set of PHP scripts that lets you run your own URL shortening service. Released under the MIT license, it gives you complete control over your links, detailed analytics, and the freedom to customize everything. The latest version, YOURLS 1.10.2, was released in July 2025 and comes with improvements to the […]

Or: How I Learned to Stop Worrying and Love the Underscore Remember when you could just tell your computer what to do, in plain English, and it would actually do it? No? Well, grab your DeLorean, because we’re going back to the future with _hyperscript (yes, that underscore is part of the name, and yes, […]

Alexander

I am a full-stack developer. My expertise include:

  • Server, Network and Hosting Environments
  • Data Modeling / Import / Export
  • Business Logic
  • API Layer / Action layer / MVC
  • User Interfaces
  • User Experience
  • Understand what the customer and the business needs


I have a deep passion for programming, design, and server architecture—each of these fuels my creativity, and I wouldn’t feel complete without them.

With a broad range of interests, I’m always exploring new technologies and expanding my knowledge wherever needed. The tech world evolves rapidly, and I love staying ahead by embracing the latest innovations.

Beyond technology, I value peace and surround myself with like-minded individuals.

I firmly believe in the principle: Help others, and help will find its way back to you when you need it.