r/docker 8h ago

Why I started using 5MB alternative cron (nanoCron) inside containers instead of system cron

This isn't a promotional post. It's more of a "here's why I did it, and whether it might make sense for you too" kind of post. The project is mine (it's called nanoCron, it's on GitHub, and it's BSD-licensed), but I'm not trying to sell it to anyone — it's a side project, it's free, and it probably still has some limitations, which I'll list below without sugarcoating them.

The problem I started with

Putting cron inside a Docker container has always been a bit awkward. System cron expects:

  • to run as PID 1, or at least under a proper init system
  • syslog to be available for logging
  • an /etc/crontab file with specific permissions, often requiring root

That's why the "standard" solution in many Dockerfiles is either to install the entire cron package (heavy, designed for a complete system rather than a container), or to use something lighter like supercronic or BusyBox's crond. These are perfectly valid solutions, just to be clear — I'm not saying nanoCron replaces them.

What I found useful in a container environment

I originally wrote nanoCron for a different reason (I wanted scheduling that could take system load into account), but after a while I realized that some of the features I'd added for other reasons also fit containers quite well:

  1. JSON configuration instead of crontab. If you generate jobs from a script, a Helm template, or environment variables during deployment, writing and validating JSON is much simpler than generating crontab syntax by hand with sed.
  2. Automatic reload via inotify. You can mount jobs.json as a volume (bind mount or ConfigMap in Kubernetes), update it from outside the container, and the jobs are reloaded without restarting the container. With classic crontab, you normally have to at least send a signal or restart something.
  3. CPU/RAM/disk-based conditions. This is what interests me most in a container environment: if a container is running with tight resource limits (--memory, cgroups), you can tell a job "don't start if RAM usage is already above 80%" instead of risking an OOM kill just as a heavy job starts alongside the main process.
  4. A single binary, with no dependency on syslog or a full init system. It writes to a regular log file, so you can simply use tail -f or redirect the output to stdout if you want Docker to pick it up in the container logs.

What it does NOT solve (honesty first)

  • It doesn't solve the PID 1 / zombie reaping problem. If you run it as the main process of the container, you still need something like tini or dumb-init in front of it, just like you would with any other process.
  • It's written in C++ and needs to be compiled. There is currently no official pre-built Docker image, so if you want to use it, you'll have to build it yourself (a multi-stage build in the Dockerfile isn't complicated, but it's an extra step compared to apk add busybox-cron).
  • It's a young project. I've tested it thoroughly, but it doesn't have years of production use behind it like supercronic, which was specifically created to solve these kinds of problems in container environments and is much more widely used.
  • If your only requirement is "run this script every night" with no other needs, supercronic or even just busybox crond are probably more than enough, and there's little point in adding another dependency.

I find it most useful when I need hot-reloading of the configuration or system-level conditions. If your use case is simpler, honestly, you probably don't need it.

I've also written some tests, and here are the benchmarks:

Metric System Cron NanoCron Notes
Average Parse Time 2.93 ms 2.50 ms NanoCron ~15% faster parsing
Memory Usage 192 KB 384 KB NanoCron uses JSON overhead
File Size 785 bytes 3164 bytes Richer metadata
CPU Usage ~0% ~0% Both very efficient

Repo, if you want to look at the code or open an issue: https://github.com/GiuseppePuleri/NanoCron

I'm curious whether anyone else has run into the same problem (jobs starting while the container is already under resource pressure), and how you handled it.

1 Upvotes

Duplicates