Python Windows Service Manager
Python Windows Service Manager is a python package that helps to easily manage windows services, mirroring the functionality of the Windows Service Controller Utility (sc.exe). It allows to easily manage both Driver and Win32 services. The following can be achieved using this package:
- Create, Remove, or Update a service configuration
- Query a specific or all Service Statuses
- Get a service configuration
- Verify a service exists
- Start, Stop, Restart, Pause, Continue, Interrogate a service
Pre-requisites
Installation
- Clone this repository
- Put the repo dir in your PYTHONPATH
- Run:
How to run Unit Tests
- pip install nose, mock
- From the module’s root directory, run ‘nosetests .’
Usage
This module attempts to follow the ORM design Pattern, where the Class WindowsServiceConfigurationManager would be your context, and object that represents the data store is a ServiceEntity.
WindowsServiceConfigurationManager
This is the entry point for all operations. This module has static methods that:
- Creates or Gets a Service, and returns a Entity object that represents that service
- Query All Service Statuses
- Verify a service exists
- Gets a specific service’s status
The following Describes these operations in detail:
-
Create a New Service:
The CreateService() function takes a parameter of Type NewServiceDefinition. The following describes the NewServiceDefinition constructor and its arguments:
NewServiceDefinition:
-
serviceName (Required): The name of the service.
- Valid Values: Any non empty string
-
displayName (Required): Indicates the friendly name that identifies the service to the user
- Valid Values: Any non empty string
-
binaryPathName (Required): Specifies the path to the executable file for the service.
- Validate Values: Any non empty string that points to an executable
-
startType: Indicates how and when the service is started
- Valid String Values: AUTO_START, DEMAND_START, BOOT_START, DISABLED, SYSTEM_START, INTERACTIVE_SHARE_PROCESS, INTERACTIVE_OWN_PROCESS
- Default Value: DEMAND_START
-
serviceType: Represents the type of the service
- Valid String Values: WIN32_SHARE_PROCESS, WIN32_OWN_PROCESS, KERNEL_DRIVER, FILE_SYSTEM_DRIVER
- Default Value: WIN32_OWN_PROCESS
-
errorControl: Specifies how to proceed if the driver for the service or device fails to load or initialize properly
- Valid String Values: ERROR_CRITICAL, ERROR_IGNORE, ERROR_NORMAL, ERROR_SEVERE
- Default Value: ERROR_NORMAL
-
loadOrderGroup: The name of the load ordering group of which this service is a member
- Valid Value: String of Group Name
- Default Value: None
-
dependencies:
- Valid Value: Array of Existing Service Names
- Default Value: None
-
serviceStartName: Name of the account in which the service runs
- Valid Value: A valid user account as a string
- Default Value: The System Account
-
description: The description of the service
- Valid Value: Any string
- Default Value: An empty string
-
failureActions: Represents the action the service controller should take on each failure of a service
- Valid Value: FailureActionConfigurationType
- Default Value: None
** See ‘Complex Example for Creating Service’ for an example, or the Section that describes the ‘FailureActionConfigurationType’
-
failureFlag: Specifies whether recovery actions will be triggered when a service stops as the result of an error
- Valid Value: Boolean
- Default Value: False
-
preShutdownInfo: The time-out value, in milliseconds.
- Valid Value: long or int
- Default Value: 180,000 milliseconds
-
serviceSIDInfo: Represents a service security identifier
- Valid String Value: SID_TYPE_NONE, SID_TYPE_RESTRICTED, SID_TYPE_UNRESTRICTED
- Default Value: SID_TYPE_UNRESTRICTED
-
delayedAutoStartInfo: The value that indicates whether the service should be delayed from starting until other automatically started services are running.
- Valid Value: Boolean
- Default Value: False
Most simplistic example of creating a service
from pywinservicemanager.WindowsServiceConfigurationManager import CreateService from pywinservicemanager.NewServiceDefinition import NewServiceDefinition serviceName = 'TestService' displayName = 'MyTestService' binaryPathName = 'c:\\myBinary.exe' newServiceDefinition = NewServiceDefinition(serviceName=serviceName, displayName=displayName, binaryPathName=binaryPathName) myService = CreateService(newServiceDefinition) # Note that the Create Service Method just creates the entity in memeory. # To save it to the 'DataStore', you must call Save() myService.Save()
Most complex example of creating a service
from pywinservicemanager.WindowsServiceConfigurationManager import CreateService from pywinservicemanager.NewServiceDefinition import NewServiceDefinition import pywinservicemanager.ConfigurationTypes as ConfigurationTypes serviceName = 'TestService' displayName = 'MyTestService' binaryPathName = 'c:\\myBinary.exe' startType = 'DEMAND_START' serviceType= 'WIN32_OWN_PROCESS' errorControl= 'ERROR_IGNORE' loadOrderGroup = None dependencies= ['nsi'] description= 'This is a test Service' failureFlag = False preShutdownInfo= 18000 serviceSIDInfo = 'SID_TYPE_UNRESTRICTED' userName = None password = None delayedAutoStartInfo = False failureActionList = [] delay = 1000 failureActionList.append(ConfigurationTypes.FailureActionTypeFactory.CreateRestartAction(delay)) failureActionList.append(ConfigurationTypes.FailureActionTypeFactory.CreateRunCommandAction(delay)) resetPeriod = 1 rebootMsg = 'MyRebootMessage' commandLine = 'MyCommandLine' failureActions = FailureActionConfigurationType(failureActionList, resetPeriod, rebootMsg, commandLine) newServiceDefinition = NewServiceDefinition(serviceName=serviceName, displayName=displayName, binaryPathName=binaryPathName, startType=startType, serviceType=serviceType, errorControl=errorControl, loadOrderGroup=loadOrderGroup, dependencies=dependencies, serviceStartName=userName, description=description, failureActions=failureActions, failureFlag=failureFlag, preShutdownInfo=preShutdownInfo, serviceSIDInfo=serviceSIDInfo, delayedAutoStartInfo=delayedAutoStartInfo) myService = CreateService(newServiceDefinition) # Note that the Create Service Method just creates the entity in memeory. # To save it to the 'DataStore', you must call Save() myService.Save(password)
-
-
Delete a Service:
from pywinservicemanager.WindowsServiceConfigurationManager import GetService serviceName = "TestService" myService = GetService(serviceName) # Note that the GetService Method reads for the data store and creates the entity in memeory. # To delete it from the 'DataStore', you must call Delete() myService.Delete()
-
Query All Services Statuses:
Returns a list of each installed service’s status. (Please see status definition below for more details)from pywinservicemanager.WindowsServiceConfigurationManager import QueryAllServicesStatus statuses = QueryAllServicesStatus() print statuses
-
Service Exists:
from pywinservicemanager.WindowsServiceConfigurationManager import ServiceExists serviceName = 'TestService' serviceExists = ServiceExists(serviceName) print serviceExists
-
Get Service Status:
Returns a single service’s status (Please see status definition below for more details)from pywinservicemanager.WindowsServiceConfigurationManager import GetServiceStatus serviceName = 'TestService' serviceStatus = GetServiceStatus(serviceName) print serviceStatus
ServiceEntity
This is the object that maps to the service.
The object contains the following commands for each service:
- Save
- Delete
- Start
- Stop
- Pause
- Continue
- Interrogate
- GetServiceStatus
- UpdateConfiguration
- Exists
You need to make sure that the commands Pause, Continue, and Interrogate are able to be excepted by the service. The accepted commands are dependent on 2 things. First, if the service is configurated to accept such commands, and second, if the current state of the service allows that command to be called on the service. The code examples below shows how to deal with this. Furthermore, if a service is not in a «Running» state, than Stop cannot be called. Vis-a-versa, if a service is not in a «Stopped» state, then Start cannot be called.
-
UpdateConfiguration: Used to update a service’s configuration in memeory. You must call the save method to persist the service.
from pywinservicemanager.WindowsServiceConfigurationManager import GetService serviceName = 'TestService' myService = GetService(serviceName) myService.UpdateConfiguration('StartType', 'DEMAND_START') myService.Save()
-
Save: Saves the current state of the ServiceEntity as a service in the OS. You can pass a password as an argument to this function if one is needed, the default value is
None
from pywinservicemanager.WindowsServiceConfigurationManager import GetService serviceName = 'TestService' myService = GetService(serviceName) myService.UpdateConfiguration('ServiceStartName', 'MyDomain\\MyNewUser') myService.Save('MyNewPassword')
-
Delete: Deletes the Service
Deletes a service. Please note that you if your service is running, you will need to stop the service for it to be deleted. Also, if anything has a handle open to the service, those need to be closed as well. If Delete() is called on service in which a handle is open, then it will be ‘Marked for Deletion’ and will not be deleted until all handles are closed.from pywinservicemanager.WindowsServiceConfigurationManager import GetService serviceName = 'TestService' myService = GetService(serviceName) myService.Delete()
-
Start: Deletes the Service
Starts a given service that has is stopped. If the service is already started, the function will just return. Also, if the service does not return from the Start command within 30 seconds, a TimeoutException is thrownfrom pywinservicemanager.WindowsServiceConfigurationManager import GetService serviceName = 'TestService' myService = WindowsServiceConfigurationManager.GetService(serviceName) myService.Start()
-
Stop: Stops the Service
Stops a given service that is started. If the service is stopped, the function will just return. Also, if the service does not return from the Stop command within 30 seconds, a TimeoutException is thrownfrom pywinservicemanager.WindowsServiceConfigurationManager import GetService serviceName = 'TestService' myService = WindowsServiceConfigurationManager.GetService(serviceName) myService.Stop()
-
Restart: Restarts the Service
Restarts a given service that is started. If the service is stopped, this is equivelent to just calling start. If the service is running, then service will be stopped and then started. Also, if the service does not return from the Stop or Start command within 30 seconds, a TimeoutException is thrownfrom pywinservicemanager.WindowsServiceConfigurationManager import GetService serviceName = 'TestService' myService = WindowsServiceConfigurationManager.GetService(serviceName) myService.Restart()
-
Continue: Continues the Service after it was paused
Stops a given service that is Paused and/or has the value ACCEPT_PAUSE_CONTINUE in ControlsAccepted. If not, an exception will be thrown. Also, if the service does not return from the Continue command within 30 seconds, a TimeoutException is thrownfrom pywinservicemanager.WindowsServiceConfigurationManager import GetService serviceName = 'TestService' myService = GetService(serviceName) status = myService.GetServiceStatus() if 'ACCEPT_PAUSE_CONTINUE' in status['ControlsAccepted']: myService.Continue()
-
Pause: Pauses the Service
Pauses a given service that is Paused and/or has the value ACCEPT_PAUSE_CONTINUE in ControlsAccepted. If not, an exception will be thrown. Also, if the service does not return from the Pause command within 30 seconds, a TimeoutException is thrownfrom pywinservicemanager.WindowsServiceConfigurationManager import GetService serviceName = 'TestService' myService = GetService(serviceName) myServiceStatus = myService.GetServiceStatus().Status if 'ACCEPT_PAUSE_CONTINUE' in status['ControlsAccepted']: myService.Pause()
-
Interrogate: Interrogates the Service
from pywinservicemanager.WindowsServiceConfigurationManager import GetService serviceName = 'TestService' myService = GetService(serviceName) myService.Interrogate()
-
GetServiceStatus: Deletes the Service
Returns a the service’s status (Please see status definition below for more details)from pywinservicemanager.WindowsServiceConfigurationManager import GetService serviceName = 'TestService' myService = WindowsServiceConfigurationManager.GetService(serviceName) status = myService.GetServiceStatus() print status
-
Exists: Deletes the Service
Returns if the service existsfrom pywinservicemanager.WindowsServiceConfigurationManager import Exists serviceName = 'TestService' myService = WindowsServiceConfigurationManager.GetService(serviceName) print myService.Exists()
FailureActionConfigurationType
Represents the action the service controller should take on each failure of a service. A service is considered failed when it terminates without reporting a status of SERVICE_STOPPED to the service controller
The constructor of this object takes the following parameters:
- failureActionsTypeList
- Valid Value: List of FailureActionType Objects (see below)
- Default:Value None
- resetPeriodType: The time after which to reset the failure count to zero if there are no failures, in seconds
- Valid Value: int or ResetPeriodType(see below)
- Default:Value None
- rebootMessageType: The message to be broadcast to server users before rebooting in response to the SC_ACTION_REBOOT service controller action
- Valid Value: string or RebootMessageType (see below)
- Default:Value None
- commandLineType: The command line of the process for the CreateProcess function to execute in response to the SC_ACTION_RUN_COMMAND service controller action. This process runs under the same account as the service.
- Valid Value: string or CommandLineType (see below)
- Default:Value None
Example:
import pywinservicemanager.ConfigurationTypes as ConfigurationTypes
failureActionList = []
delay = 1000
failureActionList.append(ConfigurationTypes.FailureActionTypeFactory.CreateRestartAction(delay))
failureActionList.append(ConfigurationTypes.FailureActionTypeFactory.CreateRunCommandAction(delay))
resetPeriod = ConfigurationTypes.FailureActionConfigurationResetPeriodType(1)
rebootMsg = ConfigurationTypes.FailureActionConfigurationRebootMessageType('MyRebootMessage')
commandLine = ConfigurationTypes.FailureActionConfigurationCommandLineType('MyCommandLineCommand')
failureActions = ConfigurationTypes.FailureActionConfigurationType(failureActionList, resetPeriod, rebootMsg, commandLine)
#or
failureActionList = []
delay = 1000
failureActionList.append(ConfigurationTypes.FailureActionTypeFactory.CreateRestartAction(delay))
failureActionList.append(ConfigurationTypes.FailureActionTypeFactory.CreateRunCommandAction(delay))
resetPeriod = 1
rebootMsg = 'MyRebootMessage'
commandLine = 'MyCommandLine'
failureActions = ConfigurationTypes.FailureActionConfigurationType(failureActionList, resetPeriod, rebootMsg, commandLine)
More information about FailureActionConfiguration Mapping
FailureActionType
Represents an action that the service control manager can perform.
A FailureAction type can be reurned by the factory object FailureActionTypeFactory, where there are 4 methods defined and an int which represents the delaly as the input parameter:
- Factory Methods:
- FailureActionTypeFactory.CreateNoAction(delay): No action.
- FailureActionTypeFactory.CreateRestartAction(delay): Restart the service.
- FailureActionTypeFactory.CreateRebootAction(delay): Reboot the computer. If the service uses the reboot action, the caller must have the SE_SHUTDOWN_NAME [privilege](https:// msdn.microsoft.com/en-us/library/windows/desktop/aa379306(v=vs.85).aspx). For more information, see [Running with Special Privileges.](https://msdn.microsoft.com/en-us/library/ windows/ desktop/ms717802(v=vs.85).aspx)
- FailureActionTypeFactory.CreateRunCommandAction(delay): Run a command.
- delay: The time to wait before performing the specified action, in milliseconds.
Example:
import pywinservicemanager.ConfigurationTypes as ConfigurationTypes
myAction = ConfigurationTypes.FailureActionTypeFactory.CreateRestartAction(300)
More information about FailureAction
ResetPeriodType
The time after which to reset the failure count to zero if there are no failures, in seconds.
The input is of time int
Example:
import pywinservicemanager.ConfigurationTypes as ConfigurationTypes
resetPeriod = ConfigurationTypes.FailureActionConfigurationResetPeriodType(1)
FailureActionConfigurationRebootMessageType
The message to be broadcast to server users before rebooting in response to the SC_ACTION_REBOOT service controller action.
If this value is None, the reboot message is unchanged. If the value is an empty string («»), the reboot message is deleted and no message is broadcast.
Example:
import pywinservicemanager.ConfigurationTypes as ConfigurationTypes
rebootMessage = ConfigurationTypes.FailureActionConfigurationRebootMessageType("My Reboot Message")
FailureActionConfigurationRebootMessageType
The command line of the process for the CreateProcess function to execute in response to the SC_ACTION_RUN_COMMAND service controller action. This process runs under the same account as the service.
If this value is None, the command is unchanged. If the value is an empty string («»), the command is deleted and no program is run when the service fails.
Example:
import pywinservicemanager.ConfigurationTypes as ConfigurationTypes
commandLine = ConfigurationTypes.FailureActionConfigurationCommandLineType("myCmd.exe")
Further Information
For more information the windows API implemented in the package and/or how the service controller utility works, please consult win32service documentation
Пройдите тест, узнайте какой профессии подходите
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы
Быстрый ответ
Для того чтобы Python-скрипт мог работать в роли службы Windows, рекомендуется использовать библиотеку pywin32
. Создайте класс службы, который будет унаследован от win32serviceutil.ServiceFramework
, а затем переопределите методы SvcDoRun
и SvcStop
для управления запуском и остановкой службы. Установка и управление службой происходит через командную строку. Пример кода представлен ниже:
Для установки и запуска службы воспользуйтесь следующими командами:
Данный метод позволит вам работать со скриптом Python как с службой Windows.

Расширение функционала вашей службы
Использование 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
.
Бесперебойные обновления и поддержка служб
Обеспечивайте возможности «горячего» обновления и «мягкого» завершения работы службы для сокращения времени простоя. Нужно продумать надежный механизм обновлений, который позволит внедрять новые версии службы без ручного вмешательства.
Полезные материалы
- NSSM – the Non-Sucking Service Manager — инструмент для простого управления Python-службами.
- Как запустить скрипт Python как службу в Windows на Stack Overflow — обсуждения и решения от сообщества разработчиков.
- Полный пример службы от Microsoft — официальные рекомендации по созданию служб Windows.
- Руководство по PyInstaller — создание автономных исполняемых файлов из Python-скриптов.
- Планировщик задач для разработчиков от Microsoft — использование Планировщика задач Windows для автоматизации Python-скриптов.
- Supervisor: Система контроля процессов — мониторинг и контроль над продолжительными службами на Python.
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:
-
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()
-
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 forExecStart
and restart policy -
[Install]
says to start on system boot as a multi-user service
-
The
-
Now reload systemd and start the service:
sudo systemctl daemon-reload sudo systemctl start myscript.service
-
Check status with
systemctl status myscript
or view logs viajournalctl -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
andExecStartPost
to add prep/cleanup logic - Set resource limits under
[Service]
likeMemoryMax=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:
- Structure the Python script to handle service commands
-
Subclass
win32serviceutil.ServiceFramework
-
Implement lifecycle event handlers like
SvcDoRun
- 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 commands –
systemctl
,nssm
, andnet
start/stop services -
Know how to disable services – Run
systemctl disable
orsc 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.
Creating a Windows executable using PyInstaller is reasonably straightforward:
pyinstaller.exe myservice.py
however, this will A) create a folder with the executable and supporting files, and B) not actually work!
The reason it won’t work is that PyInstaller, while very clever, can’t find all imports to include, and in this case will miss including win32timezone
in the package. To fix this issue we can tell PyInstaller to include it:
pyinstaller.exe --hidden-import win32timezone myservice.py
Testing is important here – the build will succeed and then the program will fail in run time, so for complex application it becomes a case of trial and error – building, running to find any missing imports, then re-building after adding new --hidden-imports
.
With this, the service works and we could install it with myservice.exe install
, but I find I’d rather have a single file, which can be accomplished using the --onefile
flag:
pyinstaller.exe --onefile --hidden-import win32timezone myservice.py
This creates a single executable, easy to distribute, and it will look like it works. However, it will unpack files in the Windows temp folder, which normally gets periodically wiped – this is likely to break any non-trivial service running on Windows for a long time. The fix for this issue is to unpack it somewhere where Windows will leave it alone, for example the current folder whatever that may be:
pyinstaller.exe --runtime-tmpdir=. --onefile --hidden-import win32timezone myservice.py
This should work – but due to issue 4579 on PyInstaller (fix incoming here), can break unexpectedly depending on what you set the runtime-tmpdir
to – my solution has been to install PyInstaller from the PR until it is merged and released.
Problem
We may need to run a long-running application without human intervention. For example, we may want to monitor performance data on a schedule or react to specific events. The application should run in its session and not interfere with other applications. In this case, we can develop an application that runs as a Window Service (Altvater, 2017). Python applications have been proliferating for performing routine tasks in recent years. Therefore, IT professionals might want to run a Python application as a Windows service.
Solution
We can use the “Python for Windows Extensions” package (known as pywin32) to run a Python application as a Windows service. The pywin32 package allows us to use Win32 application programming interface (API) features on Python (PYPI, 2022). We often use the win32serviceutil module in this package to utilize APIs. For example, the module contains a base class “win32serviceutil.ServiceFramework” that helps create Windows services in Python (Robinson & Hammond, 2000). When creating a service in Python, we usually derive service classes from the base class. The service classes inherit methods and properties from the class. This approach simplifies working with Windows services.
We design a fictitious project to exhibit the process of creating a windows service in Python. MSSQLTips.com, owned by Edgewood Solutions, has served the SQL Server community since 2006. The website’s landing page presents titles and short summaries of the most recent tips. In addition, every article comes with a featured image. Images have many purposes when used on web pages, besides helping the page look great (Simolo, 2016). However, for some reason, occasionally, some images may not display. Therefore, the CTO at Edgewood Solutions wants to monitor all featured images on the landing page to make sure all images display correctly.
We may have various Python applications on the same machine that depend on different versions of the same packages. In this case, each application should run in an isolated environment. Therefore, this project needs to run in a Python virtual environment.
We also need to use the “win32serviceutil” module to interact with Windows services. After briefly covering these prerequisites, we write an abstract class derived from the “win32serviceutil.ServiceFramework” class. We then create a service class that implements the abstract methods in order to perform the service functionality. Finally, the article explores a step-by-step process of creating a Windows service.
The author tested the project on Windows 10 Pro 10.0 <X64> and ran the Python scripts with Python 3.9.7 (64-bit). The IDE is Microsoft Visual Studio Community 2022 (64-bit).
1 – Prerequisite
There is a growing collection of several thousand third-party packages maintained by the Python community worldwide. Each package may have several versions. To avoid the version conflict, it is a good idea to run a Python application on a Python virtual environment. In addition, Python has complete support for Windows services, which are long-running applications in a Windows system.
1.1 Running a Python Script in a Virtual Environment
The tip “Creating a SQL Server Agent Job to Run Python Scripts in a Virtual Environment” demonstrates the steps to install Python on Windows. Without loss of generality, we can follow the instructions in the tip to create a virtual environment in the “C:\pyvirtuals\monitor_landing_page” folder. Click here to download the complete source code that contains the configuration file, “requirements.txt,” for installing the virtual environment. We run the following commands to create the Python virtual environment for this project and install all necessary packages from the “requirements.txt” file:
REM 1, Create a Virtual Environment python -m venv C:\pyvirtuals\monitor_landing_page REM 2, Activate the Virtual Environment C:\pyvirtuals\monitor_landing_page\Scripts\activate.bat REM 3, Install the Necessary Packages pip install -r C:\pyapps\monitor_landing_page\requirements.txt
We copy all the Python scripts to the “C:\pyapps\monitor_landing_page” folder. The Python script “app.py” implements our business requirements. Before running the script as a Windows service, we manually run the script. We use the following command to run the Python script. Figure 1 illustrates the output of the program execution. We can also find the “log_file.txt” file in the “C:\pyapps\monitor_landing_page” folder. These outputs indicate that we run the Python script successfully.
REM Run the Python script in the virtual environment. python C:\pyapps\monitor_landing_page\app.py
Figure 1 Running the Python Script in the Virtual Environment
1.2 Using the win32serviceutil Module to Connect to a Windows Service
The service control manager (SCM), started at system boot, maintains a database of installed and driver services. The SCM can control services by using control requests. For example, when a service needs to start, the SCM issues a control request to the service. The service acts on the request and reports its status to the SCM. In addition, the SCM provides a user interface to allow the user to start and stop services manually. The SCM also provides an API to enable programmatic control of services. This feature allows us to use a program to control services (Robinson & Hammond, 2000).
The Python win32serviceutil module provides some handy utilities to control existing Windows services. For example, we can connect a Windows service with some Python functions. We can conduct some tests to gain hands-on experience using this module. We start a Command Prompt as an administrator so that these Python functions have permission to access the services. The author’s machine name is “myPy,” and we test these functions against the “SQL Server VSS Writer” service. Here are the function syntaxes and Python statements used in this test:
REM the function signature StartService( serviceName , args=None , machine=None ) StopService( serviceName , machine=None ) QueryServiceStatus( serviceName , machine=None ) import win32serviceutil win32serviceutil.StopService("SQL Server VSS Writer", "myPy") win32serviceutil.StartService("SQL Server VSS Writer", None, "myPy") win32serviceutil.QueryServiceStatus("SQL Server VSS Writer", "myPy")
Figure 2 demonstrates the steps to stop a Windows service. We use the “win32serviceutil.StopService” function to stop the service and the “win32serviceutil.QueryServiceStatus” function to check the service status. Both the functions return a SERVICE_STATUS structure. According to Microsoft Docs (MS Docs, 2021), the dwCurrentState “3” means that the service is stopping, and the dwCurrentState “1” suggests that the service is not running. The output indicates that we successfully stop the Windows service. Meanwhile, we can check the service status through the SCM interface. Figure 3 shows that the service stopped.
Figure 2 Stopping the SQL Server VSS Writer Service on the Computer Named myPy
Figure 3 Check the Service Status through the SCM Interface
2 – Creating Subclasses of the win32serviceutil.ServiceFramework Class
Because of some special requirements for Windows service, we use the executable, PythonService.exe, to host Python services. When a Python service starts, the executable creates an instance of the Python service class and delegates all service functionality to this instance (Robinson & Hammond, 2000). The Python service class should provide methods to interact with the SCM. The module win32serviceutil offers a base class, “win32serviceutil.ServiceFramework,” which includes some predefined methods, for example, ServiceCtrlHandler() and SvcRun(). Therefore, when writing a Python service, we often create a class inherited from the base class.
In practice, we often write an abstract service class that provides a blueprint for other classes. The abstract class provides a standard interface for different implementations of a component. This project creates an abstract class “PythonService” that inherits from the “win32serviceutil.ServiceFramework” class. This class is responsible for installing, debugging, starting, and stopping the service. We may share this class on different projects. Next, we write another class, “MointorImageService,” that inherits from the “PythonService” class. We concentrate on the service functionality in this class.
2.1 Creating the Abstract Class to Interact with the SCM
The “win32serviceutil.ServiceFramework” class has already defined some methods. Most Python services are a subclass of this base class. Inspired by the smallest possible Python service written in Robinson & Hammond’s book, we create an abstract class to interact with the SCM. The abstract class declares the three abstract methods, i.e., start(), stop(), and main(), in which the derived class should have implementations. The following Python code exhibits the class, and the comments help explain the code.
# Reference:
# Mastromatteo, D. (2018):
https://thepythoncorner.com/posts/2018-08-01-how-to-create-a-windows-service-in-python/
# Robinson, S. &
Hammond, M. (2000). Python Programming on Win32. Sebastopol, CA: O’Reilly Media.
import
win32event
import
win32service
import
win32serviceutil
from
abcimport
ABC,abstractmethod
class
PythonService(win32serviceutil.ServiceFramework,
ABC):
@classmethod
def
parse_command_line(cls):
”’
Parse the command line ”’
win32serviceutil.HandleCommandLine(cls)
# Override the method
in the subclass to do something just before the service is stopped.
@abstractmethod
def
stop(self):
pass
# Override the method
in the subclass to do something at the service initialization.
@abstractmethod
def
start(self):
pass
# Override the method
in the subclass to perform actual service task.
@abstractmethod
def
main(self):
pass
def
__init__(self,
args):
”’
Class constructor”’
win32serviceutil.ServiceFramework.__init__(self,
args)
# Create an event
which we will use to wait on.
# The “service
stop” request will set this event.
self.hWaitStop
= win32event.CreateEvent(None,
0, 0,None)
def
SvcStop(self):
”’Called
when the service is asked to stop”’
# We may need
to do something just before the service is stopped.
self.stop()
# Before we do
anything, tell the SCM we are starting the stop process.
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
# And set my event.
win32event.SetEvent(self.hWaitStop)
def
SvcDoRun(self):
”’Called
when the service is asked to start. The method handles the service functionality.”’
self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
# We may do something
at the service initialization.
self.start()
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
# Starts a worker
loop waiting either for work to do or a notification to stop, pause, etc.
self.main()
2.2 Writing the Subclass to Perform Service Functionality
We create another new class that inherits methods and properties from the abstract class. The new class concentrates on the service functionality and some service configurations. Therefore, the new class should implement the three abstract methods declared in the abstract class. First, we set the running condition in the start() method. We then negate the running condition in the stop() method. Finally, the main() method executes a loop when the running condition is valid. The class also defines the service name and sets the location of the PythonService.exe. The following block demonstrates the code. It is worth noting that the code contains startup code that handles the command line when run as a script. We can install, debug, and start the service using the startup code.
# Reference:
# Mastromatteo, D. (2018):
https://thepythoncorner.com/posts/2018-08-01-how-to-create-a-windows-service-in-python/
import
app
import
time
from
PythonService
import
PythonService
class
MointorImageService(PythonService):
# Define the class variables
_svc_name_ =
“MointorImageOnLandingPageService”
_svc_display_name_ =
“MSSQLTips Mointor
Images Service”
_svc_description_ =
“Mointor images on
the landing page of the MSSQLTips.com.”
_exe_name_ =
“C:\pyvirtuals\monitor_landing_page\Scripts\pythonservice.exe”
# Override the method
to set the running condition
def
start(self):
self.isrunning
=True
# Override the method
to invalidate the running condition
# When the service is
requested to be stopped.
def
stop(self):
self.isrunning
=False
# Override the method
to perform the service function
def
main(self):
while
self.isrunning:
app.check_web_page_images()
time.sleep(5)
# Use this condition
to determine the execution context.
if
__name__ ==
‘__main__’:
# Handle the command
line when run as a script
MointorImageService.parse_command_line()
3 –Creating a Windows Service Using Python
We have already tested the app.py script in the virtual environment. We also created the abstract class “PythonService” and its subclass “MointorImageService.” The “C:\pyapps\monitor_landing_page” folder should have these three files. All files from the virtual environment creation are in the “C:\pyvirtuals\monitor_landing_page” folder. We use this folder structure for demonstration purposes. In practice, we can use a different folder structure. The service account should have permission to write text into the log file. We then walk through a step-by-step process to run the Python scripts as a Windows service.
Step 1: Copy the executable, pythonservice.exe, to the virtual environment scripts folder.
Copy pythonservice.exe from the “C:\pyvirtuals\monitor_landing_page\Lib\site-packages\win32” folder to the “C:\pyvirtuals\monitor_landing_page\Scripts folder” (Lindsay, 2016). The service class variable “_exe_name” should contain the fully qualified path of the executable. The “MointorImageService” class definition demonstrates the class variable initialization. In practice, we often put the path into a configuration file.
Step 2: Open the command prompt as administrator.
Enter “command” in the Windows 10 search box. The “Command Prompt” appears in the pop-up list. Right-click on the “Command Prompt” item and select “Run as administrator” in the context menu (Glenn, 2021). We then change the directory to the “C:\pyapps\monitor_landing_page” folder.
Step 3: Run the pywin32_postinstall.py to register two dynamic link libraries.
The Python script pywin32_postinstall.py is in the “C:\pyvirtuals\monitor_landing_page\Scripts” folder. We first run the “C:\pyvirtuals\monitor_landing_page\Scripts\activate.bat” to activate the virtual environment. Then, we execute the Python script “C:\pyvirtuals\monitor_landing_page\Scripts\pywin32_postinstall.py” in the command window. Figure 4 presents the output of the two commands. Here are the two commands:
C:\pyvirtuals\monitor_landing_page\Scripts\activate.bat C:\pyvirtuals\monitor_landing_page\Scripts\pywin32_postinstall.py -install
Figure 4 Execute the pywin32_postinstall.py in the command window
If we want to unregister these two dynamic link libraries, we can execute the following command to roll back the change:
C:\pyvirtuals\monitor_landing_page\Scripts\pywin32_postinstall.py -remove
Step 4: Install the service
Execute the following command to install the service. Figure 5 demonstrates this step and presents the output. We can ignore the warning message because we have already run the pywin32_postinstall.py script. The “pythoncom39.dll” and “pywintypes39.dll” are in the “c:\Windows\system32” folder. The installation command also generates a Windows service log (Event ID 7045), as shown in Figure 6. The log briefly describes the newly installed service.
python MointorImageService.py install
Figure 5 Use the command prompt to install the service
Figure 6 The Window service log indicates the new installed service
Step 5: Start the service
There are several ways to start a service. A started service should report its status to the SCM and listen for control requests from the SCM. We can use the following command to start the service:
python MointorImageService.py start
To ensure that the service starts correctly, we can check the log file, which should look like the following block.
***Start***: 20/06/2022 22:48:42 ****End****: 20/06/2022 22:48:49 The execution time: 06.734439 seconds ***Start***: 20/06/2022 22:48:54 ****End****: 20/06/2022 22:48:59 The execution time: 05.593806 seconds
The SCM provides a user interface to control services, as shown in Figure 7. We can start the service process through the interface. By default, the service startup type is “manually,” meaning a user should start the service manually. However, we can change the type to “automatically” so that the service automatically starts when the system boots. In addition, the service account is LocalSystem, a predefined local account used by the service control manager. The SCM allows us to change the service account.
Figure 7 Use the Service Control Manager to manually services
Step 6 (optional): Stop the service
The SCM provides a user interface to allow us to stop Windows services manually. When we stop a service, the SCM issues a control request to the service. The service then reacts to this request and reports its status to the SCM before it terminates. We also can use the following command to stop the service.
python MointorImageService.py stop
Step 7 (optional): Debug the service
If the service does not work properly, we use the following command to execute the service in debug mode. We often use this command to test the service after installation. The logging module helps print the debug message to the console. Figure 8 shows the output of this command. We should turn off the console output when running the service on the production server.
python MointorImageService.py debug
Figure 8 Debug the Windows service
Step 8 (optional): Update the service
When changing the Python script, we can use the following command to update the service.
python MointorImageService.py update
Step 9 (optional): Uninstall the service
We can also remove the service using a command line, as shown below:
python MointorImageService.py remove
Summary
Running a Python application as a Windows service enables us to execute long-running Python applications that utilize Windows features. We can use the Service Control Manager (SCM) to control these services. The article created a fictitious project that extracts image information from the MSSQLTips’ landing page. We wrote Python scripts to scrape the web page. We then ran the Python application as a Windows service.
We briefly covered creating a Python virtual environment and installing third-party packages. We then explored the “win32serviceutil” module and used functions to control Windows services with Python. Next, the article introduced the “win32serviceutil.ServiceFramework” base class. After creating the abstract class derived from the base class, we wrote the service class to implement the abstract methods. Finally, we walked through the step-by-step process of creating a Windows service.
Reference
Altvater, A. (2017). What are Windows Services? How Windows Services Work, Examples, Tutorials and More. https://stackify.com/what-are-windows-services/.
Glenn, W. (2021). How to Open the Command Prompt as Administrator in Windows 8 or 10. https://www.howtogeek.com/194041/how-to-open-the-command-prompt-as-administrator-in-windows-8.1/.
Lee, X. (2005). Python: List Modules, Search Path, Loaded Modules. http://xahlee.info/python/standard_modules.html.
Lindsay.stevens.au. (2016). Using PythonService.exe to host python service while using virtualenv. https://stackoverflow.com/questions/34696815/using-pythonservice-exe-to-host-python-service-while-using-virtualenv.
MS Docs. (2021). SERVICE_STATUS structure. https://docs.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_status.
PYPI. (2022). pywin32 304 – Python for Window Extensions. https://pypi.org/project/pywin32/.
Robinson, S. & Hammond, M. (2000). Python Programming On Win32. Sebastopol, CA: O’Reilly Media.
Simolo, G. (2016). When Words Meet Pictures: How to Use Text and Images to Create Striking Articles for Readers. https://www.freelancewriting.com/freelancing/when-words-meet-pictures/.
Next Steps
- This tip uses the Python for Win32 (pywin32) extensions to create a Windows service that can run Python scripts. The ability to customize the service class allows us to implement various business requirements. However, the code provided in this article is for demonstration purposes. There is much room to improve the code. For example, we should put the executable location in a configuration file. Besides the pywin32 package, we may also use the Non-sucking Service Manager to run a Python application as a Windows service.
- Check out these related tips:
- Learning Python in Visual Studio 2019
- Python Programming Tutorial with Top-Down Approach
- Using Python to Download Data from an HTML Table to an SQL Server Database
- Creating a SQL Server Agent Job to Run Python Scripts in a Virtual Environment
- Using Windows Task Scheduler to Run a Python Script at Prescribed Times
- Using Advanced Python Web Scraping to Conduct a Web Content Audit
- Using Web Crawling in Python to Conduct a Website Content Audit