Backup Script Not Working

Hi, I am trying to get a usb stick to backup my none red folder .node-red , but my script doesn't work saying that it can't create folder permission denied, I know it is a permissions problem can anyone help please ?

This is my Script:

#!/bin/bash
SOURCE="$HOME/.node-red"
DEST="/mnt/usb_backup/node-red-backups"
DATE=$(date +"%Y-%m-%d")


mkdir -p "$DEST"

rsync  -av --delete "$SOURCE/" "$DEST/latest/"

tar -czf"%DEST/node-red-$DATE.tar.gz" -C "$HOME" .node-red

Thanks

Does it make a difference if you use the absolute path rather than $HOME/.node-red?

1 Like

Hi Jbudd,

I have followed a guide from the internet, so I am not sure what you mean by that.

Thanks

An absolute path is one which starts with /.
So eg /home/stu/.node-red rather than $HOME/.node-red.
However I don't think this is the cause of a problem here.

Why does your script contain two seperate backup commands, one using tar and the other rsync?

Show us the exact text of the error message.

1 Like

You need to check whether your system has actually mounted the USB drive to /mnt/usb_backup. Issue a ls -la /mnt/usb_backup command to see whether it exists and what permissions it has. Most likely, your USB drive is not mounted at that location.

Also, that simplistic script is very wasteful since you are backing up the whole node_modules folder which is rarely needed since it is easily re-created. It will take a long time to back up the 10's of thousands of files (typically) in that folder.

1 Like

This is a script I use for backing up Node-red, which might be useful.

The script is defined within Node-red and not stored anywhere else.
With this script, the use of absolute pathnames in the inject node might cause some difficulties when you come to restore from the backup!

It uses tar -czf $BACKUPTO --numeric-owner --exclude=node_modules* to exclude the node_modules directory.
But note that tar is a very old fashioned archiving utility, it's name is from "tape archiver" from when computers used tape drives for storage.

If I wanted to move the backup file to another drive or NAS, I'd do so as a separate command in the script after creating the backup.

