Run python as windows service

Пройдите тест, узнайте какой профессии подходите

Работать самостоятельно и не зависеть от других

Работать в команде и рассчитывать на помощь коллег

Организовывать и контролировать процесс работы

Быстрый ответ

Для того чтобы Python-скрипт мог работать в роли службы Windows, рекомендуется использовать библиотеку pywin32. Создайте класс службы, который будет унаследован от win32serviceutil.ServiceFramework, а затем переопределите методы SvcDoRun и SvcStop для управления запуском и остановкой службы. Установка и управление службой происходит через командную строку. Пример кода представлен ниже:

Для установки и запуска службы воспользуйтесь следующими командами:

Данный метод позволит вам работать со скриптом Python как с службой Windows.

Кинга Идем в IT: пошаговый план для смены профессии

Расширение функционала вашей службы

Использование NSSM для упрощённого управления службами

NSSM (Non-Sucking Service Manager) предлагает комфортный графический интерфейс для управления службами, облегчая настройку использования скриптов и переменных среды.

Регистрация вашей службы с помощью sc.exe

Можно зарегистрировать ваш скрипт как службу, используя команду sc.exe:

Применение Python и Django для управления службами

Python и Django предоставляют возможность создавать веб-интерфейсы для управления службами, улучшая удобство и гибкость их использования.

Использование опыта работы с демонами в Linux

Если у вас имеется опыт работы с демонами в Linux, то вы усвоите создание служб для Windows быстрее, несмотря на специфику реализации в этих системах.

Визуализация

Представьте себе Python-скрипт, превращающийся в службу Windows, как робот (🤖), которы работает в автономном режиме:

Изначально Python-скрипт — это просто набор команд (📜), но после аккуратной настройки (🔧👨‍🔧), он превращается в полноценно функционирующую службу (🤖).

Решение возникающих проблем

Журналирование работы службы и обработка ошибок

Servicemanager поможет вести журнал работы службы и адекватно обрабатывать возникающие ошибки, давая полное представление о функционировании службы и возможных проблемах.

Управление переменными окружения

Использование динамической конфигурации на основе переменных окружения позволит службе адаптироваться к различным условиям работы: отладка, предпродакшн, продакшн.

Управление зависимостями

Работа службы может требовать конкретные версии библиотек или внешние модули Python. Для этого используются виртуальные среды и PyInstaller, который создаёт исполняемые файлы со всеми необходимыми зависимостями.

Продвинутое управление службой

Масштабирование службы

Для организации управления нагрузкой на службы можно применить виртуальное окружение, что упростит изоляцию и решение потенциальных проблем, связанных с масштабированием.

Тонкая настройка службы через реестр

Можно детально настроить поведение службы на уровне реестра Windows по адресу HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services.

Бесперебойные обновления и поддержка служб

Обеспечивайте возможности «горячего» обновления и «мягкого» завершения работы службы для сокращения времени простоя. Нужно продумать надежный механизм обновлений, который позволит внедрять новые версии службы без ручного вмешательства.

Полезные материалы

  1. NSSM – the Non-Sucking Service Manager — инструмент для простого управления Python-службами.
  2. Как запустить скрипт Python как службу в Windows на Stack Overflow — обсуждения и решения от сообщества разработчиков.
  3. Полный пример службы от Microsoft — официальные рекомендации по созданию служб Windows.
  4. Руководство по PyInstaller — создание автономных исполняемых файлов из Python-скриптов.
  5. Планировщик задач для разработчиков от Microsoft — использование Планировщика задач Windows для автоматизации Python-скриптов.
  6. Supervisor: Система контроля процессов — мониторинг и контроль над продолжительными службами на Python.

The web is a living, breathing organism – it constantly adapts and changes. In this dynamic environment, gathering time-sensitive data such as E-commerce listings only once is useless as it quickly becomes obsolete. To be competitive, you must keep your data fresh and run your web scraping scripts repeatedly and regularly.

The easiest way is to run a script in the background. In other words, run it as a service. Fortunately, no matter the operating system in use – Linux or Windows – you have great tools at your disposal. In today’s guide, we’ll demonstrate how to run a Python script on a server in just a few simple steps.

For your convenience, we also prepared this tutorial in a video format:

Preparing a Python script for Linux

In this article, information from a list of book URLs will be scraped. When the process reaches the end of the list, it loops over and refreshes the data again and again.

First, make a request and retrieve the HTML content of a page. Use the Requests module to do so:

urls = [
'https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html',
'https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html',
'https://books.toscrape.com/catalogue/sharp-objects_997/index.html',
]

index = 0
while True:
    url = urls[index % len(urls)]
    index += 1

    print('Scraping url', url)
    response = requests.get(url)

Once the content is retrieved, parse it using the Beautiful Soup library:

soup = BeautifulSoup(response.content, 'html.parser')
book_name = soup.select_one('.product_main').h1.text
rows = soup.select('.table.table-striped tr')
product_info = {row.th.text: row.td.text for row in rows}

Make sure your data directory-to-be already exists, and then save book information there in JSON format.

Protip: make sure to use the pathlib module to automatically convert Python path separators into a format compatible with both Windows and Linux systems.

data_folder = Path('./data')
data_folder.mkdir(parents=True, exist_ok=True)

json_file_name = re.sub('[: ]', '-', book_name)
json_file_path = data_folder / f'{json_file_name}.json'
with open(json_file_path, 'w') as book_file:
    json.dump(product_info, book_file)

Since this script is long-running and never exits, you must also handle any requests from the operating system attempting to shut down the script. This way, you can finish the current iteration before exiting. To do so, you can define a class that handles the operating system signals:

class SignalHandler:
    shutdown_requested = False

    def __init__(self):
        signal.signal(signal.SIGINT, self.request_shutdown)
        signal.signal(signal.SIGTERM, self.request_shutdown)

    def request_shutdown(self, *args):
        print('Request to shutdown received, stopping')
        self.shutdown_requested = True

    def can_run(self):
        return not self.shutdown_requested

Instead of having a loop condition that never changes (while True), you can ask the newly built SignalHandler whether any shutdown signals have been received:

signal_handler = SignalHandler()

# ...

while signal_handler.can_run():
    # run the code only if you don't need to exit

Here’s the code so far:

import json
import re
import signal
from pathlib import Path

import requests
from bs4 import BeautifulSoup

class SignalHandler:
    shutdown_requested = False

    def __init__(self):
        signal.signal(signal.SIGINT, self.request_shutdown)
        signal.signal(signal.SIGTERM, self.request_shutdown)

    def request_shutdown(self, *args):
        print('Request to shutdown received, stopping')
        self.shutdown_requested = True

    def can_run(self):
        return not self.shutdown_requested


signal_handler = SignalHandler()
urls = [
    'https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html',
    'https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html',
    'https://books.toscrape.com/catalogue/sharp-objects_997/index.html',
]

index = 0
while signal_handler.can_run():
    url = urls[index % len(urls)]
    index += 1

    print('Scraping url', url)
    response = requests.get(url)

    soup = BeautifulSoup(response.content, 'html.parser')
    book_name = soup.select_one('.product_main').h1.text
    rows = soup.select('.table.table-striped tr')
    product_info = {row.th.text: row.td.text for row in rows}

    data_folder = Path('./data')
    data_folder.mkdir(parents=True, exist_ok=True)

    json_file_name = re.sub('[\': ]', '-', book_name)
    json_file_path = data_folder / f'{json_file_name}.json'
    with open(json_file_path, 'w') as book_file:
        json.dump(product_info, book_file)

The script will refresh JSON files with newly collected book information.

Running a Linux daemon

If you’re wondering how to run Python script on server in Linux, there are multiple ways to do it on startup. Many distributions have built-in GUI tools for such purposes.

Let’s use one of the most popular distributions, Linux Mint, as an example. It uses a desktop environment called Cinnamon that provides a startup application utility.

System settings

System settings

It allows you to add your script and specify a startup delay.

Adding a script

Adding a script

However, this approach doesn’t provide more control over the script. For example, what happens when you need to restart it?

This is where systemd comes in. Systemd is a service manager that allows you to manage user processes using easy-to-read configuration files.

To use systemd, let’s first create a file in the /etc/systemd/system directory:

cd /etc/systemd/system
touch book-scraper.service

Add the following content to the book-scraper.service file using your favorite editor:

[Unit]
Description=A script for scraping the book information
After=syslog.target network.target

[Service]
WorkingDirectory=/home/oxylabs/Scraper
ExecStart=/home/oxylabs/Scraper/venv/bin/python3 scrape.py

Restart=always
RestartSec=120

[Install]
WantedBy=multi-user.target

Here’s the basic rundown of the parameters used in the configuration file:

  • After – ensures you only start your Python script once the network is up. 

  • RestartSec – sleep time before restarting the service.

  • Restart – describes what to do if a service exits, is killed, or a timeout is reached. 

  • WorkingDirectory – current working directory of the script.

  • ExecStart – the command to execute.

Now, it’s time to tell systemd about the newly created daemon. Run the daemon-reload command:

Then, start your service:

systemctl start book-scraper

And finally, check whether your service is running:

$ systemctl status book-scraper
book-scraper.service - A script for scraping the book information
     Loaded: loaded (/etc/systemd/system/book-scraper.service; disabled; vendor preset: enabled)
     Active: active (running) since Thu 2022-09-08 15:01:27 EEST; 16min ago
   Main PID: 60803 (python3)
      Tasks: 1 (limit: 18637)
     Memory: 21.3M
     CGroup: /system.slice/book-scraper.service
             60803 /home/oxylabs/Scraper/venv/bin/python3 scrape.py

