Create Windows Service In Python 3

Python for windows is pretty powerful. Largely by its ability to complete extensive tasks in very few lines of code. Legibility is maybe tougher, but it is a great tool when scripting or tool building.

Python gets even better with its support to run as a Windows Service. The main problem though with it is the shortage of documentation. Hopefully this post will change that.

Note the following tutorial was carried out on Windows 10 Pro x64. mborus was also able to carry out the following tutorial on Windows 7 x86 with only some minor path changes!

Installing Python 3 on Windows

First up is installing python 3 for windows. This is pretty easy - just download from For this demo I used specifically Python 3.6.1 (

Next, Python does lack some native library support needed to create services in windows - so you also need to install the python windows extension library here: This process is quite a pain to sift through, as you need to dig through the version numbers and match your python version and the python bit version. If you downloaded python 3.6.1 as I did, it has likely installed an x86/32-bit version of python 3.6.1. The corresponding extension I downloaded was in the Build 221 folder here: From here I downloaded pywin32-221.win32-py3.6.exe. Note anything with arm64 in the name is x64/64-bit.

UPDATE - April 28/18: I've been building some more recent tools with newer versions of python and pywin32, and additionaly been working with 64 bit python rather than 32 bit. The 32bit version I found has memory limitations when working with multi-processing or multi-threading. For my latest setup I used Python 3.6.5 and pywin32 release 223. To download these I downloaded the 64-bit amd64 version of python 3.6.5: and then downloaded for release 332 from pywin32's new github page in their releases: The previous sourceforge location is still kept for older versions as an archive but is no longer maintained.

Up to this point everything is auto-magically installed into place. For your service to run though, the python installation is missing required PATH variable information, so to do this you need to include a number of routes in your system and user PATH variables.

Open your windows environment variables and add the following to the system (note it must be the system path as windows services don't run under your current user by default) and user PATH variables. You will need to tweak them to your computer as Python is automatically installed in the AppData of the logged in user:

C:\Program Files\Python 3.6  

The DLL path is specifically needed so that Python can generate and run the Windows Service.

From mborus, for Windows 7 x86, only the following paths are needed in order to get the python service to run:


Note that if you have installed Python on your computer differently, or it is not located in the AppData folder, you may need to do some more searching in order to correctly find the location of the DLL and Scripts folders.

Starting Your Service Project

Now that you have everything setup with Python 3, you can build your service. Documentation here is sparse, but I was able to find another person blog and a stack overflow post to find everything needed to get started. The original posts are as follows:

From these two I was able to come up with this for a starting template

import win32serviceutil  
import win32service  
import win32event  
import servicemanager  
import configparser  
import os  
import inspect  
import logging  
from logging.handlers import RotatingFileHandler

class AppServerSvc (win32serviceutil.ServiceFramework):  
    _svc_name_ = "MyService"
    _svc_display_name_ = "My Service"

    _config = configparser.ConfigParser()

    def __init__(self,args):
        self.hWaitStop = win32event.CreateEvent(None,0,0,None) + '/config.ini')
        print(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + '/teconfig.ini')

        logDir = self._config["DEFAULT"]["loggingDir"]
        logPath = logDir + "/service-log.log"

        self._logger = logging.getLogger("MyService")
        handler = RotatingFileHandler(logPath, maxBytes=4096, backupCount=10)
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

    def SvcStop(self):

    def SvcDoRun(self):
                              (self._svc_name_,''))"Service Is Starting")


    def main(self):
        # your code could go here
        rc = None
        while rc != win32event.WAIT_OBJECT_0:

            # your code here...

            # hang for 1 minute or until service is stopped - whichever comes first
            rc = win32event.WaitForSingleObject(self.hWaitStop, (1 * 60 * 1000))

            # your code also here ...

if __name__ == '__main__':  

The above code is pretty straight forward. the SvcStop method is called when your service is stopped in windows. The SvcDoRun is called when your method is started. To keep the code more sane there is also a main method which is called at start. Within this method all of your services logic should be called. Ontop of these basic components I also added a logger and configuration loader. The configuration loader looks for a config.ini located within the same directory as the service script is above. From within this config the logging directory can be found - writing to the folder location specified in the config to the file service-log.log. _svc_name_ and _svc_display_name are both required attributes needed for the service installation. These will work as your display names and calling names from terminal.

