Setting up .NET Core service/daemon on Debian Linux OS

How to run .NET Core application as daemon on Linux

I found a lot of articles out here on this subject, but non of them actually made the daemon work from the first time. There was always some catch I had to chase and found in some other article posted online.

In this article I will try to cover as much as possible how to setup .NET Core daemon working on a clean Debian 9 Linux machine. Let's start with the steps

I use Visual Studio 2017 and Windows as my development environment, so I will do the development and compilation on my Windows machine and just move binaries to a Linux machine where we are going to setup the service/daemon.

.NET Core project

Note

Service implementation on Linux host has been updated for .NET Core 2.1. Please check this article
http://bit.ly/2JNuLnC for more details how to properly implement service for Linux environment with .NET Core 2.1 which handles SIGTERM for clean service stop

Unlike Windows, Linux does not have special concept and type of application as a service. Instead any console application can work as a service. Because of this, we are going to create a simple .NET Core console application.

It won't to any complex computation, we'll just save current date and time to a file on the filesystem every 5 seconds. Here is the sample code of Program.cs class

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading;

namespace Service.Sample
{
    class Program
    {
        static readonly CancellationTokenSource tokenSource = new CancellationTokenSource();
        static readonly String outFolderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "output");
        static readonly String outFilePath = Path.Combine(outFolderPath, $"{DateTime.Now.ToString("yyyy-MM-dd")}.txt");

        static void Main(string[] args)
        {
            AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;

            if(!Directory.Exists(outFolderPath))
            {
                Directory.CreateDirectory(outFolderPath);
            }
            Console.WriteLine($"Output file: {outFilePath}");
            File.AppendAllLines(outFilePath, new List<String>() { "------------------- SERVICE START ------------------- " }, Encoding.UTF8);

            while (!tokenSource.Token.IsCancellationRequested)
            {
                File.AppendAllLines(outFilePath, new List<String>() { DateTime.Now.ToString() }, Encoding.UTF8);
                Console.WriteLine(DateTime.Now);
                Thread.Sleep(5000);
            }

            File.AppendAllLines(outFilePath, new List<String>() { "------------------- SERVICE STOP ------------------- " }, Encoding.UTF8);
        }
        private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
        {
            File.AppendAllLines(outFilePath, new List<String>() { "------------------- PROC EXIT SIGNAL ------------------- " }, Encoding.UTF8);
            tokenSource.Cancel();
        }
    }
}

    

At the end of the article I will explain why I put two places for writing service stop event.

From Visual Studio publish your project and copy output folder to your Linux machine to /etc/SampleService folder via FTP. If you do not have permissions to copy to this folder via FTP, upload published folder to your home and then just simply copy it from the terminal on Debian host.

Setting up .NET Core

I already explained how to install .NET Core in article How to setup .NET Core 2 on Debian or Ubuntu Linux distro the easy way but I will make some small adjustments to the installation to make it more usable for running services, so I will shortly explain here how to setup .NET Core support.

Execute the following commands one by one in your terminal

mkdir $HOME/dotnet   
  
cd $HOME/dotnet   
  
wget https://download.microsoft.com/download/D/7/2/D725E47F-A4F1-4285-8935-A91AE2FCC06A/dotnet-sdk-2.0.3-linux-x64.tar.gz 

tar zxf dotnet-sdk-2.0.3-linux-x64.tar.gz

sudo cp -r ./dotnet /bin

sudo apt-get install -y libunwind-dev

export PATH=$PATH:/bin/dotnet 
    

After this you will have .NET Core support installed on your machine. To double check execute the following command in terminal

/bin/dotnet/dotnet --version
    

You should get 2.0.3 as output

Service/Daemon setup

First thing is to check that we have systemd package installed

sudo apt-get install -y systemd
    

Second thing is to setup the user we are going to run our service under. I named it dotnetuser

useradd -m dotnetuser -p dotnetpass
    

Finally we can go and setup the service configuration. Navigate to systemd configuration folder 

cd /etc/systemd/system
    

Open nano, paste the following content and save as dotnet-sample-service.service

[Unit]
Description=Dotnet Core Demo service

[Service]
ExecStart=/bin/dotnet/dotnet Service.Sample.dll
WorkingDirectory=/etc/SampleService/
User=dotnetuser
Group=dotnetuser
Restart=on-failure
SyslogIdentifier=dotnet-sample-service
PrivateTmp=true

[Install]
WantedBy=multi-user.target
    

More about .service configuration files and options you can include in your service configuration files you can find at https://www.digitalocean.com/community/tutorials/understanding-systemd-units-and-unit-files so I'll not go deeper into this.

Now we need to tell systemd to reload service configurations and create metalink for our service

systemctl daemon-reload
systemctl enable dotnet-sample-service.service
    

Our service is ready now for start

systemctl start dotnet-sample-service.service
systemctl status dotnet-sample-service.service
    

Start Net Svc

Since our code will try to write file to /etc/SampleService/output folder, our service start might fail because of insufcient permissions to perform writing to this location. It's not a big deal, just go create the folder and change the cmod

mkdir /etc/SampleService/output

chmod 777 mkdir /etc/SampleService/output
    

Another way to check if your service is up and running is to check it via top or my favorite htop package. The first one comes pre-installed but if you want to use htop you will have to install it

sudo apt-get install htop
    

Htop Net Svc

To stop the service, just call systemctl stop.

systemctl stop dotnet-sample-service.service

systemctl status dotnet-sample-service.service
    

Stop Net Svc 

Tracing service log

As in any .NET Core application, you can setup logging, but you can also just write everything to console. It is much easier for debugging and guess what, you can get all these values from the service run. 

All these values are traced and you can easily fetch them

journalctl --unit dotnet-sample-service --follow
    

Handling service stop in your code

Unlike windows service, .NET Core console is not aware of service stop request from the systemctl command, there for, if you check the output file of this sample service upon stop, you will not find the comment line which comes after while loop.

This is something that is missing in .NET Core so far, but considering the speed of framework evolving, I am sure it will be introduced in a future releases

References

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.


About the author

DEJAN STOJANOVIC

Dejan is a passionate Software Architect/Developer. He is highly experienced in .NET programming platform including ASP.NET MVC and WebApi. He likes working on new technologies and exciting challenging projects

CONNECT WITH DEJAN  Loginlinkedin Logintwitter Logingoogleplus Logingoogleplus

JavaScript

read more

SQL/T-SQL

read more

Umbraco CMS

read more

PowerShell

read more

Comments for this article