Sep 08 15:17:55 laptop python3[60803]: Scraping url https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html
Sep 08 15:17:55 laptop python3[60803]: Scraping url https://books.toscrape.com/catalogue/sharp-objects_997/index.html
Sep 08 15:17:55 laptop python3[60803]: Scraping url https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html

Protip: use journalctl -S today -u book-scraper.service to monitor your logs in real-time.

Congrats! Now you can control your service via systemd.

Running a Python script as a Windows service

Running a Python script as a Windows service is not as straightforward as one might expect. Let’s start with the script changes.

To begin, change how the script is executed based on the number of arguments it receives from the command line.  

If the script receives a single argument, assume that Windows Service Manager is attempting to start it. It means that you have to run an initialization code. If zero arguments are passed, print some helpful information by using win32serviceutil.HandleCommandLine:

if __name__ == '__main__':
    if len(sys.argv) == 1:
        servicemanager.Initialize()
        servicemanager.PrepareToHostSingle(BookScraperService)
        servicemanager.StartServiceCtrlDispatcher()
    else:
        win32serviceutil.HandleCommandLine(BookScraperService)

Next, extend the special utility class and set some properties. The service name, display name, and description will all be visible in the Windows services utility (services.msc) once your service is up and running.

class BookScraperService(win32serviceutil.ServiceFramework):
    _svc_name_ = 'BookScraperService'
    _svc_display_name_ = 'BookScraperService'
    _svc_description_ = 'Constantly updates the info about books'

Finally, implement the SvcDoRun and SvcStop methods to start and stop the service. Here’s the script so far:

import sys
import servicemanager
import win32event
import win32service
import win32serviceutil
import json
import re
from pathlib import Path

import requests
from bs4 import BeautifulSoup


class BookScraperService(win32serviceutil.ServiceFramework):
    _svc_name_ = 'BookScraperService'
    _svc_display_name_ = 'BookScraperService'
    _svc_description_ = 'Constantly updates the info about books'

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.event = win32event.CreateEvent(None, 0, 0, None)

    def GetAcceptedControls(self):
        result = win32serviceutil.ServiceFramework.GetAcceptedControls(self)
        result |= win32service.SERVICE_ACCEPT_PRESHUTDOWN
        return result

    def SvcDoRun(self):
        urls = [
'https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html',
'https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html',
'https://books.toscrape.com/catalogue/sharp-objects_997/index.html',
        ]

        index = 0

        while True:
            result = win32event.WaitForSingleObject(self.event, 5000)
            if result == win32event.WAIT_OBJECT_0:
                break

            url = urls[index % len(urls)]
            index += 1

            print('Scraping url', url)
            response = requests.get(url)

            soup = BeautifulSoup(response.content, 'html.parser')
            book_name = soup.select_one('.product_main').h1.text
            rows = soup.select('.table.table-striped tr')
            product_info = {row.th.text: row.td.text for row in rows}

            data_folder = Path('C:\\Users\\User\\Scraper\\dist\\scrape\\data')
            data_folder.mkdir(parents=True, exist_ok=True)

            json_file_name = re.sub('[\': ]', '-', book_name)
            json_file_path = data_folder / f'{json_file_name}.json'
            with open(json_file_path, 'w') as book_file:
                json.dump(product_info, book_file)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.event)


if __name__ == '__main__':
    if len(sys.argv) == 1:
        servicemanager.Initialize()
        servicemanager.PrepareToHostSingle(BookScraperService)
        servicemanager.StartServiceCtrlDispatcher()
    else:
        win32serviceutil.HandleCommandLine(BookScraperService)

Now that you have the script, open a Windows terminal of your preference. If you’re new to using your computer’s command-line interface, you can learn the basics by following our guide about how to run a Python script in terminal.

Protip: if you’re using Powershell, make sure to include a .exe extension when running binaries to avoid unexpected errors.

Terminal

Terminal

Once the terminal is open, change the directory to the location of your script with a virtual environment, for example:

Next, install the experimental Python Windows extensions module, pypiwin32. You’ll also need to run the post-install script:

.\venv\Scripts\pip install pypiwin32
.\venv\Scripts\pywin32_postinstall.py -install

Unfortunately, if you attempt to install your Python script as a Windows service with the current setup, you’ll get the following error:

**** WARNING ****
The executable at "C:\Users\User\Scraper\venv\lib\site-packages\win32\PythonService.exe" is being used as a service.

This executable doesn't have pythonXX.dll and/or pywintypesXX.dll in the same
directory, and they can't be found in the System directory. This is likely to
fail when used in the context of a service.

The exact environment needed will depend on which user runs the service and
where Python is installed. If the service fails to run, this will be why.

NOTE: You should consider copying this executable to the directory where these
DLLs live - "C:\Users\User\Scraper\venv\lib\site-packages\win32" might be a good place.

