Pass container environment settings to cron jobs
Understand cron job environments and pass selected values without treating an unescaped environment dump as a shell script.
A cron job may receive a much smaller environment than the container's startup process. This is a cron implementation and job-launch behavior, not a Docker rule that environment variables exist only for PID 1. Child processes normally inherit their parent's environment, but cron can construct a separate job environment.
Start by checking the cron implementation, job user, and exact launch command. An interactive shell, a cron job, and docker exec need not see identical settings.
Transfer only the settings the job needs
Do not blindly run printenv > /etc/environment and source the result. Values can contain spaces, quotes, newlines, or shell syntax; an environment listing is not safely quoted shell code. /etc/environment also has platform-specific parsing rules and is not a portable export script.
One explicit pattern is to serialize an allowlist as data. This example assumes a Debian-style image with Python 3 and cron installed, a root-run job, and a writable /run. In the entrypoint, before starting cron:
#!/bin/sh
set -eu
umask 077
python3 - <<'PYENV'
import json
import os
keys = ("DB_HOST", "DB_USER", "DB_PASS")
values = {key: os.environ[key] for key in keys if key in os.environ}
with open("/run/backup-env.json", "w", encoding="utf-8") as file:
json.dump(values, file)
PYENV
exec cron -f
Store this as /app/run-backup.py in the image:
import json
import os
with open("/run/backup-env.json", encoding="utf-8") as file:
settings = json.load(file)
os.environ.update(settings)
os.execv("/app/backup.sh", ["/app/backup.sh"])
The backup script must be executable with a valid interpreter. A root-owned /etc/cron.d/backup file, mode 0644 and ending in a newline, can contain:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 3 * * * root /usr/bin/python3 /app/run-backup.py >> /var/log/backup.log 2>&1
Use the actual Python path in the image. A per-user crontab omits the root field. For a non-root job, arrange narrow file ownership and readability explicitly; mode 0600 owned by root would prevent that user reading the settings.
Simpler alternatives
A few fixed, nonsecret values can be declared at the top of the crontab, such as DB_HOST=mysql, or supplied through /usr/bin/env DB_HOST=mysql /app/backup.sh. Cron syntax has its own quoting and percent-character rules, so keep complex commands in scripts.
An application scheduler can use the application's existing environment. Kubernetes CronJobs can receive environment and mounted Secrets through their workload specification. A host scheduler can invoke docker exec myapp /app/backup.sh, provided the container is running and the scheduler can reach its daemon.
FAQ
What environment does docker exec use?
It starts from the container's creation-time environment, with optional -e overrides. It does not read arbitrary environment changes made later inside PID 1 or another shell.
Does cron load my shell profile?
Do not rely on login-shell profiles. Some cron/PAM combinations load additional environment files, but that behavior must be verified for the image.
How should secrets and failures be handled?
Pass only necessary values, restrict file access, avoid logging secrets, and prefer a supported secret delivery mechanism. Record job name, completion, outcome, and duration. If collected into Guance, configure log monitoring with a window covering the schedule and allowed delay, and test both an explicit failure and a missing run.
See Debian crontab semantics and Docker exec environment settings.
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan