(very simple) auto update bash script for paperless-ngx docker-compose

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.