However, if you follow the instructions of the error output, you’ll be met with a new issue when trying to launch your script:

Error starting service: The service did not respond to the start or control request in a timely fashion.

To solve this issue, you can add the Python libraries and interpreter to the Windows path. Alternatively, bundle your script and all its dependencies into an executable by using pyinstaller:

venv\Scripts\pyinstaller --hiddenimport win32timezone -F scrape.py

The —hiddenimport win32timezone option is critical as the win32timezone module is not explicitly imported but is still needed for the script to run.

Finally, let’s install the script as a service and run it by invoking the executable you’ve built previously:

PS C:\Users\User\Scraper> .\dist\scrape.exe install
Installing service BookScraper
Changing service configuration
Service updated

PS C:\Users\User\Scraper> .\dist\scrape.exe start
Starting service BookScraper
PS C:\Users\User\Scraper>

And that’s it. Now, you can open the Windows services utility and see your new service running.

Protip: you can read more about specific Windows API functions here.

The newly created service is running

The newly created service is running

Making your life easier by using NSSM on Windows

As evident, you can use win32serviceutil to develop a Windows service. But the process is definitely not that simple – you could even say it sucks! Well, this is where the NSSM (Non-Sucking Service Manager) comes into play.

Let’s simplify the script by only keeping the code that performs web scraping:

import json
import re
from pathlib import Path

import requests
from bs4 import BeautifulSoup

urls = ['https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html',
        'https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html',
        'https://books.toscrape.com/catalogue/sharp-objects_997/index.html', ]

index = 0

while True:
    url = urls[index % len(urls)]
    index += 1

    print('Scraping url', url)
    response = requests.get(url)

    soup = BeautifulSoup(response.content, 'html.parser')
    book_name = soup.select_one('.product_main').h1.text
    rows = soup.select('.table.table-striped tr')
    product_info = {row.th.text: row.td.text for row in rows}

    data_folder = Path('C:\\Users\\User\\Scraper\\data')
    data_folder.mkdir(parents=True, exist_ok=True)

    json_file_name = re.sub('[\': ]', '-', book_name)
    json_file_path = data_folder / f'{json_file_name}.json'
    with open(json_file_path, 'w') as book_file:
        json.dump(product_info, book_file)

Next, build a binary using pyinstaller:

venv\Scripts\pyinstaller -F simple_scrape.py

Now that you have a binary, it’s time to install NSSM by visiting the official website. Extract it to a folder of your choice and add the folder to your PATH environment variable for convenience.

NSSM in a folder

NSSM in a folder

Then, run the terminal as an admin.

Running as an admin

Running as an admin

Once the terminal is open, change the directory to your script location:

Finally, install the script using NSSM and start the service:

nssm.exe install SimpleScrape C:\Users\User\Scraper\dist\simple_scrape.exe
nssm.exe start SimpleScrape

Protip: if you have issues, redirect the standard error output of your service to a file to see what went wrong:

nssm set SimpleScrape AppStderr C:\Users\User\Scraper\service-error.log

NSSM ensures that a service is running in the background, and if it doesn’t, you at least get to know why.

Conclusion

Regardless of the operating system, you have various options for setting up Python scripts for recurring web scraping tasks. Whether you need the configurability of systemd, the flexibility of Windows services, or the simplicity of NSSM, be sure to follow this tried & true guide as you navigate their features.

If you are interested in more Python automation solutions for web scraping applications or web scraping with Python, take a look at our blog for various tutorials on all things web scraping. We also offer an advanced solution, Web Scraper API, designed to collect public data from most websites automatically and hassle-free. In addition, you can use a Scheduler feature to schedule multiple web scraping jobs at any frequency you like.

People also ask

What is systemd?

Systemd is a powerful tool for managing processes. It also provides logging and dependency management, for example, starting processes only when a network is available or once a previous service has been started.

What is the difference between systemd and systemctl?

Systemctl is a utility and is part of systemd. Among other things, it allows you to manage services and check their status.

Can I use cron instead of systemd?

You definitely can if it suits your needs. However, if you need more control over the service, systemd is the way to go. It allows you to start services based on certain conditions and is perfect for dealing with long-running scripts. It even allows you to run scripts in a similar way to crontab by using timers.

What’s the difference between a daemon and a service?

The word daemon comes from MIT’s Project MAC, which first coined the term in 1963. It is, by definition, an agent that works tirelessly in the background. Later Windows and Linux adopted the term as an alternative name. Therefore, this article uses the words daemon and service interchangeably.

About the author

author avatar

Augustas Pelakauskas

Senior Technical Copywriter

Augustas Pelakauskas was a Senior Technical Copywriter at Oxylabs. Coming from an artistic background, he is deeply invested in various creative ventures — the most recent being writing. After testing his abilities in freelance journalism, he transitioned to tech content creation. When at ease, he enjoys the sunny outdoors and active recreation. As it turns out, his bicycle is his fourth-best friend.

