Just like the title states. In my previous post I published a very very simple script for doing pull automatically for the docker-compose stuff of paperless-ngx. I modified this one a bit more. Maybe useful to you, too.
So first of all as a reminder, in the docker-compose.yml you can set the versions. I believe that it should(!) be safe to update minor versions. Like updating from 7.x.x 7.y.y. However, an update from 7.x.x to 8.x.x usually involves so many changes (at least for some specific software) that you should not or cannot do this automatically. PostgreSQL is a very good example. An update from 15.x to 16.x will not work because you need to update the PostgreSQL database itself (which involves a dump and import, or pg_upgrade, or similar). Even minor versions may sometimes lead to incompatible changes so you must take care. However. I’m doing the docker compose pull automatically for my paperless-ngx docker-compose setup with the following versions set in docker-compose.yml:
~# grep "image:" /opt/paperless/paperless-ngx/docker-compose.yml
image: docker.io/library/redis:latest
image: docker.io/library/postgres:16
image: ghcr.io/paperless-ngx/paperless-ngx:latest
image: docker.io/gotenberg/gotenberg:8
image: ghcr.io/paperless-ngx/tika:latest
If you still on postgres:15 you may want to check my previous article to see how to upgrade. However. The script which does the automatic pull I’ve also shown in that previous article:
~:/opt/paperless# cat update-paperless.sh
#!/bin/bash
set -e
# you may want to add sudo -Hu paperless in front of the commands
# if you can't run this as paperless user from your crontab.
PDIR=/opt/paperless/paperless-ngx
LOG=/opt/paperless/docker-compose-cron.log
DATE=$(date);
echo "$DATE Starting Docker Compose Update" >> $LOG
cd $PDIR
# stop all containers and pull if stopping was successful
/usr/bin/docker compose down >> $LOG 2>&1 && /usr/bin/docker compose pull >> $LOG 2>&1
# start all containers
/usr/bin/docker compose up --wait -d >> $LOG 2>&1
echo "$DATE Finished Docker Compose Update" >> $LOG
While this works fine (You spotted my mistake with $DATE? Because I define it at the start of the script it does not change. So the start of update and end of update will have the same date time – fixed at the end of this post)
Wed Feb 21 01:00:01 CET 2024 Starting Docker Compose Update
Container paperless-webserver-1 Stopping
Container paperless-webserver-1 Stopped
Container paperless-webserver-1 Removing
Container paperless-webserver-1 Removed
Container paperless-db-1 Stopping
Container paperless-broker-1 Stopping
Container paperless-gotenberg-1 Stopping
Container paperless-tika-1 Stopping
Container paperless-tika-1 Stopped
Container paperless-tika-1 Removing
Container paperless-tika-1 Removed
Container paperless-broker-1 Stopped
Container paperless-broker-1 Removing
Container paperless-broker-1 Removed
Container paperless-db-1 Stopped
Container paperless-db-1 Removing
Container paperless-db-1 Removed
Container paperless-gotenberg-1 Stopped
Container paperless-gotenberg-1 Removing
Container paperless-gotenberg-1 Removed
Network paperless_default Removing
Network paperless_default Removed
gotenberg Pulling
tika Pulling
db Pulling
broker Pulling
webserver Pulling
gotenberg Pulled
db Pulled
broker Pulled
webserver Pulled
tika Pulled
Network paperless_default Creating
Network paperless_default Created
Container paperless-tika-1 Creating
Container paperless-broker-1 Creating
Container paperless-db-1 Creating
Container paperless-gotenberg-1 Creating
Container paperless-tika-1 Created
Container paperless-broker-1 Created
Container paperless-db-1 Created
Container paperless-gotenberg-1 Created
Container paperless-webserver-1 Creating
Container paperless-webserver-1 Created
Container paperless-db-1 Starting
Container paperless-gotenberg-1 Starting
Container paperless-tika-1 Starting
Container paperless-broker-1 Starting
Container paperless-tika-1 Started
Container paperless-gotenberg-1 Started
Container paperless-broker-1 Started
Container paperless-db-1 Started
Container paperless-webserver-1 Starting
Container paperless-webserver-1 Started
Container paperless-gotenberg-1 Waiting
Container paperless-tika-1 Waiting
Container paperless-broker-1 Waiting
Container paperless-db-1 Waiting
Container paperless-webserver-1 Waiting
Container paperless-broker-1 Healthy
Container paperless-tika-1 Healthy
Container paperless-db-1 Healthy
Container paperless-gotenberg-1 Healthy
Container paperless-webserver-1 Healthy
Wed Feb 21 01:00:01 CET 2024 Finished Docker Compose Update
I disliked that I do not see the times at the docker compose lines. I just see the start and end. I did not find (maybe I’m blind) a docker compose command which would print date in front of those lines… A simple, maybe hacky solution is to use while read. Take the following example:
#!/bin/bash
echo "this is a program (yes Darling, it's a script)"
sleep 3
echo "which prints multiple lines"
sleep 10
echo "and we assume I cannot modify this program (script)"
echo "but I would like to add date time in front of each"
sleep 5
echo "output line..."
If you run this script (save it as example.sh, chmod +x it and ./example.sh it)
jean@penguin:~$ ./example.sh
this is a program (yes Darling, it's a script)
which prints multiple lines
and we assume I cannot modify this program (script)
but I would like to add date time in front of each
output line...
Then you can do so by using while read -r
jean@penguin:~$ ./example.sh | while read -r l; do d=`date`; echo "$d: $l"; done
Wed Feb 21 09:19:46 PM +01 2024: this is a program (yes Darling, it's a script)
Wed Feb 21 09:19:49 PM +01 2024: which prints multiple lines
Wed Feb 21 09:19:59 PM +01 2024: and we assume I cannot modify this program (script)
Wed Feb 21 09:19:59 PM +01 2024: but I would like to add date time in front of each
Wed Feb 21 09:20:04 PM +01 2024: output line...
The downside of this approach is that the return value $? will not be of the specific command and this might not be highly accurate / precise. But for this simple update script it will do.
First of all I add a function to my bash script which will do the while part:
wlog () {
local cmd="$@"
$cmd 2>&1 | while read -r l; do d=`date`; echo "$d: $l"; done
}
Then I just need to use wlog and the command. Here’s an example:
~:/opt/paperless# wlog () {
local cmd="$@"
$cmd 2>&1 | while read -r l; do d=`date`; echo "$d: $l"; done
}
root@paperless:/opt/paperless/paperless-ngx# wlog docker compose down
Wed Feb 21 21:29:45 CET 2024: Container paperless-webserver-1 Stopping
Wed Feb 21 21:29:53 CET 2024: Container paperless-webserver-1 Stopped
Wed Feb 21 21:29:53 CET 2024: Container paperless-webserver-1 Removing
Wed Feb 21 21:29:53 CET 2024: Container paperless-webserver-1 Removed
Wed Feb 21 21:29:53 CET 2024: Container paperless-broker-1 Stopping
Wed Feb 21 21:29:53 CET 2024: Container paperless-tika-1 Stopping
Wed Feb 21 21:29:53 CET 2024: Container paperless-db-1 Stopping
Wed Feb 21 21:29:53 CET 2024: Container paperless-gotenberg-1 Stopping
Wed Feb 21 21:29:53 CET 2024: Container paperless-tika-1 Stopped
Wed Feb 21 21:29:53 CET 2024: Container paperless-tika-1 Removing
Wed Feb 21 21:29:53 CET 2024: Container paperless-tika-1 Removed
Wed Feb 21 21:29:54 CET 2024: Container paperless-broker-1 Stopped
Wed Feb 21 21:29:54 CET 2024: Container paperless-broker-1 Removing
Wed Feb 21 21:29:54 CET 2024: Container paperless-broker-1 Removed
Wed Feb 21 21:29:54 CET 2024: Container paperless-db-1 Stopped
Wed Feb 21 21:29:54 CET 2024: Container paperless-db-1 Removing
Wed Feb 21 21:29:54 CET 2024: Container paperless-db-1 Removed
Wed Feb 21 21:30:03 CET 2024: Container paperless-gotenberg-1 Stopped
Wed Feb 21 21:30:03 CET 2024: Container paperless-gotenberg-1 Removing
Wed Feb 21 21:30:03 CET 2024: Container paperless-gotenberg-1 Removed
Wed Feb 21 21:30:03 CET 2024: Network paperless_default Removing
Wed Feb 21 21:30:04 CET 2024: Network paperless_default Removed
Turning my previos script into:
#!/bin/bash
set -e
PDIR=/opt/paperless/paperless-ngx
LOG=/opt/paperless/docker-compose-cron.log
DATE=$(date);
wlog () {
local cmd="$@"
$cmd 2>&1 | while read -r l; do d=`date`; echo "$d: $l"; done
}
echo "$DATE Starting Docker Compose Update" >> $LOG
cd $PDIR
# stop all containers and pull
wlog /usr/bin/docker compose down >> $LOG
wlog /usr/bin/docker compose pull >> $LOG
# start all containers
wlog /usr/bin/docker compose up --wait -d >> $LOG
echo "$DATE Finished Docker Compose Update" >> $LOG
You might have noticed that now pull will also run if docker compose down gave an error. If I check the return value it will be the return value of the function wlog. and the return value of the function wlog will be the return value of the while. It will not be the return value of $cmd. Check the following examples:
commandnotfound 2>&1 | while read -r l; do echo $?; done
0
# also echo $? now will return 0. Also if I do not
# use 2>&1 both results will be 0 (success)
~:/opt/paperless/paperless-ngx# commandnotfound
-bash: commandnotfound: command not found
~:/opt/paperless/paperless-ngx# echo $?
127
With Bash 4.2+ you just need to add:
shopt -s lastpipe
shopt -so pipefail
to the bash script. The first shell option will make the last segment of a pipe run in the current shell, the second shell option will make $? contain the exit code of the first failing command in the pipeline. I also fixed the $DATE mistake here in the following:
#!/bin/bash
set -e
shopt -s lastpipe
shopt -so pipefail
PDIR=/opt/paperless/paperless-ngx
LOG=/opt/paperless/docker-compose-cron.log
wlog () {
local cmd="$@"
$cmd 2>&1 | while read -r l; do d=`date`; echo "$d: $l"; done
}
wlog echo "Starting Docker Compose Update" >> $LOG
cd $PDIR
# stop all containers and pull if stopping was successful
wlog /usr/bin/docker compose down >> $LOG && wlog /usr/bin/docker compose pull >> $LOG
# start all containers
wlog /usr/bin/docker compose up --wait -d >> $LOG
wlog echo "Finished Docker Compose Update" >> $LOG
By the way, this is just playground for my personal / private stuff. And I also wrote this because this solution is not just useful for the docker compose part. It might be used for other programs / scripts / tools, too.
Anyway, if you’re here because you were looking for an easy way to keep docker images up2date… take a look at docker watchtower – it likely is a better solution for you.