Recently, I started a Python project that performs some web crawling stuff and presents information via Telegram bot. The project is partially working, hence, I want to make it run on my VPS as a background service. In this post, I will share my configuration to run the Python script using the Linux service manager.
Systemctl is a tool that can control the systemd system and service manager. And systemd is a system and service manager for Linux operating systems. When running as the first process on boot (as PID 1), it acts as init system that brings up and maintains userspace services.
In short, what we need to do is:
- Prepare your code/scripts and Python environment.
- Make a service configuration file.
- Reload the service and start it.
Preparing Code and Environment
For this part, you have the full freedom to do anything with your code. Make sure that you have installed all dependencies, and your code can be run without errors. Your script also needs to be capable to handle some system signals like SIGTERM (when you stop a service, the manager will send this signal to your script).
Virtualenv
It is always a good idea to separate your Python environment. I create a separate environment for each project.
# install virtualenv
sudo apt install virtualenv python3-virtualenv
Then, you can create a separated environment. Make sure that you are using the desired python3 or use a full path to a specified Python interpreter. --copies
can make your environment fully separated which means it’s portable for other similar machines.
# create your virutal environment
python3 -m venv /path/to/virtual/environment --copies
You can use your environment by commands:
# use your virtualenv
source /path/to/virtual/environment/bin/activate
# exit your virtualenv
deactivate
Signal Handling
To deal with signals in Python, we can use the signal
package for Python. Here’s a simple general framework to achieve that:
import signal
# import other stuffs
# the main function
def main():
# run your script function here
sth = your_script_class()
# define a signal handler
def signalHandler(sig, frame):
print('Signal received: %d' % sig)
# do something to your running script here
sth.stop()
return
# register your signal handler
signal.signal(signal.SIGTERM, signalHandler)
signal.signal(signal.SIGINT, signalHandler)
# hang the func forever until a signal comes
# not working on Windows, try to use loops
signal.pause()
return
if __name__ == '__main__':
main()
In the abovementioned codes, we use a closure to handle signals and make the running functions or classes to stop properly. You can modify this framework to adapt to your specific setup.
Service Configuration File
Now, we will do the actual work for the Python script running as a system service. First, create a service file for the systemd (the configuration instruction can be found here):
# you can use any other editor as you wish
# also change the service name
sudo vim /usr/lib/systemd/system/pythonscript.service
Then, add configuration to the file:
[Unit]
Description=Your Service Name
After=multi-user.target
[Service]
Type=idle
Restart=always
RestartSec=30
WorkingDirectory=full/directory/to/your/working/path
ExecStart=/full/directory/to/virtualenv/python /full/directory/to/your/script.py -c command
[Install]
WantedBy=multi-user.target
You need to change the description of your service, working directory and ExecStart (command to start your Python script). Make sure all directories and executables are in full path starting from /
. RestartSec
specifies a delay in seconds after encountering an exception. If you don’ want your script to restart, you should delete both Restart
and RestartSec
lines. Service type idle
indicates that the service will not be run until all jobs are dispatched.
Reload and Start the Service
Next, we need to reload the newly added service and enable the service on system boot:
# reload
sudo systemctl daemon-reload
# enable
sudo systemctl enable pythonscript.service
Finally, we can run and manage our service using commands:
# run
sudo systemctl start pythonscript.service
# check status
sudo systemctl status pythonscript.service
# stop
sudo systemctl stop pythonscript.service
# restart
sudo systemctl restart pythonscript.service
Note: Some of the status output are omitted. To display the full text output, add -l
behind the above commands.