All information on Oxylabs Blog is provided on an «as is» basis and for informational purposes only. We make no representation and disclaim all liability with respect to your use of any information contained on Oxylabs Blog or any third-party websites that may be linked therein. Before engaging in scraping activities of any kind you should consult your legal advisors and carefully read the particular website’s terms of service or receive a scraping license.

Related articles

ISO/IEC 27001:2017 certified products:

Scale up your business with Oxylabs®

Forget about complex web scraping processes

Choose Oxylabs’ advanced web intelligence collection solutions to gather real-time public data hassle-free.

WINDOWS

When automating tasks on a Windows machine, running a Python script as a Windows service can be incredibly beneficial. Services are background processes that can start up automatically when the system boots, allowing your script to run without the need for a user to be logged in. This guide will help you navigate around Windows permissions issues, user logins, and set up your Python script to run smoothly anytime your computer is on.

Why Run a Python Script as a Windows Service?

Running scripts as services has several advantages:

  • Automatic Start: Services can start when the system boots up, ensuring that your script is always running.

  • No User Session Required: Unlike running scripts from a user account, services can run without any user logged in.

  • Better Management: Being able to start, stop, or restart applications through the Services management console can make your life easier.

Prerequisites

Before diving in, ensure you have the following:

  • Python installed on your Windows machine (preferably the latest version).

  • pywin32 package, which provides access to many Windows APIs from Python, including service functionalities.

You can install the required pywin32 library using pip:

Step-by-Step Guide to Create a Windows Service for Your Python Script

Step 1: Create Your Python Script

Before creating the service, you need a functional Python script. Here is a simple example script that writes the current time to a log file every minute:

import time
import logging

# Set up logging
logging.basicConfig(filename='example.log', level=logging.INFO)

while True:
    logging.info("Current time is: {}".format(time.ctime()))
    time.sleep(60)  # Wait for one minute

Step 2: Creating the Service Script

You will need to create another Python script that defines the service itself. Here’s how to do it:

import time
import servicemanager
import win32service
import win32serviceutil
import logging
import os

class PythonService(win32serviceutil.ServiceFramework):
    _svc_name_ = "PythonService"
    _svc_display_name_ = "Python Service Example"
    _svc_description_ = "Python Service that logs time to a file."

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.stop_event = win32event.CreateEvent(None, 0, 0, None)
        self.running = True
        self.log_file = 'example.log'
        logging.basicConfig(filename=self.log_file, level=logging.INFO)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.stop_event)
        self.running = False

    def SvcDoRun(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                               servicemanager.PYS_SERVICE_STARTED,
                               (self._svc_name_, ''))
        self.main()

    def main(self):
        while self.running:
            logging.info("Current time is: {}".format(time.ctime()))
            time.sleep(60)  # Wait for one minute

if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(PythonService)

Step 3: Install the Service

To install the service, open a command prompt with administrative privileges and run the following command:

python your_service_script.py install

Replace your_service_script.py with the name of the service script you just created.

Step 4: Start the Service

Once installed, you can start the service using:

python your_service_script.py start

Alternatively, you can manage your service through the Windows Services management console.

Step 5: Debugging and Logs

Check the example.log file to see if it is logging the time as expected. In case something goes wrong, you can troubleshoot by running the service script directly in a terminal (not as a service) to see if any errors are raised.

Step 6: Stop and Uninstall the Service

To stop the service, use:

python your_service_script.py stop

And to uninstall it, run:

python your_service_script.py remove

Managing Permissions and User Logins

Running scripts as a service might raise issues related to permissions. Here are a few tips to manage these:

  • User Account: When setting up the service, ensure it runs under a user account with the right permissions to access files or external resources used by the script.

  • Interactive Services: Windows services typically cannot interact with the desktop. If your script requires user input, consider redesigning it to operate without interaction.

  • Logs: Always include logging in your script, so any issues can be diagnosed easily. Make sure the account running the service has write permissions to the log location.

Conclusion

Running a Python script as a Windows service can enhance automation by ensuring your tasks run consistently in the background, regardless of user logins. Utilizing the pywin32 library simplifies creating and managing services, alleviating many common permissions issues. By following this guide, you can effectively automate your processes, ensuring they are executed whenever your machine is turned on.

Key Takeaways

  • Automate tasks by running Python scripts as Windows services.

  • Utilize the pywin32 library for service creation.

  • Manage service permissions carefully to avoid runtime issues.

  • Implement logging for easier troubleshooting.

By applying this knowledge, you can make full use of your Python scripts in a Windows environment and streamline your workflow efficiently.

Suggested Articles

WINDOWS

WINDOWS

WINDOWS

WINDOWS

WINDOWS

WINDOWS

Need to continuously run Python scripts for ongoing automation and scheduling? Configuring scripts as long-running services can be a robust solution.

