In this section, we are going to show how to run a python script as a systemd service, allowing you to boot it at the start of a Linux machine and to maintain it alive.
We are going to use a very basic Telegram bot in order to test that our script will:
- Automatically start when the machine boot
- Automatically restart when it crashes/exits for whichever reason
If the bot is alive, then it means that our method works. Of course, we will also be able to check the status of the service through systemd, but just to be sure ... This bot is going to send us
- Create a ~/Temp folder on your Raspberry Pi using SSh
- Create a virtual environment in ~/Temp, you should already have
venv
we installed it at the beginning.
dietpi@solidsnake:~/Temp$ python3 -m venv .env
-
Load the virtual environment:
source .env/bin/activate
-
Let's also update pip, and install a pip we need.
(.env) dietpi@solidsnake:~/Temp$ pip install --upgrade pip
(.env) dietpi@solidsnake:~/Temp$ pip install pytelegrambotAPI
- Create a bot.py:
nano bot.py
:
Let's give our bot:
- A help message
# -*- coding: utf-8 -*-
import telebot
bot = telebot.TeleBot("725494896:AAFyUSf5e_QyYve0lmtr0AN7EcKvgy_aB-U")
@bot.message_handler(commands=['help', 'aide'])
def send_welcome(message):
text = """
Help !
"""
bot.reply_to(message, text)
bot.polling()
- Test your script:
python bot.py
Let's now make this script into a systemd service.
Important
All the paths in your scripts have to be absolute paths, there can be no relative path in your scripts. If there are relative paths that you must keep, you will have to change your current working directory by retrieving
- Modify the python script, add first line:
#!/home/dietpi/Temp/.env/bin/python3
which is the path to the python in the virtual env chmod +x
the python file to make it executable, it will execute with the python you specified (no need to load the venv yourself, just execute without specifying the python bin)- Type the following command in your terminal to add a systemd service
sudo systemctl edit --force --full dummy.service
:
[Unit]
Description=Dummy Service
Wants=network.target
After=network.target
[Service]
ExecStartPre=/bin/sleep 10
ExecStart=/home/dietpi/Temp/bot.py
Restart=always
[Install]
WantedBy=multi-user.target
-
Note that ExecStart is the path to the Python file directly if you made it an executable using the right virtual environment. If you did not, then you have to specify a python binary to execute it.
-
We have to add an ExecStartPre delay otherwise the service keeps tries to start before internet is even available and we get this error:
● dummy.service
Loaded: loaded (/etc/systemd/system/dummy.service; enabled; vendor preset: enabled)
Active: failed (Result: exit-code) since Tue 2019-10-29 00:26:18 GMT; 5min ago
Process: 442 ExecStart=/home/dietpi/guerab/guerab.py (code=exited, status=1/FAILURE)
Main PID: 442 (code=exited, status=1/FAILURE)
Oct 29 00:26:18 solidsnake guerab.py[442]: timeout=(connect_timeout, read_timeout), proxies=proxy)
Oct 29 00:26:18 solidsnake guerab.py[442]: File "/home/dietpi/guerab/.env/local/lib/python2.7/site-packages/requests/sessions.py", line 465, in request
Oct 29 00:26:18 solidsnake guerab.py[442]: resp = self.send(prep, **send_kwargs)
Oct 29 00:26:18 solidsnake guerab.py[442]: File "/home/dietpi/guerab/.env/local/lib/python2.7/site-packages/requests/sessions.py", line 573, in send
Oct 29 00:26:18 solidsnake guerab.py[442]: r = adapter.send(request, **kwargs)
Oct 29 00:26:18 solidsnake guerab.py[442]: File "/home/dietpi/guerab/.env/local/lib/python2.7/site-packages/requests/adapters.py", line 415, in send
Oct 29 00:26:18 solidsnake guerab.py[442]: raise ConnectionError(err, request=request)
Oct 29 00:26:18 solidsnake guerab.py[442]: requests.exceptions.ConnectionError: ('Connection aborted.', gaierror(-3, 'Temporary failure in name resolution'))
Oct 29 00:26:18 solidsnake systemd[1]: dummy.service: Main process exited, code=exited, status=1/FAILURE
Oct 29 00:26:18 solidsnake systemd[1]: dummy.service: Failed with result 'exit-code'.
- We also add a Restart flag in order to get systemd to always restart the script if it were to ever fail
Use Ctrl X + Y to save and exit when you finished editing.
- Enable the service
sudo systemctl enable dummy.service
- Reboot, wait for 30 seconds
- Try to contact your bot with
/help
, All good ! - SSH into your RPi
- Check your service status:
sudo systemctl status dummy.service
- Manipulate your service:
sudo systemctl stop dummy.service #To stop running service
sudo systemctl start dummy.service #To start running service
sudo systemctl restart dummy.service #To restart running service
- Let's validate that it will really restart on crash. Let's add a function to our bot that simply kills the script. By killing, I mean that we are going to create an error in order to get the script to crash. When we are working on a telegram bot script, each function is kind of loaded separately, we are going to create an error in a new function and use it to check if the bot truely restarts, add this:
#!/home/dietpi/Temp/.env/bin/python3
# -*- coding: utf-8 -*-
import telebot
bot = telebot.TeleBot("TELEGRAM_BOT_TOKEN")
@bot.message_handler(commands=['help', 'aide'])
def send_welcome(message):
text = """
Help !
"""
bot.reply_to(message, text)
@bot.message_handler(commands=["kill"])
def get_kill(message):
print(a)
bot.polling()
If you try ths script (in your Virtual Environment not as a service), you will see that the script will return the /help
command, but it will simply crash if you try to run the /kill command
which tries to print a variable a
that was never defined. Because python sees each telegram bot function as a separate function, it does not check that all variables exist before, as a variable can be defined with an incoming Telegram message.
- Deploy (copy/paste) this new script on the Raberry pi, then reboot the RPi, so it properly loads as the new systemd service
- Wait for 30 seconds, then contact your bot using
/help
to check that it is online - Use the kill command, the bot should die, wait for a bit more than 10 seconds, as we have a 10 second timer set prior to starting the script
- Try
/help
again, IT WORKS!
In order to delete the service:
sudo systemctl disable dummy.service
then reboot.
UPDATE (March 2020)
The freqtrade code was updated in February 2020 in such a way that freqtrade should now be run from the freqtrade/freqtrade/main.py
file instead of freqtrade/bin/freqtrade
. This means that there is a difference between the file being edited in the video and the one edited in this document. The version presented in this document should be used.
In addition, the DefaultStrategy
was removed from freqtrade. You will thus get an error if you try to run it, use BBRSI
instead.
Cheers !
- Go to
~/freqtrade/freqtrade
, and edit the file calledmain.py
using nano:
nano main.py
- Change the first line so that your new file looks like this:
#!/home/dietpi/freqtrade/.env/bin/python3
"""
Main Freqtrade bot script.
Read the documentation to know what cli arguments you need.
"""
from freqtrade.exceptions import FreqtradeException, OperationalException
import sys
# check min. python version
if sys.version_info < (3, 6):
sys.exit("Freqtrade requires Python version >= 3.6")
# flake8: noqa E402
import logging
from typing import Any, List
from freqtrade.commands import Arguments
logger = logging.getLogger('freqtrade')
def main(sysargv: List[str] = None) -> None:
"""
This function will initiate the bot and start the trading loop.
:return: None
"""
...
- Add the freqtrade systemd service using:
sudo systemctl edit --force --full freqtrade.service
- Paste the following:
[Unit]
Description=Freqtrade Service
Wants=network.target
After=network.target
[Service]
ExecStartPre=/bin/sleep 10
ExecStart=/home/dietpi/freqtrade/freqtrade/main.py trade -c /home/dietpi/freqtrade/config.json --strategy DefaultStrategy --userdir /home/dietpi/freqtrade/
Restart=always
[Install]
WantedBy=multi-user.target
-
Use
Ctrl+X
thenY
followed byEnter
to save the new serviced. -
Enable the service
sudo systemctl enable freqtrade.service
-
Reboot the Raspberry pi:
sudo reboot now
UPDATE (March 2020)
The freqtrade code was updated in February 2020 in such a way that freqtrade should now be run from the freqtrade/freqtrade/main.py
file instead of freqtrade/bin/freqtrade
. This means that there is a difference between the file being edited in the video and the one edited in this document. The version presented in this document should be used.
In addition, the DefaultStrategy
was removed from freqtrade. You will thus get an error if you try to run it, use BBRSI
instead.
Cheers !
- Go to
~/freqtrade/freqtrade
, and edit the file calledmain.py
using nano:
nano main.py
- Change the first line so that your new file looks like this:
#!/root/freqtrade/.env/bin/python3
"""
Main Freqtrade bot script.
Read the documentation to know what cli arguments you need.
"""
from freqtrade.exceptions import FreqtradeException, OperationalException
import sys
# check min. python version
if sys.version_info < (3, 6):
sys.exit("Freqtrade requires Python version >= 3.6")
# flake8: noqa E402
import logging
from typing import Any, List
from freqtrade.commands import Arguments
logger = logging.getLogger('freqtrade')
def main(sysargv: List[str] = None) -> None:
"""
This function will initiate the bot and start the trading loop.
:return: None
"""
...
- Add the freqtrade systemd service using:
sudo systemctl edit --force --full freqtrade.service
- Paste the following:
[Unit]
Description=Freqtrade Service
Wants=network.target
After=network.target
[Service]
ExecStartPre=/bin/sleep 10
ExecStart=/root/freqtrade/freqtrade/main.py trade -c /root/freqtrade/config.json --strategy DefaultStrategy --userdir /root/freqtrade/
Restart=always
[Install]
WantedBy=multi-user.target
-
Use
Ctrl+X
thenY
followed byEnter
to save the new serviced. -
Enable the service
sudo systemctl enable freqtrade.service
-
Reboot the Raspberry pi:
sudo reboot now
- https://raspberrypi.stackexchange.com/questions/90148/running-a-python-telegram-bot-at-startup-and-24-7
- https://www.digitalocean.com/community/tutorials/how-to-configure-a-linux-service-to-start-automatically-after-a-crash-or-reboot-part-2-reference
- Exit Telegram Bot: python-telegram-bot/python-telegram-bot#801