Install Your Service

Installing your service in python is extremely easy. Open a terminal with Administrator privileges and cd to the location of your service script. Type the following:

python <nameOfFile>.py install  

This will install your service. For additional options simply run your script with no parameters. You can update or remove your service at any time using the appropriate update and remove commands, or even run in debug with debug

Extra Tips

One of the most common errors from windows when starting your service is Error 1053: The service did not respond to the start or control request in a timely fashion. This can occur for multiple reasons but there are a couple things to check when you do get them:

  • Make sure your service is actually stopping:Note the `main` method has an infinite loop. The template above with break the loop if the stop even occurs, but that will only happen if you call `win32event.WaitForSingleObject` somewhere within that loop; setting `rc` to the updated value
  • Make sure your service actually starts: Same as the first one, if your service starts and does not get stuck in the infinite loop, it will exit. Terminating the service
  • Check your system and user PATH contains the necessary routes: The DLL path is extremely important for your python service as its how the script interfaces with windows to operate as a service. Additionally if the service is unable to load python - you are also hooped. Check by typing `echo %PATH%` in both a regular console and a console running with Administrator priveleges to ensure all of your paths have been loaded
  • Give the service another restart: Changes to your `PATH` variable may not kick in immediately - its a windows thing

ProGuard, Gradle BuildTypes & Dynamic Class Loading In Android

Android has many great build tools for compiling and configuring your app in the most ideal and organized way possible, but not without some unknown tricks that can throw you (or mostly me) off when trying to get there. The problems I ran into was when configuring gradle build types with proguard and my app which contained dynamicaly loaded classes. Arguably, not loading classes dynamicaly would have avoided this entire issue, but thats not the point, and for the future functionality of my app, was not an option.

The problem starts when trying to configure FireBase into my application. The recommended setup to seperate your development event logs from production development logs, is to have seperate applicationIds. This lead to configuring my with the following (inside the android {} block) :