In this comprehensive, 2200+ word guide, we’ll walk through the steps to run Python scripts as system services on both Linux and Windows.

We’ll cover:

  • Key benefits of using services for automation
  • How Linux systemd and Windows Service Control Manager work
  • Configuring systemd units to manage Python services
  • Building Windows services with pywin32
  • Simplifying Windows services with NSSM
  • Tips for security, logging, and troubleshooting

Follow along and you’ll gain the knowledge to configure your Python automation scripts to run reliably as services on any system!

Why Run Python Scripts as Services?

Running scripts via python script.py works fine for one-off jobs. But for recurring automation tasks, dedicated services provide important benefits:

Scheduled Execution

Services run continuously in the background rather than needing manual starts. This enables python scripts to be run on schedules.

Automatic Restarts

Crashed or stopped services can restart automatically. This results in resilient automation.

Centralized Control

Tools like systemd and the Windows Service Control Manager provide one place to monitor, configure, and manage services.

Security

Services can run under isolated users and permissions for reduced risk.

Common examples of Python scripts that work well as services:

  • Web scraping scripts that need to run regularly to fetch updated data
  • Machine learning model training that is resource-intensive if run too often
  • Cron-style scheduled jobs like cleaning temp files or synchronizing caches

Next, we‘ll look at how Linux systemd and Windows Service Control Manager enable running scripts as managed services.

An Introduction to Service Managers

Modern operating systems come equipped with service managers that handle launching, monitoring, and stopping services.

systemd

The most widely used service manager on Linux is systemd. It acts as the init system for most major distros like Ubuntu, Debian, RHEL.

systemd is powerful not only because it handles service processes, but also because it manages system startup, devices, sockets, timers, and more.

Key capabilities provided by systemd for services:

  • Service lifecycle management – start, stop, restart
  • Configurable restart policies – always restart, on-failure, etc
  • Resource limitation – set memory, CPU, IO limits
  • Log integration with journald
  • Dependency ordering – start service X after Y

Services are configured by creating unit files, typically stored in /etc/systemd/system.

Windows Service Control Manager

This is the service manager built into Windows for handling long-running backend processes. It provides similar capabilities to systemd, such as:

  • Automated startup of services
  • Monitoring service state – running, stopped, etc
  • Centralized control for starting, stopping services
  • Configurable restart settings and recovery actions

Services register themselves with the SCM and provide lifecycle event handlers. The net command is used to manage services.

Now that we‘ve seen how Linux and Windows enable services, let‘s walk through configuring Python scripts on each!

Step-by-Step: Creating systemd Services for Python

On Linux, we can leverage systemd to run Python scripts as managed services. This offers standardized configs and tight integration with the OS.

Let‘s look at how to configure a Python script as a systemd service on Linux:

  1. First, create a Python script with the logic you want to run continuously:

     import requests
    
     def main():
         response = requests.get("https://www.example.com")
         print(response.status_code)
    
     if __name__ == "__main__":
         main() 
  2. Next, create a systemd service file like /etc/systemd/system/myscript.service:

     [Unit]
     Description=My Script Service
    
     [Service]
     ExecStart=/usr/bin/python3 /opt/scripts/myscript.py 
     Restart=on-failure
     RestartSec=10
    
     [Install]
     WantedBy=multi-user.target

    Let‘s explain some key points:

    • The [Unit] section provides metadata like a description
    • [Service] defines the script path for ExecStart and restart policy
    • [Install] says to start on system boot as a multi-user service
  3. Now reload systemd and start the service:

     sudo systemctl daemon-reload
     sudo systemctl start myscript.service
  4. Check status with systemctl status myscript or view logs via journalctl -u myscript

And we have a Python service managed by systemd! The script will run continuously, logging to journald, and auto-restart on failures.

Some more tips for robust Python systemd services:

  • Use ExecStartPre and ExecStartPost to add prep/cleanup logic
  • Set resource limits under [Service] like MemoryMax=200M
  • Make the service a template file at /etc/systemd/system/[email protected] for multiple instances

Overall systemd provides a powerful way to deploy Python scripts as managed Linux services.

Creating Windows Services in Python with pywin32

On Windows, we can use the pywin32 library to directly create services using Python.

The steps are:

  1. Structure the Python script to handle service commands
  2. Subclass win32serviceutil.ServiceFramework
  3. Implement lifecycle event handlers like SvcDoRun
  4. Install and run the service

Here is an example Python Windows service:

import win32serviceutil
import win32service

class PySvc(win32serviceutil.ServiceFramework):
    _svc_name_ = "PyService"
    _svc_display_name_ = "Python Sample Service"

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)

    def SvcDoRun(self):
        self.ReportServiceStatus(win32service.SERVICE_RUNNING) 
        # service logic goes here

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOPPED)
        # shutdown logic goes here 

if __name__ == ‘__main__‘: 
    win32serviceutil.HandleCommandLine(PySvc)

