Question: I have managed to cobble together a working demo of a pywin32 Windows service running Flask inside the Pylons waitress WSGI server (below). A nice self contained solution is the idea... I have spent hours reviewing and testing ways of making waitress exit cleanly (like this and this), but the best I can do so far is a kind of suicidal SIGINT which makes Windows complain "the pipe has been ended" when stopping through the Services control panel, but at least it stops :-/ I guess the pythonservice.exe which pywin32 starts, should not terminate, just the waitress treads? To be honest I'm still uncertain if this is a question about waitress, pywin32, or maybe it's just plain Python. I do have the feeling the answer is right in front of me, but right now I'm completely stumped. import os import random import signal import socket from flask import Flask, escape, request import servicemanager import win32event import win32service import win32serviceutil from waitress import serve app = Flask(__name__) @app.route('/') def hello(): random.seed() x = random.randint(1, 1000000) name = request.args.get("name", "World") return 'Hello, %s! - %s - %s' % (escape(name), x, os.getpid()) # based on https://www.thepythoncorner.com/2018/08/how-to-create-a-windows-service-in-python/ class SMWinservice(win32serviceutil.ServiceFramework): '''Base class to create winservice in Python''' _svc_name_ = 'WaitressService' _svc_display_name_ = 'Waitress server' _svc_description_ = 'Python waitress WSGI service' @classmethod def parse_command_line(cls): ''' ClassMethod to parse the command line ''' win32serviceutil.HandleCommandLine(cls) def __init__(self, args): ''' Constructor of the winservice ''' win32serviceutil.ServiceFramework.__init__(self, args) self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) socket.setdefaulttimeout(60) def SvcStop(self): ''' Called when the service is asked to stop ''' self.stop() servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STOPPED, (self._svc_name_, '')) self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) win32event.SetEvent(self.hWaitStop) def SvcDoRun(self): ''' Called when the service is asked to start ''' self.start() servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, '')) self.main() def start(self): pass def stop(self): print 'sigint' os.kill(os.getpid(), signal.SIGINT) def main(self): print 'serve' serve(app, listen='*:5000') if __name__ == '__main__': SMWinservice.parse_command_line() 

Answer: I have found a solution using a sub-thread that seems to work. I am not quite sure if this may have possible unintended consequences yet... I believe the updated version below, "injecting" a SystemExit into the waitress thread is as good as it gets. I think thee original kills the thread hard, but this one prints "thread done" indicating a graceful shutdown. Corrections or improvements welcome! import ctypes import os import random import socket import threading from flask import Flask, escape, request import servicemanager import win32event import win32service import win32serviceutil from waitress import serve app = Flask(__name__) # waitress thread exit based on: # https://www.geeksforgeeks.org/python-different-ways-to-kill-a-thread/ @app.route('/') def hello(): random.seed() x = random.randint(1, 1000000) name = request.args.get("name", "World") return 'Hello, %s! - %s - %s' % (escape(name), x, os.getpid()) class ServerThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): print('thread start\n') serve(app, listen='*:5000') # blocking print('thread done\n') def get_id(self): # returns id of the respective thread if hasattr(self, '_thread_id'): return self._thread_id for id, thread in threading._active.items(): if thread is self: return id def exit(self): thread_id = self.get_id() res = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, ctypes.py_object(SystemExit)) if res > 1: ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0) print('Exception raise failure') class SMWinservice(win32serviceutil.ServiceFramework): _svc_name_ = 'WaitressService' _svc_display_name_ = 'Waitress server' _svc_description_ = 'Python waitress WSGI service' @classmethod def parse_command_line(cls): win32serviceutil.HandleCommandLine(cls) def __init__(self, args): win32serviceutil.ServiceFramework.__init__(self, args) self.stopEvt = win32event.CreateEvent(None, 0, 0, None) socket.setdefaulttimeout(60) def SvcStop(self): servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STOPPED, (self._svc_name_, '')) self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) win32event.SetEvent(self.stopEvt) def SvcDoRun(self): servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, '')) self.main() def main(self): print('main start') self.server = ServerThread() self.server.start() print('waiting on win32event') win32event.WaitForSingleObject(self.stopEvt, win32event.INFINITE) self.server.exit() # raise SystemExit in inner thread print('waiting on thread') self.server.join() print('main done') if __name__ == '__main__': SMWinservice.parse_command_line()