defaultConfig {

    applicationId "com.projectterris.myapplication"
    minSdkVersion 21
    targetSdkVersion 23
    versionCode 1
    versionName "1.0.0"
    multiDexEnabled true

buildTypes {  
    release {
        minifyEnabled true
        shrinkResources true
        proguardFiles getDefaultProguardFile("proguard-android.txt"), ""

debug {  
    applicationIdSuffix ".debug"
    debuggable true

    minifyEnabled true
    shrinkResources true
    proguardFiles getDefaultProguardFile('proguard-android.txt'), ''

Normaly, its not ideal to run proguard in your debug builds, as it causes your stack traces to be obfusicated, which causes issues when debugging. When I discovered this issue, I was testing my proguard setup and wanted to see the setup without having to create a release type.

After syncing and building I started getting these errors. Note my app was written in Kotlin, if your in Java and you have this issue, youll get roughly the same errors as Kotlin compiles down to Java bytecode anyway

Caused by: java.lang.ClassNotFoundException: Didn't find class "com.projectterris.myapplication.debug.datastore.dll.SQLiteHelper" on path: DexPathList[[zip file "/data/app/com.projectterris.myapplicaiton.debug-1/base.apk"],nativeLibraryDirectories=[/data/app/com.projectterris.myapplication.debug-1/lib/arm, /vendor/lib, /system/lib]]  

I eventualy discovered this was because of how my SQLiteHelper class was being dynamicaly loaded.

val classLoader =  
val aClass = classLoader.loadClass("com.projectterris.myapplication.datastore.dll.SQLiteHelper")  
val ctor = aClass.getDeclaredConstructor(  
val instance = ctor.newInstance(context) as SQLiteHelper  

Now you may be thinking "oh your fully qualified name does not contain 'debug' in it", but this causes an interesting problem. It seems that when you have different applicationIds configured in gradle, the package name to find all your compiled classes changes aswell. So for a release build the SQLiteHelper's canonical name is com.projectterris.myapplication.datastore.dll.SQLiteHelper and for debug it is com.projectterris.myapplication.debug.datastore.dll.SQliteHelper.

The fix for this can be implemented in a couple of different ways. For one, every android application has a BuildConfig object which contains a boolean variable that can be accessed as BuildConfig.DEBUG. This value will return true if your BuildType is debug. Consistency in this variable working though in Android Studio is argued alot on StackOverflow, so an alternative is preferrable. Another option is that you can set variables in gradle that can be accessed via the BuildConfig object.

Both of these options work in most cases, except for when proguard gets involved, and brings us to the next bug I encountered in my app. Proguard is basicaly the minifyer for Java/Kotlin, but in the process of doing so, also changes all the names of your classes and methods. This makes using a static string in the classLoader.loadClass(String) method not an option. Regardless of whether .debug is included in the canonical name or not, the actual class name or package will not be equivelent to its uncompiled canonical name. The solution I found to this was to use reflection again in determining the canonical name. This way, I always have the complete and proper canonical name, regardless of proguard or my buildtype. The code in the ended looked like this:

val classLoader =  
val aClass = classLoader.loadClass(  
val ctor = aClass.getDeclaredConstructor(  
val instance = ctor.newInstance(context) as SQLiteHelper  

The next problem the appeared was from my constructor, which proguard decided at compile time was no longer needed and I ended up with errors like this:

Caused by: java.lang.NoSuchMethodException: <init> [class android.content.Context]  

This fortunatly was one of the easier fixes and is well described in the StackOverflow solution here :

Basicaly, what I needed to do was tell proguard not to remove the constructor entry, thus allowing me to dynamicaly instantiate it. Adding the following rule to my file, resolved this final issue:

-keepclassmembers class com.projectterris.myapplication.datastore.dll.SQLiteHelper{
 public <init>(android.content.Context);

Note that proguard rules can be used extensively and are quite powerful. The StackOverflow link above shows how you can also configure proguard to exempt any class that extends a common super class. This could be another fourth alternative to using the relective canonical name when loading dynamic classes with classLoader.loadClass(String). I haven't tested this, but telling proguard not to obfusicate the SQLiteHelper class could greatly simplify implementation, and reduce the reliance on reflection (which is known to be slow)

I ended up in this process also with AndroidManifest.xml permission errors not being shared between my .debug and normal applicationId apps, but later found out this was just intermediate files not being deleted properly. A clean and rebuild was able to resolve that issue. And so my project continues...

Volume "filesystem root has only 0 bytes of disk space" Errors on Fedora 24


Recently I encountered an unpleasent system error on Fedora 24. "filesystem root has only 0 bytes of disk space" after some googling, resolved quite simply to the root partition / being out of space. The problem though was trying to find the right places to delete the right things. If you are like me and installed Fedora simply by following the GUI installer and largely accepted default settings, you end up with your HDD heavily repartitioned into various segments, including /dev, /home, /boot and / being root. Any data on your computer not stored within the other segments is fair game to be taking up space in the / root segment. I was able to find though a number of useful tools for confirming and clearing out old data stored on Fedora 24 and cleanup the / root segment.

Viewing Your Segments

You can view all segments on your system using the df command. This is useful to confirm the problem is the space on your system You'll get an output looking something like this:

As of this writing and the above screenshot, you can see I've already been able to clear out space from my / root partition under the Mounted on column. The / root partition was given 50GB on my system, and I was able to cut down the used space to 82% which was acceptable for me for the time being.

Remove Orphan Packages

On debian there is a tool called deborphan which can be installed to remove "orphaned packages" or packages that no other package depends on. In Fedora there are couple of different commands to replicate this functionality.


package-cleanup is a yum-utils package that still works on Fedora 24, even with yum no longer being used. You can cleanup orphaned packages with the following command

package-cleanup --quiet --leaves --exclude-bin | xargs dnf remove -y  

package-cleanup can sometimes be too bold, so the --exclude-bin parameter is included to avoid potentialy important packages from being removed. The results from package-cleanup are then piped into xargs and the dnf remove command as parameters. --quiet is used to minimize the output and --leaves is the command to look for packages which aren't being relied on by currently installed packages.


For rpm based systems there is an actual identical tool to deborphan named rpmorphan. This tool does similar to mentioned above. If your system though gets the "filesystem root has only 0 bytes of disk space" you will not have any space available to install this package. So you will likely have to execute the package-cleanup command above before you can install rpmorphan. rpmorphan will also look for unused libraries and can also be configured to do further and more braver sweeps of packages. To run rpmorphan, execute the following commands

sudo dnf install rpmorphan  
sudo dnf remove `rpmorphan`  

The first command will install the package, followed by executing and removing the resulting packages from rpmorphan


dnf includes a parameter called autoremove which operates similarily to apt-get autoremove in that it will remove unused depency packages. The problem I found with this command is that it can include programs you may want to keep. Running the call I found dnf wanting to remove programs such as firefox, thunderbird, and wireshark. So I would recommend you use this command with caution, if at all. To execute an autoremove, simply execute the following command

sudo dnf autoremove  

dnf will prompt you with a list of all the packages it will remove, at which point you can answer Y/N to proceed. Check the packages at this point to decide whether to proceed.

Remove Old Package Versions

From your already installed packages, Fedora has a habit of keeping the previously installed outdated versions after an upgrade. These obviously take up space. This can be done simply enough using dnf and a number of commands available through it. The following command will fetch and remove all outdated copies of packages installed on your system

sudo dnf repoquery --quiet --duplicated --latest-limit -1 | xargs -l1 dnf remove -y  

Whats happening in the above command is dnf is making a repoquery for all duplicate packages using the --duplicated flag. These results though will also include the latest version, so a filter is applied using the --latest-limit flag. By setting this to -1 we tell dnf to not include the latest version in its repoquery. This thus generates a list of duplicated packages on your system, not including the latest installed version. This is all then piped into xargs and into the dnf remove command. When I execute this command, older versions of systemd packages showed up. systemd packages are obviously important, but you don't have to worry about them as dnf will not allow you to uninstall them, and will simply skip them as it processes the list

Remove PackageKit Meta

Using the GNOME Disk Usage Analytics tool I discovered the /var/cache/PackageKit folder was taking up 16GB of space in the / root segment. Within this folder is kept cache copies of different programs on your system. These are used by GNOME and not by dnf and is only referred to if you use the GNOME GUI to install your rpm applications. Clearing this cache will give you a large amount of space back in the / root segment. On Fedora, there is an included console client that will refresh the PackageKit system. Execute the following command

sudo pkcon refresh force -c -1  

The refresh refreshes the cached information and the -c command sets the cache age for how old the data can be before it is removed. By passing -1 it sets the cache age for to none, and thus deletes the cache.

Interestingly this command doesn't always work, and after refreshing on the GNOME Disk Usage Analytics tool, still had a large amount of data being stored in the PackageKit folder. In this case, some manual work needs to be done. You can manually clear the folder with the following command

sudo rm -rf /var/cache/PackageKit/metadata/updates/packages/*  

After doing this open the PackageKit config file located at /etc/PackageKit/PackageKit.conf and uncomment the following line within the file


This will stop the PackageKit from caching anymore packages in the future and hopefully mitigate the need to repeat this cleaning process

Cherry Pick Broken and Orphaned Packages

During an upgrade, certain packages may not be easily retrievable using the above methods. Fortunatly dnf comes with a couple of tools to spot out this information. The following commands will only list these odd packages. These calls typicaly will include packages you want to keep, so to use these you will have to run them and then remove them each individualy.

sudo dnf list extras  

This command will list extra packages found on the system that the current version of dnf cannot do much with. An example is if you are upgrading from Fedora 21 to 22, the above command will list packages from Fedora 21 that are of no longer use to Fedora 22. You can identify these packages by their postfix (in our example ending with .fc21). This can be applied to any Fedora upgrade situation

sudo dnf repoquery --unsatisfied  

This command will list any packages that may have broken or has unsatisfied dependencies on your system. In most use cases, you probably will want to update or install the appropriate dependencies for the package, assuming you want to use whatever package that is unsatisfied, but if not, these packages will still be taking up room in their broken state on your system and can be removed to save space.

Install Apache Traffic Server From Source on Ubuntu 14.04.3/16.04.1


Building and configuring the Apache Traffic Server is super easy. There is only a few configuration changes to consider and some useful documentation areas to read over before beginning the procedure. This tutorial will build the server form source, but downloaded from the Apache Traffic Server website. Alternatively you can download from the repository or some Linux package managers such as apt-get and yum have older versions of the Traffic Server themselves. This tutorial will be using Apache Traffic Server 6.2.0.


download the program with wget by executing the following commands



Execute the following commands to configure the project. The command ./configure will check you have all of the required dependencies for the installation. You may have to run it several times to ensure everything is included. Much of the online documentation provides general names for what is being looked for, but not everything is included.

tar xvf trafficserver-6.2.0.tar.bz2  
cd trafficserver-6.2.0.tar.bz2  

One of the more difficult dependencies is the openssl dependency. This required installing openssl, libssl1.0.0, libssl-dev, libsslcommon2 and libsslcommon2-dev on unbuntu to pass the configuration check


Execute the next command to install

sudo make install  

Then execute the following and restart

sudo ldconfig  
sudo shutdown -r now  

You system will restart. ldconfig is a command that resets a number of control systems on linux. I ran into issues with the application until i ran this

Now your server is ready to go. Upon installation, the trafficserver command will be available anywhere in console. Simply start/stop/restart/status with a call like this:

sudo trafficserver <start/stop/restart/status>  

Keep an eye on the right hand side after each command as that will give u a status as to whether the command executed properly or not.

Debug / Problems

Errors can occur if the traffic server doesn't have permissions to the log files, which can cause more issues if you have other issues as well. By default the traffic server logs to the syslog daemon and information should be located in /var/log/trafficserver. Note if the trafficserver folder does not exist and does not have read/write permissions, logging will not work. Additionally, traffic server may write its logs to its install location under /usr/local/var/log/trafficserver. You can explicitly set the path that logs will be written to in the records.config file. Add or edit under the logging section of the file the proxy.config.log.logfile_dir attribute with the path to the desired logging location

If there are still issues and writing logs is not working, try executing the command traffic_ctl. This is another helper command for configuring the traffic server while it is still running. If there are errors, this command typicaly will dump them to console

Compile Thread Safe PHP 5.6.20 from Source


Compiling PHP 5.6.20 with thread safety is surprisingly straight forward. The first link mentioned in the sources actually is a shell script that will execute the process for you on Ubuntu Linux. be warned, there is a bug in that script though. Basically on line 39 of the script, the script clones the master branch of the pthreads project. This branch though is now setup for compatibility with PHP7. In order to compile pthreads with PHP5, you need to checkout the PHP5 branch on the repo before compiling.

We will be going through all of these steps in the tutorial below. This tutorial will walk through basically the above mentioned script as well as give equivalent commands so as to execute the script in Fedora 22 if that is your system instead. Commenting will be added beside any code if there is any difference as to whether to execute it in Ubuntu or Fedora 22. Additionally I am presenting this tutorial as if you were going to run this script by hand in the terminal. So you may find if you are reading along that a few steps appear out of order, this is merely for preference and maybe more intuitive ?

Update The System And Essential Packages

You should always make sure you have the latest packages. run updates for your system with the following commands

sudo apt-get update #Ubuntu  
sudo dnf update # Fedora 22  

Then install some essential tools you will need for downloading and manipulating the build

sudo apt-get install -y build-essential git-core vim curl #Ubuntu  
sudo dnf install -y git-core vim curl #Fedora 22  

Setup Terminal And Directories

For this tutorial I am assuming your executing everything in terminal, and not just running the script in the source links. So we need to set a couple of variables now

sudo mkdir /etc/php5ts


NOTE: At this point do not close your terminal before finishing this tutorial. The variables that have been set above are only kept as long as the current terminal session remains open. Closing the session will remove the PHP_DIRECTORY and PHP_TIMEZONE variables. You can test if the variables are set by entering the following commands


Build Thread Safe PHP 5.6.20

Download and extract PHP 5.6.20 from here: and navigate to the extracted root.

Install additional packages with the following commands:

sudo apt-get install -y make autoconf re2c bison #Ubuntu  
sudo dnf install -y make autoconf re2c bison #Fedora 22

sudo apt-get install -y libicu-dev libmcrypt-dev libssl-dev libcurl4-openssl-dev libbz2-dev libxml2-dev libpng-dev libjpeg-dev libedit-dev #Ubuntu

sudo dnf install -y libicu-devel libmcrypt-devel openssl* libcurl libcurl-devel bzip2-devel libxml-devel libpng-devel libjpeg-devel libedit-devel #Fedora 22  

Now lets start building PHP! At the PHP folder root enter the following commands

./buildconf --force

./configure --prefix=$PHP_DIRECTORY --with-config-file-path=$PHP_DIRECTORY --with-config-file-scan-dir=$PHP_DIRECTORY/conf.d --disable-all --enable-maintainer-zts --with-curl --with-openssl --with-gd --enable-gd-native-ttf --enable-intl --enable-mbstring --with-mcrypt --with-mysqli=mysqlnd --with-zlib --with-bz2 --enable-exif --with-pdo-mysql=mysqlnd --with-libedit --enable-zip --enable-pdo --enable-pcntl --enable-sockets --enable-mbregex --with-tsrm-pthreads

These commands will do additional checks for any missing packages on your system. If either of these command return errors of missing packages, Install them and then rerun the command.

If both run successfully without errors. Execute this next


This will compile 5.6.20 and could take awhile. After this has completed execute

make test  

This will test the compiled php that all functionality expected works. You should only see PASSES and SKIPS while it processes the tests. The test procedure will give you a summary at the end and may prompt you to send results to the php development team. You can say yes or no here. This testing process executes around 10k tests, so aswell, this step will take awhile.

Once complete, run the following command

sudo make install  
sudo cp php.ini-production /etc/php5ts/php.ini  

Great, you now have compiled php5 with threading capabilities. Lets now add the threading!

Add Threading

Download and clone the pthread project from Github. Also we need to checkout the PHP5 branch, since the master branch is being developed for PHP7

git clone  
git checkout PHP5  

Then install php5 dev tools

sudo apt-get install -y php5-dev #Ubuntu  
sudo dnf install -y php5-devel #Fedora 22  

Now lets build the pthread project

./configure --with-php-config=$PHP_DIRECTORY/bin/php-config

Again, configure will check for any additional dependencies needed that are not on your system. Install them and then rerun the configure step if you are given any errors when executing that command. Then as done when building PHP 5.6.20; compile, test and install pthread with the following commands

make test  
make install  

This process should not take as long. The test suite only has about 60 tests in comparison. Again you may be prompted to send your results to the php dev team. The choice is up to you.

Complete the installation with the following commands

mkdir $PHP_DIRECTORY/conf.d  
echo "" > /etc/php5ts/conf.d/pthreads.ini  

Congratulations, you have now completed the instalation of the pthreads extension. Lets configure our PHP 5.6.20 we build to use it!

Configure PHP 5.6.20 with pthread

Execute the following commands to update a number of settings in our compiled php to make it aware of the pthread extension

sudo su

sed 's#;date.timezone\([[:space:]]*\)=\([[:space:]]*\)*#date.timezone\1=\2\"'"$PHP_TIMEZONE"'\"#g' $PHP_DIRECTORY/php.ini > $PHP_DIRECTORY/php.ini.tmp

mv $PHP_DIRECTORY/php.ini.tmp $PHP_DIRECTORY/php.ini

sed 's#display_errors = Off#display_errors = On#g' $PHP_DIRECTORY/php.ini > $PHP_DIRECTORY/php.ini.tmp

mv $PHP_DIRECTORY/php.ini.tmp $PHP_DIRECTORY/php.ini

sed 's#display_startup_errors = Off#display_startup_errors = On#g' $PHP_DIRECTORY/php.ini > $PHP_DIRECTORY/php.ini.tmp

mv $PHP_DIRECTORY/php.ini.tmp $PHP_DIRECTORY/php.ini

sed 's#error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT#error_reporting = E_ALL#g' $PHP_DIRECTORY/php.ini > $PHP_DIRECTORY/php.ini.tmp

mv $PHP_DIRECTORY/php.ini.tmp $PHP_DIRECTORY/php.ini  

Execution of the mv steps may cause prompts to confirm whether to overwrite the php.ini file. Say yes to this.

Installation of PHP 5.6.20 has now been completed on your system. To use this thread safe version you will have to call it with /etc/php5ts/bin/php. Calling just php in your terminal will execute the dev php we downloaded as part of this compiling process, which is not thread safe. You can make your life easier by creating a symlink to the thread safe php.