I need to run a short-lived command repeatedly inside a Docker-compatible container. The first run should happen immediately, and subsequent runs should occur at a consistent interval measured from the previous run rather than on fixed clock boundaries. For example, a 25-minute interval should produce runs at approximately startup, +25 minutes, +50 minutes, and so on—not at :00, :25, and :50, where the gap across the hour is only 10 minutes.
Cron is not a great fit because its interval syntax is anchored to the clock, and it also makes it awkward to propagate stdout, stderr, exit statuses, and termination signals to a process supervisor. Systemd timers have the desired behavior but are generally unavailable inside Docker images. I can use a shell loop with `sleep`, but handling signals, process groups, logging, and failures makes the wrapper more complicated than I would prefer.
The command runs alongside a main service under supervisord, and the interval is configurable through an environment variable. Is there a small, portable executable or established pattern for repeatedly running a command at a consistent interval while forwarding signals and preserving its exit status?
5 Answers
A practical supervisord configuration is to run the command followed by a sleep, then let supervisord restart the short-lived program. For example:
`command=bash -c 'getmail --getmaildir /var/lib/getmail && sleep %(ENV_GETMAIL_POLL)sm'`
With `autorestart=true`, a successful command sleeps for the configured interval and then exits, causing the next run. If the command fails, the non-zero status is returned immediately instead of being hidden. Configure `stopasgroup=true` so stopping the supervisor service signals both the command and the sleep process, avoiding orphaned children. The service can also run as a non-root user through supervisord’s `user` setting.
A small scheduler library can do this, but it may be more infrastructure than you need. For example, a queue scheduler can use a fixed duration such as 25 minutes instead of clock-based scheduling, and the duration can be measured from container startup. The tradeoff is bringing in extra dependencies such as a worker queue and a data store for what is essentially one sequential command.
For a single task, a shell loop is probably the simplest option. If you want the command to run immediately and wait between successful runs, use the supervisor to manage the process group and keep the wrapper small. A service manager such as s6 can also help if you already use it, but it does not remove the need to define the recurring-task behavior somewhere.
Another approach is to schedule a frequent check, such as once per minute, and have a wrapper decide whether the real interval has elapsed. Store the last-run time in a file or use a tool that supports a timestamp file. This can approximate consistent intervals, but it still adds a wrapper, has polling overhead, and does not naturally solve process-group handling or exit-status propagation.
The `at` command can schedule the next invocation using a relative delay such as `now + 25 minutes`, which gives the desired timing. However, it requires an `at` daemon, complicates running as different users, and makes lifecycle management harder when the surrounding service stops. For a containerized service, keeping the task under the same supervisor is usually less complicated.

The loop itself is easy; the annoying parts are forwarding termination signals, preserving the child’s output, and making failures visible to the supervisor. That is why a dedicated helper binary would be nicer than another custom script.