[{"id":"923cd1917835b2a2","type":"group","z":"7f20ad1a9174e987","name":"Backup Node-red","style":{"label":true},"nodes":["f854b7672172886b","40ec46521782eb90","cf19faaabdfb2dbe","fb47ca1393ddc5aa","3c4d7bf52fe7f7f6"],"x":14,"y":19,"w":852,"h":142},{"id":"f854b7672172886b","type":"inject","z":"7f20ad1a9174e987","g":"923cd1917835b2a2","name":"Node-red location & backup target","props":[{"p":"nodereddirectory","v":".node-red","vt":"str"},{"p":"backupfilename","v":"\"node-red-\" & $moment().format(\"YYYY-MM-DD\") & \".tar.gz\"","vt":"jsonata"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":200,"y":80,"wires":[["40ec46521782eb90"]]},{"id":"40ec46521782eb90","type":"template","z":"7f20ad1a9174e987","g":"923cd1917835b2a2","name":"Backup script","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"# To override defaults pass these in as msg properties NB Bash does not see these mustaches\nNODEREDDIR={{{nodereddirectory}}}   \nBACKUPTO={{{backupfilename}}}\n\n# Default location\nif [ -z $NODEREDDIR ]\nthen\n   NODEREDDIR=.node-red\nfi\n\n# Default backup file\nif [ -z $BACKUPTO ]\nthen\n   BACKUPTO=nodered.tar.gz\nfi\n\n# Don't overwrite backupfile\n#if [ -s \"$BACKUPTO\" ]\n#then\n#  echo \"Error: $BACKUPTO already exists\" >&2\n#  exit 1\n#fi\n\nARCHIVER=\"tar -czf $BACKUPTO --numeric-owner --exclude=node_modules*\"\n$($ARCHIVER $NODEREDDIR)   # Do the backup\n\nCOUNTFILES=\"$(tar -tvf $BACKUPTO | wc -l)\"\nFILESIZE=\"$(du -h $BACKUPTO | sed -e 's/\\s.*//')\"\nprintf \"%s files %s in %s\" $COUNTFILES $FILESIZE $BACKUPTO","output":"str","x":440,"y":80,"wires":[["cf19faaabdfb2dbe"]]},{"id":"cf19faaabdfb2dbe","type":"exec","z":"7f20ad1a9174e987","g":"923cd1917835b2a2","command":"","addpay":"payload","append":"","useSpawn":"false","timer":"","winHide":false,"oldrc":false,"name":"","x":590,"y":80,"wires":[["fb47ca1393ddc5aa"],["3c4d7bf52fe7f7f6"],[]]},{"id":"fb47ca1393ddc5aa","type":"debug","z":"7f20ad1a9174e987","g":"923cd1917835b2a2","name":"Backup stats","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":750,"y":60,"wires":[]},{"id":"3c4d7bf52fe7f7f6","type":"debug","z":"7f20ad1a9174e987","g":"923cd1917835b2a2","name":"Errors","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":730,"y":120,"wires":[]}]

ps No backup strategy is valid unless you have tested it and practised restoring from the backup!

1 Like

Hi Totally,

I think I made the usb drive auto mount, I restarted the pi and issued the ls -la command and I got this:

total 28
drwxr-xr-x 4 root root  4096 Jan 19 12:32 .
drwxr-xr-x 3 root root  4096 Jan 19 12:01 ..
drwx------ 2 root root 16384 Jan 19 11:58 lost+found
drwxr-xr-x 2 root root  4096 Jan 19 12:32 node-red-backups

so I think I have it auto booting,

after what you said though, I think I need to follow a different guide to backup all files except the node modules

Thanks

Thanks Jbudd,

I was going to ask is there not a way to get node red to do it, I think this might be an easier way to do it for me, going to have a look now

Thanks

So it is mounted but look at the ownership, everything is owned by root which is the superuser. So at the moment, changing your script isn't going to help. You need to change the mount permissions so that the user running Node-RED has access.

2 Likes

Hi Tottaly,

Yes I am not sure how I would go about doing that

Thanks

Not a Node-RED issue I'm afraid. You will need to look up instructions for your OS. AI may help.

1 Like

OK thanks I will try and find a good Raspberry Pi Forum

Thanks

Good luck with that! :face_with_peeking_eye:

1 Like

That doesn't sound good, are there none ?

Thanks

https://forums.raspberrypi.com/ is fine but it contains a shed-load of out of date advice and sometimes answers are not clearly explained.

1 Like

Thanks Jbudd,

I will have a look tonight

Thanks

Just search for the answer - it is easy enough to find.

1 Like

Hi Jbudd,

after a morning of going backwards and forwards with chat gpt I have a flow that works when I click on Backup now, and it does actually backup and send me a success email, chat gpt's version sent 1 success email and 4 emails saying that it wasn't successful, but I am not sure how to test it if it truly is unsuccessful, could you tell from looking at the flow ?

[
    {
        "id": "b69d691090d14131",
        "type": "function",
        "z": "d5abf393d3c8d4ce",
        "name": "Start Backup",
        "func": "return msg;",
        "outputs": 1,
        "x": 390,
        "y": 540,
        "wires": [
            [
                "def8a7864c6b3f12"
            ]
        ]
    },
    {
        "id": "def8a7864c6b3f12",
        "type": "exec",
        "z": "d5abf393d3c8d4ce",
        "command": "mount | grep -q /mnt/usb_backup",
        "addpay": "",
        "append": "",
        "useSpawn": "false",
        "timer": "",
        "winHide": false,
        "name": "Check USB Mounted",
        "x": 650,
        "y": 540,
        "wires": [
            [
                "87247002d94b14b8"
            ],
            [],
            []
        ]
    },
    {
        "id": "87247002d94b14b8",
        "type": "exec",
        "z": "d5abf393d3c8d4ce",
        "command": "touch /mnt/usb_backup/.nr_test && rm /mnt/usb_backup/.nr_test",
        "addpay": "",
        "append": "",
        "useSpawn": "false",
        "timer": "",
        "winHide": false,
        "name": "Check USB Writable",
        "x": 930,
        "y": 540,
        "wires": [
            [
                "be6b4f11a33ac7ce"
            ],
            [],
            []
        ]
    },
    {
        "id": "be6b4f11a33ac7ce",
        "type": "function",
        "z": "d5abf393d3c8d4ce",
        "name": "Build Backup Command",
        "func": "const date = new Date().toISOString().slice(0,10);\nmsg.payload = `mkdir -p /mnt/usb_backup/node-red-backups && tar -czf /mnt/usb_backup/node-red-backups/node-red-${date}.tar.gz --exclude=node_modules -C /home/pi/.node-red .`;\nreturn msg;",
        "outputs": 1,
        "x": 1210,
        "y": 540,
        "wires": [
            [
                "e2bcdee2f6db52af"
            ]
        ]
    },
    {
        "id": "e2bcdee2f6db52af",
        "type": "exec",
        "z": "d5abf393d3c8d4ce",
        "command": "",
        "addpay": true,
        "useSpawn": false,
        "name": "Create Backup",
        "x": 1450,
        "y": 540,
        "wires": [
            [
                "5ac99bb92372b7e3"
            ],
            [],
            []
        ]
    },
    {
        "id": "5ac99bb92372b7e3",
        "type": "exec",
        "z": "d5abf393d3c8d4ce",
        "command": "find /mnt/usb_backup/node-red-backups -name 'node-red-*.tar.gz' -mtime +28 -delete",
        "addpay": "",
        "append": "",
        "useSpawn": "false",
        "timer": "",
        "winHide": false,
        "name": "Cleanup Old Backups",
        "x": 1710,
        "y": 540,
        "wires": [
            [
                "a23e5b1b0bd8f77c"
            ],
            [
                "c9dbe409740e626f"
            ],
            []
        ]
    },
    {
        "id": "a23e5b1b0bd8f77c",
        "type": "function",
        "z": "d5abf393d3c8d4ce",
        "name": "Build Success Email",
        "func": "const now = new Date().toLocaleString();\nmsg.topic = \"System update\";\nmsg.payload = `Hello,\\n\\nYour scheduled Node Red backup completed successfully.\\n\\nDate: ${now}\\n\\nKind regards Savio Sensors`;\nreturn msg;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 2000,
        "y": 520,
        "wires": [
            []
        ]
    },
    {
        "id": "c9dbe409740e626f",
        "type": "function",
        "z": "d5abf393d3c8d4ce",
        "name": "Build Error Email",
        "func": "// Ignore empty or harmless stderr messages\nif (!msg.payload || msg.payload.toString().trim() === \"\") {\n    return null;\n}\n\nmsg.topic = \"System notification\";\nmsg.payload = `Node-RED backup FAILED.\\n\\nError details:\\n${msg.payload}`;\nreturn msg;\n",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 2010,
        "y": 820,
        "wires": [
            []
        ]
    },
    {
        "id": "84cd95aaa671dcae",
        "type": "inject",
        "z": "d5abf393d3c8d4ce",
        "name": "â–¶ Backup Now",
        "props": [
            {
                "p": "payload",
                "v": "manual",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "x": 150,
        "y": 470,
        "wires": [
            [
                "b69d691090d14131"
            ]
        ]
    },
    {
        "id": "5e1441ae0a972d88",
        "type": "inject",
        "z": "d5abf393d3c8d4ce",
        "name": "⏰ Weekly Backup",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "weekly",
        "payloadType": "str",
        "x": 180,
        "y": 570,
        "wires": [
            [
                "b69d691090d14131"
            ]
        ]
    }
]

Thanks

I looked at the flow.

I find this function

and this exec node
touch /mnt/usb_backup/.nr_test && rm /mnt/usb_backup/.nr_test
to be of dubious value.

I find myself pondering the validity of chatgpt's assumption that an error at an exec node results in no message passed onward, and that file globbing in an exec node will be correctly interpreted by the shell.

It is utterly different from anything I would write, and indeed different from the backup flow I use and which I shared here.

Perhaps it's just a difference of style?

Hi Jbudd,
I am presuming that you think it is nonsense as I did after telling it that it was full of mistakes and spitting error messages out all morning, I wanted to use your example but I don't know how to change it to my drive name etc, does it use variables for that ? if you can help me achieve it I would be grateful

Thanks