A reliable Bash script for managing Docker Compose stack image updates.
Inspired by pullio.
- Scans all running Docker containers associated with Compose projects.
- Pulls newer images only when available.
- Recreates the container and its entire dependency tree (using
depends_on,links, and shared networks) with the--always-recreate-depsflag. - Warns via webhook if an image is older than the configured age limit, even if no new image is available.
- Prioritizes per-container labels over global environment variables for configuration.
- Reports fatal script errors instantly via webhook to prevent silent failures.
curl -fsSL https://codeberg.org/karan/stackrefresh/raw/branch/main/stackrefresh.sh -o /usr/local/bin/stackrefresh
chmod +x /usr/local/bin/stackrefreshSet global fallbacks using environment variables (e.g., in /etc/environment or an inline export).
# Example: Enable updates, notifications, and set a global age limit
GLOBAL_UPDATE=true
GLOBAL_NOTIFY=true
GLOBAL_MAX_AGE_DAYS=14
GLOBAL_DISCORD_WEBHOOK="https://discord.com/api/webhooks/..."Schedule the script using cron to run daily at 4:00 AM, logging all output to a file and preventing silent errors.
# /etc/cron.d/stackrefresh
# Runs at 4 AM daily. 2>&1 redirects STDOUT and STDERR to the tee command,
# which appends (-a) output to the log file.
0 4 * * * root /usr/local/bin/stackrefresh 2>&1 | tee -a /var/log/stackrefresh.logIf the script encounters a fatal error, it will send a webhook notification before exiting.
Labels are placed on the service definition in your docker-compose.yml and always override global variables.
| Label | Example | Purpose |
|---|---|---|
io.goel.stackrefresh.update |
true |
Allows StackRefresh to recreate this container and its dependencies. |
io.goel.stackrefresh.notify |
true |
Sends web-hook notifications for any change or age warning. |
io.goel.stackrefresh.max_age_days |
21 |
Warns if the current image is older than 21 days. |
io.goel.stackrefresh.registry.authfile |
/secure/auth.json |
Path to private registry credentials (e.g., config.json). |
io.goel.stackrefresh.discord.webhook |
https://discord.com/... |
Container-specific Discord end-point. |
io.goel.stackrefresh.generic.webhook |
https://ntfy.sh/mytopic |
Container-specific generic POST destination. |
These variables are used only if the corresponding container label is absent or empty.
| Variable | Default (Script) | Description |
|---|---|---|
HOSTNAME |
(System Value) | (Read-Only) The system hostname, included in all webhooks. |
GLOBAL_UPDATE |
true |
Globally allows container recreation. |
GLOBAL_NOTIFY |
true |
Globally allows web-hook notifications. |
GLOBAL_MAX_AGE_DAYS |
90 |
Default age threshold for warnings (0 = ignore). |
GLOBAL_DISCORD_WEBHOOK |
(Empty) | Default Discord URL. |
GLOBAL_GENERIC_WEBHOOK |
(Empty) | Default generic POST URL. |
GLOBAL_REGISTRY_AUTHFILE |
(Empty) | Default auth file path. |
GLOBAL_AUTHOR_AVATAR |
(Empty) | Icon URL used in web-hooks. |
GLOBAL_AUTHOR_URL |
(Empty) | Author link used in web-hooks. |
When a new image is found for a service configured for updates, StackRefresh executes a Compose command that ensures all related services are refreshed:
docker compose up -d --always-recreate-deps- The
--always-recreate-depsflag ensures that any dependent service (depends_on,links) is also stopped and recreated using its newest image, guaranteeing dependency compatibility across the stack. - Volumes are preserved; only the containers are replaced.
Use the --debug flag to see all decisions, scanned variables, age checks, and the exact docker compose commands executed without changing script behavior:
stackrefresh --debug 2>&1 | less