To install:

pyinstaller --onefile pyservice.py
pyservice.exe --install  
net start PyService

This allows full control over the Python service logic. But the pywin32 APIs can be verbose and low-level.

Simplifying Windows Services with NSSM

An alternative to pywin32 for running Python scripts as Windows services is the NSSM – the Non-Sucking Service Manager.

NSSM makes it very easy to wrap executables as Windows services with just a few commands:

nssm install MyService C:\Python\myscript.exe
nssm set MyService AppParameters "--log=C:\logs\mylog.txt"
nssm start MyService

Advantages of using NSSM over pywin32:

  • No need to write Python service classes
  • Easily pass arguments with AppParameters
  • Management via concise commands like nssm [action] [service]
  • Monitor stdout/stderr, set working directory, etc

So if you just need to quickly run a Python script as a Windows service, definitely give NSSM a try!

Securing and Troubleshooting Python Services

When running Python scripts as persistent services, we also need to consider security, logging, and troubleshooting.

Here are some tips:

  • Run under a limited user – Don‘t give the service account more permissions than necessary
  • Enable logging – For systemd, integrate with journald. For Windows, redirect to a log file
  • Limit restart attempts – In case of crashes, don‘t restart endlessly
  • Sandbox the environment – Use virtual environments and AppArmor profiles
  • Review logs – journalctl and event viewer are key troubleshooting tools
  • Monitor processes – Check systemctl status or task manager for resource usage
  • Use service management commandssystemctl, nssm, and net start/stop services
  • Know how to disable services – Run systemctl disable or sc config [Service] start= disabled

Taking these best practices into account will help keep Python services running smoothly and securely.

Alternative Service Managers

While systemd and Windows SCM are native solutions, there are also some cross-platform alternatives:

  • supervisord – Popular for managing Python and Go services
  • pm2 – Node.js service manager with a large ecosystem

Both allow configuring services in ini-style config files and provide features like monitoring and log aggregation.

For simpler scripts, systemd or NSSM may be preferable. But supervisord and pm2 are worth evaluating if you need broader language support.

Key Takeaways

We‘ve covered a lot of ground on running Python scripts as managed services! Here are some key tips to remember:

  • Use systemd on Linux and SCM on Windows for native OS integration
  • Familiarize yourself with journald and event viewer for logging
  • systemd uses unit files, SCM uses registry keys
  • For Windows, NSSM makes deployment easy
  • Control service lifecycles via systemctl, net stop, etc
  • Follow security best practices like limited accounts
  • Set restart policies to balance availability and recoverability

Whether you‘re on Linux or Windows, you now have the knowledge to configure Python scripts as services! Automating recurring tasks with Python is now easy and reliable.

If you want to run a Python 3 application as a Windows Service, then you are going to love NSSM. Since NSSM handles the life cycle of a Windows Service for our Python 3 application, we can keep it platform agnostic.

Previously, we saw how we can use a Python 3 virtual environment in Windows 10. Given that, let’s see how we can use NSSM to run a Python 3 application as a Windows Service in its own Python 3 virtual environment.

Creating a sample Python 3 application

In order to simplify this tutorial, let’s attempt to run the Python 3 Flask application with an API endpoint that generates a QR Code image as a Windows Service.

In addition to that, let us place requirements.txt and run_app.py into the %systemdrive%%homepath%\Documents\qr-code-app folder.

Contents of requirements.txt

flask-restplus==0.12.1
Pillow==6.0.0
qrcode==6.1

Contents of run_app.py

from flask import Flask, Blueprint, request, send_file
from flask_restplus import Api, Namespace, Resource, fields
# import library to help us with file IO
from io import BytesIO
 
import os, qrcode
 
# Create Flask app
app = Flask(__name__)
 
# Associate Api with Blueprint
api_blueprint = Blueprint('API', __name__)
api = Api(api_blueprint,
    title='Api for QR code app',
    version='1.0',
    description='This is an API for QR code app',
    # All API metadatas
)
 
# Create namespace for containing Qr Code related operations
qrcode_namespace = Namespace('QrCode', description='Qr code related operations')
# Specify uri of qrcode namespace as /qrcode
api.add_namespace(qrcode_namespace, path='/qrcode')
# Specify uri of api blueprint as /api
app.register_blueprint(api_blueprint, url_prefix='/api')
 
