A database password written in crontab is a time bomb — once the file leaks, all assets are exposed. This episode teaches storing secrets in an env file with 600 permissions or a secret manager like Vault, plus script discipline: set -euo pipefail, absolute paths, and input validation.

In episode 13 we limited who may schedule and with what privileges. Now we discuss the contents of the job itself: its scripts and secrets.
A far-too-common scenario: crontab containing pg_dump -U root -p Sekret123. Now imagine that crontab gets backed up to a repository, shared on pastebin, or seen by a user who shouldn't — the password leaks, and because the job runs periodically, the leak goes undetected until it's too late. This episode closes that gap.
/var/spool/cron/crontabs/ — some backup tools copy them too.The golden rule: crontab contains only schedules; secrets live elsewhere.
Store secrets in a separate file only readable by the owning user:
RESTIC_PASSWORD=very-strong-secret
PGPASSWORD=db-secret
DB_HOST=localhostchmod 600 /home/deploy/.backup.env
chown deploy:deploy /home/deploy/.backup.envLoad it from the script:
#!/bin/bash
set -a
source /home/deploy/.backup.env
set +a
pg_dump -h "$DB_HOST" -U backup_user -Fc mydb > /backup/mydb.dumpset -a exports all sourced variables so they can be inherited by child processes (like pg_dump).
Warning
chmod 600 only protects against other users — it doesn't protect against root, and it doesn't protect against processes running as the same user. It's a basic control, not a substitute for a secret manager. Combine it with a dedicated user (episode 13) and consider Vault for more sensitive secrets.
For larger organizations, a secret manager like HashiCorp Vault or OpenBao is the answer:
#!/bin/bash
export PGPASSWORD=$(vault kv get -field=password secret/db/backup)
pg_dump -h localhost -U backup_user -Fc mydb > /backup/mydb.dumpAdvantages: centralized rotation, an audit log of who accessed which secret, and no text files piling up on disk. Disadvantages: you need a Vault token that must also be guarded, plus additional infrastructure.
Three bash options that change a script from "may fail silently" to "fails clearly":
#!/bin/bash
set -euo pipefail-e — exit immediately when a command returns a non-0 exit code.-u — error when using an unset variable (catches typos).-o pipefail — a pipeline is considered failed if any of its stages fails, not just the last one.Without -o pipefail, a pg_dump | gzip whose pg_dump fails is still considered successful because gzip returns 0.
Remember the lesson from episode 6: don't rely on PATH. Use absolute paths or build PATH inside the script:
#!/bin/bash
set -euo pipefail
export PATH=/usr/local/bin:/usr/bin:/bin
LOGFILE=/var/log/backup.log
exec > >(tee -a "$LOGFILE") 2>&1Scripts that accept arguments must validate them before doing work:
#!/bin/bash
set -euo pipefail
TARGET="${1:?Argumen TARGET wajib diisi}"
if [[ ! -d "$TARGET" ]]; then
echo "ERROR: $TARGET bukan direktori" >&2
exit 1
fi${1:?message} immediately exits with a message if the argument is empty — preventing the script from working with a wrong value.
Note
The combination of set -euo pipefail + validation + absolute paths doesn't make a script error-proof — but it turns hidden errors into visible errors that can be logged and alerted on (episode 12). Visibility is a prerequisite of reliability.
| Practice | Prevents | Tool |
|---|---|---|
| No hardcoded secrets | Credential leaks | Env file 600 / Vault |
chmod 600 | Reading by other users | File permissions |
set -euo pipefail | Hidden failures | Bash options |
| Absolute paths | command not found | Script |
| Input validation | Damage from wrong arguments | Guard clauses |
Key takeaways:
set -euo pipefail makes failures visible.In episode 15 we'll cover cron in containers and Kubernetes CronJob — why crond inside a container is an anti-pattern, how to replace it with an entrypoint loop or a K8s CronJob, and the anatomy of a batch/v1 manifest with schedule, concurrencyPolicy, and TTL!