Is it possible to run a Python script so it doesn't exit if Node-RED stops?

Simple as that. I have a Python script that I run from an exec node with the command

python3 myscript.py

But that stops if Node-RED exits. I want to keep it running no matter what. I assume that's possible, but how?

Perhaps it would be better to run it separately from node-red. If you need to communicate between it and node red then you could use MQTT for example.

There was a thread a while back on the opposite problem. That was scripts not stopping when closed by exec. I think if you call it via a small shell script then we may close the shell script but not the child app called by it. (Or I may be mis-remembering)

1 Like

@Colin Thanks, I do communicate with the script with MQTT, but not with Node-RED. It communicates with EventGhost on a Windows VM to control that Node-RED and HomeAssistant are running when they should. But I need it to be started by Node-RED and then be possible to both start and stop from Node-RED but at the same time be able to run if Node-RED is killed or exited because something locks up (which happens once or twice a week in my busy system).

@dceejay Thanks, I tried to do that both by running the script directly from a starter script and as a subprocess. But that doesn't work, unfortunately. I actually thought that using

subprocess.Popen(['python3', 'myscript.py'])

should do the trick, but it just finishes the first script and then runs the second script, and if I close the command line window (or Node-RED, I believe those work exactly the same way, so if I managed one, then I manage the other) it will kill the subprocess script. This was a bit more difficult then I thought...

Edit: I thought I found it, and now I'm confused. Because it seems like command line and Node-RED start isn't the same after all. This works in command line:

os.system('nohup python3 myscript.py')

It will keep running if I close the command window. But if I start it with the exec node in Node-RED, the process will close when Node-RED exits. Which is what happens if I start in a command window and then use CTRL+C instead of closing the command window. Weird...

Perhaps you could modify the script so that it runs permanently but doesn't do anything until instructed to do so (via MQTT possibly). Similarly it could be disabled via MQTT, but is still left running, waiting to be re-enabled. That is a very common pattern (though not usually MQTT) with system services sitting around doing nothing, waiting to be given something to do.

Thanks, but I think I just found a solution: Running it as a systemd service seems to let me control it from Node-RED, and not enabling the service means that it doesn't start on boot. :slight_smile:

1 Like

How are you starting the service from node-red?

Startup trigger with:

sudo systemctl start myscript.service

OK, presumably you have setup the system so as not to require a password for the node-red user to do that.

Yes, the Pi is inaccessible for anybody but me, both physically and on the network. Separate network segment that only goes to a server that I have physical control over, behind a pfSense firewall. :slight_smile:

1 Like

Could you post the contents of your myscript.service (to save others having to work out what to do?)

I'm showing the full procedure, in case somebody's as green with Linux as I am. :wink:

sudo mousepad /usr/lib/systemd/user/myscript.service

[Unit]
Description=My script for doing something
After=multi-user.target

[Service]
Type=simple
ExecStart=/usr/bin/python3 /home/pi/myscript.py > /home/pi/myscript.log 2>&1

[Install]
WantedBy=multi-user.target

(the > /home/pi and so on is to direct output to a log file).

sudo systemctl daemon-reload

I didn't do this next line, but if you want it to start automatically on boot, do it:

systemctl enable myscript.service --user

Then I tested the service:

systemctl start myscript.service --user
systemctl status myscript.service --user

Then you can run an excec node in Node-RED with this:

systemctl restart myscript.service --user

On more thing: If the script is dependant on other stuff (like my script was on Paho MQTT), you need to install those for the user you're running the script for, or the script running in a systemd service won't find them.

I believe that was all. :smiley:

3 Likes

This is generally a bad idea as it is much better not to run as sudo unless you really need to. Much better to work out the kinks. For example, when starting services from systemd, you get full control over what user/group is used so you could create a user and/or group specifically for your collection of related services.

Of course this is of less importance if you are the only person that ever accesses that system but still it is far too easy to forget and, with eventual scope creep, end up with a highly insecure system exposed to potential attack.

1 Like

As I said my system's only accessible for me, so that's really not an issue. But of course you can specify User=Pi in the service file and avoid it, I was just too lazy to. I even run file manager sudo, so I can edit config files easier... :rofl:

Edit: I tried now, and adding User=Pi in the service file made it ask me for the password, which wouldn't really work when running from Node-RED, would it?

Maybe bad advice but if you run a command from the exec node and a password for user is requested, in the below case sudo, you can pass it by using a command like:

echo password | sudo -S mount -all

EDIT: Haven't tried it but this might work for you

echo password | -S your_script.py

Hi, Krambriw! :slight_smile: Thanks, but the problem was systemctl with Systemd, not running the script as a standalone script.

Passing the password is not a good way to go, much better is to use visudo to give that user permission to use the particular command without having to enter a password. Then you are not leaving your password lying about in files in plain text.
In fact I believe the default Raspbian install gives the user pi permission to use sudo for anything without entering a password which is a bad idea except possibly on a system that has no access to the outside world. Even then I would not do it, nor would I leave the user pi on the system. It just makes life easier for any hacker who tries to gain access.

1 Like

@Colin, I tried that now, just for fun. And I can't get it to work. This is how that part of my visudo file looks:

ALL     ALL=(ALL) NOPASSWD: /bin/systemctl stop pi@myscript.service
ALL     ALL=(ALL) NOPASSWD: /bin/systemctl restart pi@myscript.service
ALL     ALL=(ALL) NOPASSWD: /bin/systemctl start pi@myscript.service

I am still asked for a password when I run:

systemctl stop pi@myscript.service

Do you see a mistake there?

Edit: I tried it without the pi@ as well, but that didn't help.

I don't think you can specify parameters to the command, at least I can't find any examples of that. I also tie it down to a particular user, so you could try
your_user_name ALL=NOPASSWD:/bin/systemctl

Possibly the spaces after the colon are also a problem in your tests.
Also can I check you are editing it using sudo visudo and that when you run systemctl you are still using sudo.

That didn't work, no. I put pi instead of your_user_name and copied the rest, and it didn't do anything. I still get:

/bin/systemctl restart mysecript.service
Failed to restart myscript.service: Access denied
See system logs and 'systemctl status myscript.service' for details.

But what do you mean by

when you run systemctl you are still using sudo

I may have misunderstood you here, but the point is to run systemctl for that service without having to use sudo. Oh, yes. And I do use sudo visudo. I don't think it's possible to edit that file in any other way, even with root it will open as read only when opened directly in another text editor.

Edit: As for parameters, if you mean the start, stop and restart, I have found several places where that is used, like here:

%deployer ALL= NOPASSWD: /bin/systemctl start my_app
%deployer ALL= NOPASSWD: /bin/systemctl stop my_app
%deployer ALL= NOPASSWD: /bin/systemctl restart my_app