# Define input model
qrcode_creation_input = qrcode_namespace.model('QRCode creation Input', {
    'value': fields.String(required=True, description='The value that is supposed to be encoded into qrcode'),
})
 
 
# Define API endpoint for creating Qr Code image
@qrcode_namespace.route('/')
@qrcode_namespace.doc('Creates a QRCode image based on a string value.')
class QrCodeRoot(Resource):
 
    @qrcode_namespace.expect(qrcode_creation_input)
    @qrcode_namespace.produces(['image/png'])
    @qrcode_namespace.response(200, description='Return QR Code png image file.')
    @qrcode_namespace.response(400, description='Invalid input provided.')
    def post(self):
 
        # Get value to encode into QR Code
        json_input = request.get_json()
        value_to_turn_into_qrcode = json_input['value']
 
        # Create qr code image and return it as HTTP response
        pil_img = qrcode.make(value_to_turn_into_qrcode)
        img_io = BytesIO()
        pil_img.save(img_io, 'PNG')
        img_io.seek(0)
        return send_file(img_io, mimetype='image/png')
 
 
if __name__ == '__main__':
    port = int(os.getenv("PORT", "5678"))
    app.run(host='0.0.0.0', port=port)

Preparing the virtual environment to run our Python 3 application

Before continuing on, be sure to go through the tutorial on how to use a Python 3 virtual environment in Windows 10. After you had gone through the tutorial, you would have installed Python 3 on your Windows machine. In addition to that, you will understand how to create, activate and install dependencies into a virtual environment.

Creating the virtual environment to run our Python 3 application

Given these points, let us first create the virtual environment to run our Python 3 application. In order to do so, start a command prompt window and type in the following command:

python -m venv "%systemdrive%%homepath%\Documents\qr-code-app-env"

After the command completes, you will find the Python 3 virtual environment within the %systemdrive%%homepath%\Documents\qr-code-app-env folder.

Installing the application dependencies into the virtual environment

Next, activate your Python 3 virtual environment by running the following command:

"%systemdrive%%homepath%\Documents\qr-code-app-env\Scripts\activate.bat"

After your Python 3 virtual environment got activated, run the following command to install the Python dependencies for the application:

pip install -r "%systemdrive%%homepath%\Documents\qr-code-app\requirements.txt"

When the dependencies are installed, you will be able to run your Python application within the virtual environment.

Creating the .bat file to run our Python 3 application in its own virtual environment

At this point in time, you are ready to create a .bat file to start your application within the virtual environment. Given that, create a run_app.bat in the %systemdrive%%homepath%\Documents\qr-code-app folder with the following content:

call ..\qr-code-app-env\Scripts\activate.bat
call python run_app.py

Downloading a copy of NSSM

After you had created the windows batch file to run the your Python application within the virtual environment, proceed to download NSSM. Save the .zip file and extract its content. After extracting the content, you will find a nssm.exe file inside the win32 and win64 folder.

Depending on whether your Windows is 32 bit or 64 bit, you will use the corresponding nssm.exe file to install your Python application as a Windows Service.

For the purpose of this tutorial, let’s assume that we want to use the nssm.exe in the C:\nssm-2.24\win64 folder.

Run a command prompt instance as administrator

In order to avoid being prompted for administrator access for subsequent calls to C:\nssm-2.24\win64, let’s run a command prompt instance as administrator.

We will run the subsequent commands in this command prompt instance.

Installing your Python application as a Windows Service with nssm.exe

Given that our nssm.exe is located in C:\nssm-2.24\win64, we can then use the following command to install our Python application as a Windows Service:

C:\nssm-2.24\win64\nssm install qrcodeapp "%systemdrive%%homepath%\Documents\qr-code-app\run_app.bat"

When you run the command, NSSM will install your Python 3 application as a Windows Service.

At this point in time, your Windows Service is not yet started.

Enabling log files for your Python 3 application

When you start your Windows Service at this point in time, it will not generate any log files when it runs.

Therefore, let’s configure it to channel application output to log files.

In order to do so, first start NSSM service editor with the following command:

C:\nssm-2.24\win64\nssm edit qrcodeapp

After the command run, NSSM service editor will appear for enabling log files for your Python 3 application.

nssm-2.24 service editor for editing qrcodeapp written in Python 3 Flask

Given that, click on the I/O tab and enter the paths that you want the log files to appear:

nssm-2.24 service editor for qrcodeapp with output and error log paths set

Once you are done with that, click Edit service.

Starting your Python application as a Windows Service

In order to start the Windows Service for your Python application, run the following command:

C:\nssm-2.24\win64\nssm start qrcodeapp

Since Startup Type for the Windows Service is set to Automatic, our Python 3 Flask application will run automatically when we had started our Windows machine.

Further usages of NSSM

At this point in time, we will have fulfilled our goal of using NSSM to run a Python 3 application as a Windows Service in its own Python 3 virtual environment.

In addition to what I had covered, the nssm.exe contains several other functionalities.

In case you need to use other functionalities of NSSM, you can refer to NSSM usage page for more information.

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Копия windows выполняемая на этом компьютере не прошла проверку на подлинность windows 7 как убрать
  • Half life 1 for windows 10
  • Windows 10 принудительное удаление обновлений
  • Как убрать пароль на windows 10 pro при входе в систему
  • Как сбросить ноутбук до заводских настроек без переустановки windows