nix-processmgmt: An experimental Nix-based process manager-agnostic framework
Burg, Sander van der
Nix is package manager that offers all kinds of powerful features to make package deployments reliable and reproducible. Although Nix can be used to conveniently deploy packages, on various operating systems (such as Linux and macOS), and even allows unprivileged users to deploy packages, deploying services (such as PostgreSQL and Apache HTTPD) still has its limitations. Currently, Nix-based service deployment is solved by a small number of solutions: NixOS requires you to adopt a fully Nixified Linux system and uses systemd as a process manager. nix-darwin only works on macOS with launchd as a process manager If you are using Nix on a conventional Linux distribution, a different operating system (e.g. FreeBSD), with a different process manager (e.g. supervisord), or as an unprivileged user, then there is no off-the-shelf solution that can help you (yet) to conveniently deploy Nix-provided services. The nix-processmgmt framework (-processmgmt) is a prototype that tries to provide universal Nix-based service deployment on all systems where Nix can be used. It offers the following features: * It uses simple conventions for describing process instances, e.g. function definitions and function invocations * It works with high-level deployment specifications that can universally target the following process managers: sysvinit, bsdrc, systemd, supervisord, cygrunsrv and launchd * Tested on the following operating systems: Linux, macOS, Cygwin and FreeBSD * Automatically derives the activation order from process dependencies * Allows you to deploy multiple instances of the same service * Unprivileged user deployments In this talk, I will provide background information about this framework, describe how it works, and show a number of real-life usage scenarios using commonly used services (PostgreSQL, Apache HTTPD etc.) in a number of interesting usage scenarios
 Our next talk is Nix Process Management, an experimental Nix-based process manager agnostic framework, and the presenter is named Sander van der Berg. This talk is about complementing Nix with any process manager. And a little bit more information about Sander, if you're not familiar, is that he is a Nix contributor since 2007 who's worked on so many things, such as like the FHF-shoot environments and the Nix Android build environment. And is a developer, well, the main developer for various Nix-related utilities such as DysNix, DysNomia, Notenix, and ComposerTenix. And I would also like suspect that a lot of NixOS users have probably encountered Sander's blog, which is sander van der Berg dot blogspot dot com. Okay, with that out of the way, you can free to start, Sander. Okay, thank you for the kind introduction. So hello everybody. This presentation is going to be about a personal research project that I've been working on in the last year or so. And I think it addresses a very important shortcoming of basically the tools in the Nix ecosystem. So as you may probably already know, the Nix package manager is a powerful solution. So it offers all kinds of nice features. Like you can conveniently construct packages from source code and all the required build time dependencies. It offers build determinism, transparent binary deployments by downloading existing builds from a binary cache. It allows you to store multiple versions and variants of the same package safely next to each other. You can do unprevent user deployments. So if you want to install packages, you don't need to be root. It can also be used on multiple operating systems. So in addition to Linux, it is also well supported on macOS. And very recently, FreeBSD was also accepted into the FreeBSD ports tree. And with some little effort, you can also use it on other Unix-like operating systems as well. So when I have to explain Nix to newcomers, what I typically use is a Nix shell example to show all kinds of nice interesting properties of Nix. So for example, if you're on a conventional Linux distribution and you want to install with packages like Python or Node.js, you may already have a version of Python installed on your machine and you may not have installed Node.js on your machine yet. And what you can conveniently do is you can spawn a shell session in which you have a certain version of Python and Node.js. And the Nix package manager will automatically install it. And in this shell session, you can basically use these development utilities. And the nice thing is because they're stored in a Nix store, they will not conflict with other versions of packages and they won't interfere with packages installed on your host system either. So when I show this to people, especially newcomers, they typically get quite happy. Basically what they tell me is, yes, this is exactly what I'm looking for. So I'm going to experiment with packages. I'll try to install PostgreSQL or the patchy web server or Nginx because, yeah, Nix is a very convenient tool to experiment with packages. And then I have to disappoint people because I have to explain them. Nix is a package manager. It's not a service manager. So you can install, for example, the patchy web servers on your machine, but it just provides you the executable. You're still responsible for configuring the service yourself and also make sure that the lifecycle of the process gets managed. And in order to do that, you basically need to use some other tool like system D. And that is actually quite confusing for newcomers. So what I also typically do is I explain newcomers that there is, of course, the Nix package manager, but there are also sister projects that can complement Nix with all kinds of other deployment features, such as process management. And I think the most famous project is NixOS. So NixOS can generate system D unit files with the package manager. And then system D is responsible for managing the lifecycle of these processes. But there's one catch. If you want to use NixOS, that basically means that you're forced to adapt a fully Nix managed VINNIC distribution. And if you want to use another operating system, or if you're not familiar to Nix yet, this is typically a pain. For seasoned users like me, this is not a big deal. This is actually what I want, but it's not always an option to use NixOS. The same thing, for example, applies to Mac OS. So on Mac OS, there is a project called Nix Darwin that offers all kinds of system services that you can manage with launch D. But obviously it's limited to Mac OS only. So people typically tell me when they're on conventional Linux distributions, yeah, perhaps I should use Docker because, yeah, Docker, I can manage services. And then I can, of course, also tell people that Nix is also an interesting solution to use in combination with Docker. You can basically use Nix in the construction of images. You can even use Nix to build Docker images completely. And these images are typically way more space efficient than conventional Docker images. But this is not always a compelling use case because what we're basically doing is we squeeze Nix into a Docker workflow, but it's not a Nix-driven deployment process. So this is basically my motivation to start developing the Nix process management framework. So it's basically a general solution for complementing Nix with process management facilities. And it's built around a number of key concepts. So first of all, it's entirely Nix-driven. So the idea is that you write system configurations completely in the Nix expression language. It's also based on simple conventions. That means you follow the similar conventions to how packages are organized in the Nix packages repository. So the idea is that for running processes, you write a function definition. And to compose running process instances, you define an attribute set with function invocations. And a nice small extension to this framework is that you can also organize process dependencies with the same formalism as well. And the framework will automatically arrange the ordering if needed. So that, for example, processes are activated in the right order. Another key concept is that it's process manager agnostic. So it's not designed for a specific solution, but it should work with all kinds of process managers. So currently you can use system five in its scripts, supervisor the system D, launch the BSD RC scripts, and Windows services on Sikwin. But the nice thing is the model is flexible enough that you can even use it with solutions that are not qualified as process managers. So you can, for example, also use it in combination with Dysnix and Docker. And the reason why you can do that is these solutions are multifunctional solutions and they can also organize processes. So in the framework, we can basically use these properties for basically all kinds of interesting reasons. It's also operating system agnostic because it supports process managers on a variety of operating systems. And Nix is portable to some degree as well. You can also use it for unprivileged user installations. And the reason why that is possible is because I built in a global switch that allows you to disable the creation of users and changing user permission. So as a non-route user, you typically don't have the permissions to do this. And by disabling it, you can basically just run any process you want without restrictions. And the final key concept is that it doesn't require any advanced concepts like namespaces and C-groups that are commonly used for containers. So the solution relies on conflict avoidance rather than isolation. And that is, for example, good for portability, as I'll explain later in this presentation. So how to explain you how the framework works. I developed a very simple example system. It's actually quite an over-engineered example, but I think it's quite easy to understand how it works. So basically, this is a web application system that consists of multiple running processes. What you see on the right of this diagram are three web application processes. So they're basically processes with an embedded HTTP server. And the only purpose of these web applications is to render a static HTML page that basically states the identity of the service. In front of the web application process, there's Nginx, that acts as a reverse proxy. And Nginx basically redirects users to the web application process instances based on the virtual host header field. So for example, if the user in the web browser opens the URL web app 1.local, then Nginx will redirect the user to the first web application instance. Likewise, if you use web app 2.local, then the user gets redirected to the second web app instance. So in order to automate the deployment of this system, so we have four process instances. You need to write Nginx expressions for each process instance. And one way of doing that is by specifically writing Nginx expressions for a process and a specific service manager, such as system 5 in it. And this is basically what Nginx expression looks like. So as you may probably notice, this is actually quite similar to the convention of how we in the Nginx package manager declare packages. So this is a function definition. The first line is basically the function header. And these refer to all the built inputs that are required to generate a system 5 in it script. So create system 5 in it script is an abstraction function that allows you to basically generate a huge shell script with deployment activities. Web app refers to the web app application executable and port refers to the TCP port number that the server should bind to. And what I do in the body is I invoke the function abstraction and I basically specify what all the deployment activities should look like. So this, for example, is the start activity. It starts the executable and it uses the dash D parameter to specify that it should run in demon mode. The stop activity is stopping the executable. The restart activity is basically calling stop and start and status is basically used to basically show whether the process is running or not. The run levels parameters to specify the run level. So I don't know if you still recall system 5 in the scripts that we used to develop 10 years ago. So when you boot in run level 3, that is typically used to boot in terminal mode and run level 5 is used to boot a graphical desktop environment. And basically this parameter states that the service should start on boot up for all these three run levels. Now, writing a system 5 in the script is a bit for both. So if I have to, for example, package another process like engine X, then I end up repeating the same patterns over and over again. So what I also did is I developed a higher level abstraction function for system 5 in the scripts that look like this. So instead of specifying the activities, what I do is I specify the process that I want to manage. So this is the executable I want to manage with this command line parameter. And basically what the function abstraction does is it infers the activities automatically. So basically the result of evaluating this function invocation is this. So the framework also has abstraction functions for other service managers. So this is system 5 in it, but maybe I want to use system D instead. So this is basically the function abstraction that you can use to generate system D unit configuration files. So again, what I do here is I basically, I basically, basically specify here what executable I want to manage and to which TCP port it should bind. And basically what this function abstraction does is it generates a system D unit configuration file with nearly an identical structure. So this is pretty much a one on one translation from a NIC expression to a system D unit configuration file. And the framework is basically full of other abstraction functions for all kinds of process managers. So there's also, for example, the create supervisor D program abstraction function. There's an abstraction function for launch D for BSD or scripts and many more. Now, the interesting thing is if I would compare, for example, the system D expression with the system 5 expression that I've shown in the previous slide, then you see that they're slightly different, but they're not all that different. They're still mostly the same. So what I also did in the in the framework is I created an abstraction function that abstracts over all these process manager specific abstraction functions. And that basically looks like this. So what I do in this particular NIC expression is I use a generic create manage process function that basically describes from a high level perspective. What process do I want to manage? And these concepts can be easily translated to function invocations to the target specific abstraction functions. And basically any process manager in the framework is supported. So system 5 launch the system D they can they can all work with a high level specification like this. The only thing that I that you need to do is system D prefers to work with processes that run in the foreground. Whereas system 5 in it wants processes to demonize on their own. What you need to specify is is basically for both foreground processes and demon processes. Yeah, what kind of additional settings do they require? So this demon arcs parameter basically specifies if the process needs to demonize then past these common line arguments to the executable. And if you want to run as a foreground process, yeah, basically nothing is required. Actually specifying foreground process and demons is not a strict requirement. They can also be simulated, but for an optimal user experience, it's important that you still make a distinction between those two. Then there's one more concept that I need to explain. So in the in the example that I've shown you earlier, we want to run web app processes, but we actually need multiple instances of them. We need three of them to be able to construct multiple web application instances. You need to follow a slightly different convention. So basically what I do here is I declare a nested function and the outer function are basically. So the first line is referring to parameters that apply to all web app process instances and the inner function header refers to parameters that that are instance specific. And if you specify a unique combination of these parameters, then multiple instances can coexist on the same machine. So, for example, the port number only one service can bind to a specific port number. But if you allow every instance to bind to a unique port number, they can coexist. And the same thing applies to the instance name. So normally when you launch a process as a demon file, the pit file has the same name as the demon. But if you want to run multiple instances, you have to generate PID files with unique names. And basically the instance name parameters are high level concept that allows you to basically generate unique PID file names so that process can coexist. So in addition to declaring constructive functions that you can use to create process instances, you also need to actually specify what process instances you need. And that is done in a composition model that looks like this. So again, this is similar to the top level expression in the next packages collection. Again, this declares a function. So these are parameters that basically apply to all. Excuse me, Sander. Sorry to interrupt you. Yes. You're in like your Q&A portion is in about three minutes. So you'll be eating into that. Oh, yeah, I'm almost done. Yeah, so these properties apply to all running instances. And what I do here is here I construct two web instances. They can coexist because they have a unique port number and instance suffix. And what I do in this in this expression as well, I construct an engine reverse proxy. And yeah, that is basically responsible for setting up the redirection. So I'm actually going to take a risk now. I'll show you that this is basically the example. And I can basically deploy it as any. So this basically deploys the entire system as a collection of system five in its scripts. As you can see, it deploys five instances for the web application and two engine instances. And this basically shows that the redirection is working. So now the first instance is responding. And now the second instance is responding. And I can also deploy and deploy the system like this. And now it's not running anymore. I can also deploy it as system these scripts. So if I change the command line instruction from system five in it to system D, I can deploy the entire system as system D in its scripts. And as you can see, the system responds again, and if I request a process overview, then you can also see that the system is running. So I basically use one single specification to deploy the system for multiple process managers. So I'm almost done. So I'll just wrap up because I'm running out of time. There are lots of more interesting combinations possible. So you can also deploy in free BSD as BSD RC scripts. You can even deploy Docker containers for the process instances. Yeah, there are lots of other features possible that I haven't explained. So you can also basically create users and groups so that the processors run as unprivileged users. You can automatically assign port numbers, user IDs, and group IDs. You can also combine the NICs process management with framework with DysNICs. And the nice thing is that you can deploy process to networks of machines. And you can also combine them with things that aren't processes. So for example, Apache Tomcat can be managed as a process, but you can also deploy Java web applications to that running Tomcat instance. So this is very nice to deploy very heterogeneous systems. Okay, thank you, Sander. Is that the end of your presentation? Yeah, it's pretty much done. So yeah, there's some future work. And if you want to play around, do this link. And yeah, that's pretty much it. So. Okay. So we have about, I think, five questions. I will start with the first one. This one is from NICU on IRC. And they said, do you think that NICS OS, NICS Darwin, and other projects managing services around NICS packages, could in the near future move to such abstraction layer and share more search definitions, et cetera? I think that is very well possible. But yeah, the biggest problem we have to overcome that is related to Ilco's discussion from yesterday is the module system, for example, is somewhat problematic if you want to support features like being able to construct multiple instances of processes, because modules are basically not units of instantiation. So that is a problem. There are actually concepts that you can integrate into the framework. So for example, the translation process from a high level specification to any process manager, that is actually also something you could integrate in the module system. Basically the concepts are, I think, very generic. So what we could also, in theory, do in NICS packages is we have a system D layer and perhaps a launch D layer. And we also implement a module that resides somewhere in the middle of the translation process. So a generic process management player. And if we use that, then we can already, with the same specifications, support multiple process managers. Of course, another small thing we need to do is we need to be able to use this service layer separately. But I think that is something we can easily overcome. So awesome. All this is actually really interesting to me. I was like, super surprised with your presentation. Yeah. Yeah. The question we have is, how do you envision to generalize some system D specific features like hardening or socket activation that some services use? Yeah, that's a good question. So socket activation, for example, that is a concept that also launched these supports. But the sad thing is, is that they don't follow the same protocol. So it's fair. I don't think it's impossible to generalize that. But I think it's very difficult. So what you can, of course, still do is you can define overwrites for the process managers that you want. So you can basically, if some service needs socket activation, then you can, for example, declare for launch D, do this and for system D, do something else. Yeah. Sadly, you can't use a generalized concept. But it's still possible to address these deficiencies with overwrites if you want to. Great. Great. And I think we have one more question, a pretty short one, I think. So by running processes as different user and group, do you mean running a service as a proper system service? So not a user service anymore? Yeah. Yeah. That's exactly what I mean. Yeah. Okay. So I'm not sure you'll have time to completely finish, but we'll give you a minute, is what about a static process manager, e.g., doing a topological sort at next build time to output a certain derivation representing the processes which will run at runtime? I'm not sure if I completely understood the concept of static process manager, but that is actually already possible if you combine this framework with Disnix as a process management backend. It uses the Nix language to basically generate a dependency tree. And the only thing the Disnix does is it invokes a module that simply starts the process. So that is already somewhat possible. So, but yeah, the framework is not really designed to statically generate the dependencies trees itself. It only tries to utilize the features of the process manager backend as well as it could possibly do. Okay, great. Thank you so much. We are now out of time for the Q&A portion. There's some really interesting sort of crossover between the talks happening here, I think. Which is pretty cool. So yeah, thanks. And everyone, please remember to put those clapping emojis in the IRC chat and show the love. Yeah, and the breakout room for this talk, since it seems to be pretty lively Q&A going on here, is Nix-process management. And management being MGMT, like an acronym. Yeah. Yep. Okay, everyone. We'll be right back in about five minutes.
 Our next talk is Nix Process Management, an experimental Nix-based process manager agnostic framework,  and the presenter is named Sander van der Berg. This talk is about complementing Nix with any process manager.  And a little bit more information about Sander, if you're not familiar, is that he is a Nix contributor  since 2007 who's worked on so many things, such as like the FHF-shoot environments and the Nix Android build environment.  And is a developer, well, the main developer for various Nix-related utilities such as DysNix, DysNomia,  Notenix, and ComposerTenix. And I would also like suspect that a lot of NixOS users have probably encountered  Sander's blog, which is sander van der Berg dot blogspot dot com. Okay, with that out of the way, you can free to start, Sander.  Okay, thank you for the kind introduction. So hello everybody. This presentation is going to be about a personal research project  that I've been working on in the last year or so. And I think it addresses a very important shortcoming of basically the tools in the Nix ecosystem.  So as you may probably already know, the Nix package manager is a powerful solution. So it offers all kinds of nice features.  Like you can conveniently construct packages from source code and all the required build time dependencies.  It offers build determinism, transparent binary deployments by downloading existing builds from a binary cache.  It allows you to store multiple versions and variants of the same package safely next to each other.  You can do unprevent user deployments. So if you want to install packages, you don't need to be root.  It can also be used on multiple operating systems. So in addition to Linux, it is also well supported on macOS.  And very recently, FreeBSD was also accepted into the FreeBSD ports tree.  And with some little effort, you can also use it on other Unix-like operating systems as well.  So when I have to explain Nix to newcomers, what I typically use is a Nix shell example to show all kinds of nice interesting properties of Nix.  So for example, if you're on a conventional Linux distribution and you want to install with packages like Python or Node.js,  you may already have a version of Python installed on your machine and you may not have installed Node.js on your machine yet.  And what you can conveniently do is you can spawn a shell session in which you have a certain version of Python and Node.js.  And the Nix package manager will automatically install it.  And in this shell session, you can basically use these development utilities.  And the nice thing is because they're stored in a Nix store, they will not conflict with other versions of packages  and they won't interfere with packages installed on your host system either.  So when I show this to people, especially newcomers, they typically get quite happy.  Basically what they tell me is, yes, this is exactly what I'm looking for.  So I'm going to experiment with packages.  I'll try to install PostgreSQL or the patchy web server or Nginx because, yeah, Nix is a very convenient tool to experiment with packages.  And then I have to disappoint people because I have to explain them.  Nix is a package manager.  It's not a service manager.  So you can install, for example, the patchy web servers on your machine, but it just provides you the executable.  You're still responsible for configuring the service yourself and also make sure that the lifecycle of the process gets managed.  And in order to do that, you basically need to use some other tool like system D.  And that is actually quite confusing for newcomers.  So what I also typically do is I explain newcomers that there is, of course, the Nix package manager,  but there are also sister projects that can complement Nix with all kinds of other deployment features, such as process management.  And I think the most famous project is NixOS.  So NixOS can generate system D unit files with the package manager.  And then system D is responsible for managing the lifecycle of these processes.  But there's one catch. If you want to use NixOS, that basically means that you're forced to adapt a fully Nix managed VINNIC distribution.  And if you want to use another operating system, or if you're not familiar to Nix yet, this is typically a pain.  For seasoned users like me, this is not a big deal.  This is actually what I want, but it's not always an option to use NixOS.  The same thing, for example, applies to Mac OS.  So on Mac OS, there is a project called Nix Darwin that offers all kinds of system services that you can manage with launch D.  But obviously it's limited to Mac OS only.  So people typically tell me when they're on conventional Linux distributions, yeah, perhaps I should use Docker because, yeah,  Docker, I can manage services.  And then I can, of course, also tell people that Nix is also an interesting solution to use in combination with Docker.  You can basically use Nix in the construction of images.  You can even use Nix to build Docker images completely.  And these images are typically way more space efficient than conventional Docker images.  But this is not always a compelling use case because what we're basically doing is we squeeze Nix into a Docker workflow,  but it's not a Nix-driven deployment process.  So this is basically my motivation to start developing the Nix process management framework.  So it's basically a general solution for complementing Nix with process management facilities.  And it's built around a number of key concepts.  So first of all, it's entirely Nix-driven.  So the idea is that you write system configurations completely in the Nix expression language.  It's also based on simple conventions.  That means you follow the similar conventions to how packages are organized in the Nix packages repository.  So the idea is that for running processes, you write a function definition.  And to compose running process instances, you define an attribute set with function invocations.  And a nice small extension to this framework is that you can also organize process dependencies with the same formalism as well.  And the framework will automatically arrange the ordering if needed.  So that, for example, processes are activated in the right order.  Another key concept is that it's process manager agnostic.  So it's not designed for a specific solution, but it should work with all kinds of process managers.  So currently you can use system five in its scripts,  supervisor the system D, launch the BSD RC scripts, and Windows services on Sikwin.  But the nice thing is the model is flexible enough that you can even use it with solutions that are not qualified as process managers.  So you can, for example, also use it in combination with Dysnix and Docker.  And the reason why you can do that is these solutions are multifunctional solutions and they can also organize processes.  So in the framework, we can basically use these properties for basically all kinds of interesting reasons.  It's also operating system agnostic because it supports process managers on a variety of operating systems.  And Nix is portable to some degree as well.  You can also use it for unprivileged user installations.  And the reason why that is possible is because I built in a global switch that allows you to disable the creation of users and changing user permission.  So as a non-route user, you typically don't have the permissions to do this.  And by disabling it, you can basically just run any process you want without restrictions.  And the final key concept is that it doesn't require any advanced concepts like namespaces and C-groups that are commonly used for containers.  So the solution relies on conflict avoidance rather than isolation.  And that is, for example, good for portability, as I'll explain later in this presentation.  So how to explain you how the framework works.  I developed a very simple example system.  It's actually quite an over-engineered example, but I think it's quite easy to understand how it works.  So basically, this is a web application system that consists of multiple running processes.  What you see on the right of this diagram are three web application processes.  So they're basically processes with an embedded HTTP server.  And the only purpose of these web applications is to render a static HTML page that basically states the identity of the service.  In front of the web application process, there's Nginx, that acts as a reverse proxy.  And Nginx basically redirects users to the web application process instances based on the virtual host header field.  So for example, if the user in the web browser opens the URL web app 1.local, then Nginx will redirect the user to the first web application instance.  Likewise, if you use web app 2.local, then the user gets redirected to the second web app instance.  So in order to automate the deployment of this system, so we have four process instances.  You need to write Nginx expressions for each process instance.  And one way of doing that is by specifically writing Nginx expressions for a process and a specific service manager, such as system 5 in it.  And this is basically what Nginx expression looks like.  So as you may probably notice, this is actually quite similar to the convention of how we in the Nginx package manager declare packages.  So this is a function definition.  The first line is basically the function header.  And these refer to all the built inputs that are required to generate a system 5 in it script.  So create system 5 in it script is an abstraction function that allows you to basically generate a huge shell script with deployment activities.  Web app refers to the web app application executable and port refers to the TCP port number that the server should bind to.  And what I do in the body is I invoke the function abstraction and I basically specify what all the deployment activities should look like.  So this, for example, is the start activity.  It starts the executable and it uses the dash D parameter to specify that it should run in demon mode.  The stop activity is stopping the executable.  The restart activity is basically calling stop and start and status is basically used to basically show whether the process is running or not.  The run levels parameters to specify the run level.  So I don't know if you still recall system 5 in the scripts that we used to develop 10 years ago.  So when you boot in run level 3, that is typically used to boot in terminal mode and run level 5 is used to boot a graphical desktop environment.  And basically this parameter states that the service should start on boot up for all these three run levels.  Now, writing a system 5 in the script is a bit for both.  So if I have to, for example, package another process like engine X, then I end up repeating the same patterns over and over again.  So what I also did is I developed a higher level abstraction function for system 5 in the scripts that look like this.  So instead of specifying the activities, what I do is I specify the process that I want to manage.  So this is the executable I want to manage with this command line parameter.  And basically what the function abstraction does is it infers the activities automatically.  So basically the result of evaluating this function invocation is this.  So the framework also has abstraction functions for other service managers.  So this is system 5 in it, but maybe I want to use system D instead.  So this is basically the function abstraction that you can use to generate system D unit configuration files.  So again, what I do here is I basically, I basically, basically specify here what executable I want to manage and to which TCP port it should bind.  And basically what this function abstraction does is it generates a system D unit configuration file with nearly an identical structure.  So this is pretty much a one on one translation from a NIC expression to a system D unit configuration file.  And the framework is basically full of other abstraction functions for all kinds of process managers.  So there's also, for example, the create supervisor D program abstraction function.  There's an abstraction function for launch D for BSD or scripts and many more.  Now, the interesting thing is if I would compare, for example, the system D expression with the system 5 expression that I've shown in the previous slide,  then you see that they're slightly different, but they're not all that different.  They're still mostly the same.  So what I also did in the in the framework is I created an abstraction function that abstracts over all these process manager specific abstraction functions.  And that basically looks like this.  So what I do in this particular NIC expression is I use a generic create manage process function that basically describes from a high level perspective.  What process do I want to manage?  And these concepts can be easily translated to function invocations to the target specific abstraction functions.  And basically any process manager in the framework is supported.  So system 5 launch the system D they can they can all work with a high level specification like this.  The only thing that I that you need to do is system D prefers to work with processes that run in the foreground.  Whereas system 5 in it wants processes to demonize on their own.  What you need to specify is is basically for both foreground processes and demon processes.  Yeah, what kind of additional settings do they require?  So this demon arcs parameter basically specifies if the process needs to demonize then past these common line arguments to the executable.  And if you want to run as a foreground process, yeah, basically nothing is required.  Actually specifying foreground process and demons is not a strict requirement.  They can also be simulated, but for an optimal user experience, it's important that you still make a distinction between those two.  Then there's one more concept that I need to explain.  So in the in the example that I've shown you earlier, we want to run web app processes, but we actually need multiple instances of them.  We need three of them to be able to construct multiple web application instances.  You need to follow a slightly different convention.  So basically what I do here is I declare a nested function and the outer function are basically.  So the first line is referring to parameters that apply to all web app process instances and the inner function header refers to parameters that that are instance specific.  And if you specify a unique combination of these parameters, then multiple instances can coexist on the same machine.  So, for example, the port number only one service can bind to a specific port number.  But if you allow every instance to bind to a unique port number, they can coexist.  And the same thing applies to the instance name.  So normally when you launch a process as a demon file, the pit file has the same name as the demon.  But if you want to run multiple instances, you have to generate PID files with unique names.  And basically the instance name parameters are high level concept that allows you to basically generate unique PID file names so that process can coexist.  So in addition to declaring constructive functions that you can use to create process instances, you also need to actually specify what process instances you need.  And that is done in a composition model that looks like this.  So again, this is similar to the top level expression in the next packages collection.  Again, this declares a function.  So these are parameters that basically apply to all.  Excuse me, Sander. Sorry to interrupt you.  Yes.  You're in like your Q&A portion is in about three minutes.  So you'll be eating into that.  Oh, yeah, I'm almost done.  Yeah, so these properties apply to all running instances.  And what I do here is here I construct two web instances.  They can coexist because they have a unique port number and instance suffix.  And what I do in this in this expression as well, I construct an engine reverse proxy.  And yeah, that is basically responsible for setting up the redirection.  So I'm actually going to take a risk now.  I'll show you that this is basically the example.  And I can basically deploy it as any.  So this basically deploys the entire system as a collection of system five in its scripts.  As you can see, it deploys five instances for the web application and two engine instances.  And this basically shows that the redirection is working.  So now the first instance is responding.  And now the second instance is responding.  And I can also deploy and deploy the system like this.  And now it's not running anymore.  I can also deploy it as system these scripts.  So if I change the command line instruction from system five in it to system D, I can deploy  the entire system as system D in its scripts.  And as you can see, the system responds again, and if I request a process overview, then you  can also see that the system is running.  So I basically use one single specification to deploy the system for multiple process  managers.  So I'm almost done.  So I'll just wrap up because I'm running out of time.  There are lots of more interesting combinations possible.  So you can also deploy in free BSD as BSD RC scripts.  You can even deploy Docker containers for the process instances.  Yeah, there are lots of other features possible that I haven't explained.  So you can also basically create users and groups so that the processors run as unprivileged  users.  You can automatically assign port numbers, user IDs, and group IDs.  You can also combine the NICs process management with framework with DysNICs.  And the nice thing is that you can deploy process to networks of machines.  And you can also combine them with things that aren't processes.  So for example, Apache Tomcat can be managed as a process, but you can also deploy Java  web applications to that running Tomcat instance.  So this is very nice to deploy very heterogeneous systems.  Okay, thank you, Sander.  Is that the end of your presentation?  Yeah, it's pretty much done.  So yeah, there's some future work.  And if you want to play around, do this link.  And yeah, that's pretty much it.  So.  Okay.  So we have about, I think, five questions.  I will start with the first one.  This one is from NICU on IRC.  And they said, do you think that NICS OS, NICS Darwin, and other projects managing services  around NICS packages, could in the near future move to such abstraction layer and share more  search definitions, et cetera?  I think that is very well possible.  But yeah, the biggest problem we have to overcome that is related to Ilco's discussion from  yesterday is the module system, for example, is somewhat problematic if you want to support  features like being able to construct multiple instances of processes, because modules are  basically not units of instantiation.  So that is a problem.  There are actually concepts that you can integrate into the framework.  So for example, the translation process from a high level specification to any process  manager, that is actually also something you could integrate in the module system.  Basically the concepts are, I think, very generic.  So what we could also, in theory, do in NICS packages is we have a system D layer and perhaps  a launch D layer.  And we also implement a module that resides somewhere in the middle of the translation  process.  So a generic process management player.  And if we use that, then we can already, with the same specifications, support multiple  process managers.  Of course, another small thing we need to do is we need to be able to use this service  layer separately.  But I think that is something we can easily overcome.  So awesome.  All this is actually really interesting to me.  I was like, super surprised with your presentation.  Yeah.  Yeah.  The question we have is, how do you envision to generalize some system D specific features  like hardening or socket activation that some services use?  Yeah, that's a good question.  So socket activation, for example, that is a concept that also launched these supports.  But the sad thing is, is that they don't follow the same protocol.  So it's fair.  I don't think it's impossible to generalize that.  But I think it's very difficult.  So what you can, of course, still do is you can define overwrites for the process managers  that you want.  So you can basically, if some service needs socket activation, then you can, for example,  declare for launch D, do this and for system D, do something else.  Yeah.  Sadly, you can't use a generalized concept.  But it's still possible to address these deficiencies with overwrites if you want to.  Great.  Great.  And I think we have one more question, a pretty short one, I think.  So by running processes as different user and group, do you mean running a service as  a proper system service?  So not a user service anymore?  Yeah.  Yeah.  That's exactly what I mean.  Yeah.  Okay.  So I'm not sure you'll have time to completely finish, but we'll give you a minute, is what  about a static process manager, e.g., doing a topological sort at next build time to output  a certain derivation representing the processes which will run at runtime?  I'm not sure if I completely understood the concept of static process manager, but that  is actually already possible if you combine this framework with Disnix as a process management  backend.  It uses the Nix language to basically generate a dependency tree.  And the only thing the Disnix does is it invokes a module that simply starts the process.  So that is already somewhat possible.  So, but yeah, the framework is not really designed to statically generate the dependencies  trees itself.  It only tries to utilize the features of the process manager backend as well as it could  possibly do.  Okay, great.  Thank you so much.  We are now out of time for the Q&A portion.  There's some really interesting sort of crossover between the talks happening here, I think.  Which is pretty cool.  So yeah, thanks.  And everyone, please remember to put those clapping emojis in the IRC chat and show the  love.  Yeah, and the breakout room for this talk, since it seems to be pretty lively Q&A going  on here, is Nix-process management.  And management being MGMT, like an acronym.  Yeah.  Yep.  Okay, everyone.  We'll be right back in about five minutes.
NixCon_2020_Vid_12_0_455.png NixCon_2020_Vid_12_456_563.png NixCon_2020_Vid_12_564_1331.png NixCon_2020_Vid_12_1332_1391.png NixCon_2020_Vid_12_1392_1931.png NixCon_2020_Vid_12_1932_3383.png NixCon_2020_Vid_12_3384_5387.png NixCon_2020_Vid_12_5388_8927.png NixCon_2020_Vid_12_8928_9299.png NixCon_2020_Vid_12_9300_13223.png NixCon_2020_Vid_12_13224_15719.png NixCon_2020_Vid_12_15720_19643.png NixCon_2020_Vid_12_19644_21131.png NixCon_2020_Vid_12_21132_21191.png NixCon_2020_Vid_12_21192_21623.png NixCon_2020_Vid_12_21624_22535.png NixCon_2020_Vid_12_22536_22679.png NixCon_2020_Vid_12_22680_22799.png NixCon_2020_Vid_12_22800_26063.png NixCon_2020_Vid_12_26064_27815.png NixCon_2020_Vid_12_27816_29531.png NixCon_2020_Vid_12_29532_29627.png NixCon_2020_Vid_12_29628_29783.png NixCon_2020_Vid_12_29784_29831.png NixCon_2020_Vid_12_29832_30143.png NixCon_2020_Vid_12_30144_30587.png NixCon_2020_Vid_12_30588_31391.png NixCon_2020_Vid_12_31392_31511.png NixCon_2020_Vid_12_31512_31619.png NixCon_2020_Vid_12_31620_31667.png NixCon_2020_Vid_12_31668_31763.png NixCon_2020_Vid_12_31764_31919.png NixCon_2020_Vid_12_31920_33215.png NixCon_2020_Vid_12_33216_33323.png NixCon_2020_Vid_12_33324_33407.png NixCon_2020_Vid_12_33408_39246.png
Our next talk is Nix Process Management, an experimental Nix-based process manager agnostic framework, and the presenter is named Sander van der Berg. This talk is about complementing Nix with any process manager. And a little bit more information about Sander, if you're not familiar, is that he is a Nix contributor since 2007 who's worked on so many things, such as like the FHF-shoot environments and the Nix Android build environment. And is a developer, well, the main developer for various Nix-related utilities such as DysNix, DysNomia, Notenix, and ComposerTenix. And I would also like suspect that a lot of NixOS users have probably encountered Sander's blog, which is sander van der Berg dot blogspot dot com. Okay, with that out of the way, you can free to start, Sander. Okay, thank you for the kind introduction. So hello everybody. This presentation is going to be about a personal research project that I've been working on in the last year or so. And I think it addresses a very important shortcoming of basically the tools in the Nix ecosystem. So as you may probably already know, the Nix package manager is a powerful solution. So it offers all kinds of nice features. Like you can conveniently construct packages from source code and all the required build time dependencies. It offers build determinism, transparent binary deployments by downloading existing builds from a binary cache. It allows you to store multiple versions and variants of the same package safely next to each other. You can do unprevent user deployments. So if you want to install packages, you don't need to be root. It can also be used on multiple operating systems. So in addition to Linux, it is also well supported on macOS. And very recently, FreeBSD was also accepted into the FreeBSD ports tree. And with some little effort, you can also use it on other Unix-like operating systems as well. So when I have to explain Nix to newcomers, what I typically use is a Nix shell example to show all kinds of nice interesting properties of Nix. So for example, if you're on a conventional Linux distribution and you want to install with packages like Python or Node.js, you may already have a version of Python installed on your machine and you may not have installed Node.js on your machine yet. And what you can conveniently do is you can spawn a shell session in which you have a certain version of Python and Node.js. And the Nix package manager will automatically install it. And in this shell session, you can basically use these development utilities. And the nice thing is because they're stored in a Nix store, they will not conflict with other versions of packages and they won't interfere with packages installed on your host system either. So when I show this to people, especially newcomers, they typically get quite happy. Basically what they tell me is, yes, this is exactly what I'm looking for. So I'm going to experiment with packages. I'll try to install PostgreSQL or the patchy web server or Nginx because, yeah, Nix is a very convenient tool to experiment with packages. And then I have to disappoint people because I have to explain them. Nix is a package manager. It's not a service manager. So you can install, for example, the patchy web servers on your machine, but it just provides you the executable. You're still responsible for configuring the service yourself and also make sure that the lifecycle of the process gets managed. And in order to do that, you basically need to use some other tool like system D. And that is actually quite confusing for newcomers. So what I also typically do is I explain newcomers that there is, of course, the Nix package manager, but there are also sister projects that can complement Nix with all kinds of other deployment features, such as process management. And I think the most famous project is NixOS. So NixOS can generate system D unit files with the package manager. And then system D is responsible for managing the lifecycle of these processes. But there's one catch. If you want to use NixOS, that basically means that you're forced to adapt a fully Nix managed VINNIC distribution. And if you want to use another operating system, or if you're not familiar to Nix yet, this is typically a pain. For seasoned users like me, this is not a big deal. This is actually what I want, but it's not always an option to use NixOS. The same thing, for example, applies to Mac OS. So on Mac OS, there is a project called Nix Darwin that offers all kinds of system services that you can manage with launch D. But obviously it's limited to Mac OS only. So people typically tell me when they're on conventional Linux distributions, yeah, perhaps I should use Docker because, yeah, Docker, I can manage services. And then I can, of course, also tell people that Nix is also an interesting solution to use in combination with Docker. You can basically use Nix in the construction of images. You can even use Nix to build Docker images completely. And these images are typically way more space efficient than conventional Docker images. But this is not always a compelling use case because what we're basically doing is we squeeze Nix into a Docker workflow, but it's not a Nix-driven deployment process. So this is basically my motivation to start developing the Nix process management framework. So it's basically a general solution for complementing Nix with process management facilities. And it's built around a number of key concepts. So first of all, it's entirely Nix-driven. So the idea is that you write system configurations completely in the Nix expression language. It's also based on simple conventions. That means you follow the similar conventions to how packages are organized in the Nix packages repository. So the idea is that for running processes, you write a function definition. And to compose running process instances, you define an attribute set with function invocations. And a nice small extension to this framework is that you can also organize process dependencies with the same formalism as well. And the framework will automatically arrange the ordering if needed. So that, for example, processes are activated in the right order. Another key concept is that it's process manager agnostic. So it's not designed for a specific solution, but it should work with all kinds of process managers. So currently you can use system five in its scripts, supervisor the system D, launch the BSD RC scripts, and Windows services on Sikwin. But the nice thing is the model is flexible enough that you can even use it with solutions that are not qualified as process managers. So you can, for example, also use it in combination with Dysnix and Docker. And the reason why you can do that is these solutions are multifunctional solutions and they can also organize processes. So in the framework, we can basically use these properties for basically all kinds of interesting reasons. It's also operating system agnostic because it supports process managers on a variety of operating systems. And Nix is portable to some degree as well. You can also use it for unprivileged user installations. And the reason why that is possible is because I built in a global switch that allows you to disable the creation of users and changing user permission. So as a non-route user, you typically don't have the permissions to do this. And by disabling it, you can basically just run any process you want without restrictions. And the final key concept is that it doesn't require any advanced concepts like namespaces and C-groups that are commonly used for containers. So the solution relies on conflict avoidance rather than isolation. And that is, for example, good for portability, as I'll explain later in this presentation. So how to explain you how the framework works. I developed a very simple example system. It's actually quite an over-engineered example, but I think it's quite easy to understand how it works. So basically, this is a web application system that consists of multiple running processes. What you see on the right of this diagram are three web application processes. So they're basically processes with an embedded HTTP server. And the only purpose of these web applications is to render a static HTML page that basically states the identity of the service. In front of the web application process, there's Nginx, that acts as a reverse proxy. And Nginx basically redirects users to the web application process instances based on the virtual host header field. So for example, if the user in the web browser opens the URL web app 1.local, then Nginx will redirect the user to the first web application instance. Likewise, if you use web app 2.local, then the user gets redirected to the second web app instance. So in order to automate the deployment of this system, so we have four process instances. You need to write Nginx expressions for each process instance. And one way of doing that is by specifically writing Nginx expressions for a process and a specific service manager, such as system 5 in it. And this is basically what Nginx expression looks like. So as you may probably notice, this is actually quite similar to the convention of how we in the Nginx package manager declare packages. So this is a function definition. The first line is basically the function header. And these refer to all the built inputs that are required to generate a system 5 in it script. So create system 5 in it script is an abstraction function that allows you to basically generate a huge shell script with deployment activities. Web app refers to the web app application executable and port refers to the TCP port number that the server should bind to. And what I do in the body is I invoke the function abstraction and I basically specify what all the deployment activities should look like. So this, for example, is the start activity. It starts the executable and it uses the dash D parameter to specify that it should run in demon mode. The stop activity is stopping the executable. The restart activity is basically calling stop and start and status is basically used to basically show whether the process is running or not. The run levels parameters to specify the run level. So I don't know if you still recall system 5 in the scripts that we used to develop 10 years ago. So when you boot in run level 3, that is typically used to boot in terminal mode and run level 5 is used to boot a graphical desktop environment. And basically this parameter states that the service should start on boot up for all these three run levels. Now, writing a system 5 in the script is a bit for both. So if I have to, for example, package another process like engine X, then I end up repeating the same patterns over and over again. So what I also did is I developed a higher level abstraction function for system 5 in the scripts that look like this. So instead of specifying the activities, what I do is I specify the process that I want to manage. So this is the executable I want to manage with this command line parameter. And basically what the function abstraction does is it infers the activities automatically. So basically the result of evaluating this function invocation is this. So the framework also has abstraction functions for other service managers. So this is system 5 in it, but maybe I want to use system D instead. So this is basically the function abstraction that you can use to generate system D unit configuration files. So again, what I do here is I basically, I basically, basically specify here what executable I want to manage and to which TCP port it should bind. And basically what this function abstraction does is it generates a system D unit configuration file with nearly an identical structure. So this is pretty much a one on one translation from a NIC expression to a system D unit configuration file. And the framework is basically full of other abstraction functions for all kinds of process managers. So there's also, for example, the create supervisor D program abstraction function. There's an abstraction function for launch D for BSD or scripts and many more. Now, the interesting thing is if I would compare, for example, the system D expression with the system 5 expression that I've shown in the previous slide, then you see that they're slightly different, but they're not all that different. They're still mostly the same. So what I also did in the in the framework is I created an abstraction function that abstracts over all these process manager specific abstraction functions. And that basically looks like this. So what I do in this particular NIC expression is I use a generic create manage process function that basically describes from a high level perspective. What process do I want to manage? And these concepts can be easily translated to function invocations to the target specific abstraction functions. And basically any process manager in the framework is supported. So system 5 launch the system D they can they can all work with a high level specification like this. The only thing that I that you need to do is system D prefers to work with processes that run in the foreground. Whereas system 5 in it wants processes to demonize on their own. What you need to specify is is basically for both foreground processes and demon processes. Yeah, what kind of additional settings do they require? So this demon arcs parameter basically specifies if the process needs to demonize then past these common line arguments to the executable. And if you want to run as a foreground process, yeah, basically nothing is required. Actually specifying foreground process and demons is not a strict requirement. They can also be simulated, but for an optimal user experience, it's important that you still make a distinction between those two. Then there's one more concept that I need to explain. So in the in the example that I've shown you earlier, we want to run web app processes, but we actually need multiple instances of them. We need three of them to be able to construct multiple web application instances. You need to follow a slightly different convention. So basically what I do here is I declare a nested function and the outer function are basically. So the first line is referring to parameters that apply to all web app process instances and the inner function header refers to parameters that that are instance specific. And if you specify a unique combination of these parameters, then multiple instances can coexist on the same machine. So, for example, the port number only one service can bind to a specific port number. But if you allow every instance to bind to a unique port number, they can coexist. And the same thing applies to the instance name. So normally when you launch a process as a demon file, the pit file has the same name as the demon. But if you want to run multiple instances, you have to generate PID files with unique names. And basically the instance name parameters are high level concept that allows you to basically generate unique PID file names so that process can coexist. So in addition to declaring constructive functions that you can use to create process instances, you also need to actually specify what process instances you need. And that is done in a composition model that looks like this. So again, this is similar to the top level expression in the next packages collection. Again, this declares a function. So these are parameters that basically apply to all. Excuse me, Sander. Sorry to interrupt you. Yes. You're in like your Q&A portion is in about three minutes. So you'll be eating into that. Oh, yeah, I'm almost done. Yeah, so these properties apply to all running instances. And what I do here is here I construct two web instances. They can coexist because they have a unique port number and instance suffix. And what I do in this in this expression as well, I construct an engine reverse proxy. And yeah, that is basically responsible for setting up the redirection. So I'm actually going to take a risk now. I'll show you that this is basically the example. And I can basically deploy it as any. So this basically deploys the entire system as a collection of system five in its scripts. As you can see, it deploys five instances for the web application and two engine instances. And this basically shows that the redirection is working. So now the first instance is responding. And now the second instance is responding. And I can also deploy and deploy the system like this. And now it's not running anymore. I can also deploy it as system these scripts. So if I change the command line instruction from system five in it to system D, I can deploy the entire system as system D in its scripts. And as you can see, the system responds again, and if I request a process overview, then you can also see that the system is running. So I basically use one single specification to deploy the system for multiple process managers. So I'm almost done. So I'll just wrap up because I'm running out of time. There are lots of more interesting combinations possible. So you can also deploy in free BSD as BSD RC scripts. You can even deploy Docker containers for the process instances. Yeah, there are lots of other features possible that I haven't explained. So you can also basically create users and groups so that the processors run as unprivileged users. You can automatically assign port numbers, user IDs, and group IDs. You can also combine the NICs process management with framework with DysNICs. And the nice thing is that you can deploy process to networks of machines. And you can also combine them with things that aren't processes. So for example, Apache Tomcat can be managed as a process, but you can also deploy Java web applications to that running Tomcat instance. So this is very nice to deploy very heterogeneous systems. Okay, thank you, Sander. Is that the end of your presentation? Yeah, it's pretty much done. So yeah, there's some future work. And if you want to play around, do this link. And yeah, that's pretty much it. So. Okay. So we have about, I think, five questions. I will start with the first one. This one is from NICU on IRC. And they said, do you think that NICS OS, NICS Darwin, and other projects managing services around NICS packages, could in the near future move to such abstraction layer and share more search definitions, et cetera? I think that is very well possible. But yeah, the biggest problem we have to overcome that is related to Ilco's discussion from yesterday is the module system, for example, is somewhat problematic if you want to support features like being able to construct multiple instances of processes, because modules are basically not units of instantiation. So that is a problem. There are actually concepts that you can integrate into the framework. So for example, the translation process from a high level specification to any process manager, that is actually also something you could integrate in the module system. Basically the concepts are, I think, very generic. So what we could also, in theory, do in NICS packages is we have a system D layer and perhaps a launch D layer. And we also implement a module that resides somewhere in the middle of the translation process. So a generic process management player. And if we use that, then we can already, with the same specifications, support multiple process managers. Of course, another small thing we need to do is we need to be able to use this service layer separately. But I think that is something we can easily overcome. So awesome. All this is actually really interesting to me. I was like, super surprised with your presentation. Yeah. Yeah. The question we have is, how do you envision to generalize some system D specific features like hardening or socket activation that some services use? Yeah, that's a good question. So socket activation, for example, that is a concept that also launched these supports. But the sad thing is, is that they don't follow the same protocol. So it's fair. I don't think it's impossible to generalize that. But I think it's very difficult. So what you can, of course, still do is you can define overwrites for the process managers that you want. So you can basically, if some service needs socket activation, then you can, for example, declare for launch D, do this and for system D, do something else. Yeah. Sadly, you can't use a generalized concept. But it's still possible to address these deficiencies with overwrites if you want to. Great. Great. And I think we have one more question, a pretty short one, I think. So by running processes as different user and group, do you mean running a service as a proper system service? So not a user service anymore? Yeah. Yeah. That's exactly what I mean. Yeah. Okay. So I'm not sure you'll have time to completely finish, but we'll give you a minute, is what about a static process manager, e.g., doing a topological sort at next build time to output a certain derivation representing the processes which will run at runtime? I'm not sure if I completely understood the concept of static process manager, but that is actually already possible if you combine this framework with Disnix as a process management backend. It uses the Nix language to basically generate a dependency tree. And the only thing the Disnix does is it invokes a module that simply starts the process. So that is already somewhat possible. So, but yeah, the framework is not really designed to statically generate the dependencies trees itself. It only tries to utilize the features of the process manager backend as well as it could possibly do. Okay, great. Thank you so much. We are now out of time for the Q&A portion. There's some really interesting sort of crossover between the talks happening here, I think. Which is pretty cool. So yeah, thanks. And everyone, please remember to put those clapping emojis in the IRC chat and show the love. Yeah, and the breakout room for this talk, since it seems to be pretty lively Q&A going on here, is Nix-process management. And management being MGMT, like an acronym. Yeah. Yep. Okay, everyone. We'll be right back in about five minutes.
information about Sander, if you're not familiar, know, the Nix package manager is a powerful solution. So it offers all kinds of nice determinism, transparent binary deployments by to be root. It can also be used on multiple operating systems. So in addition to Linux, it is also well supported on macOS. And very recently, show all kinds of nice interesting properties of Nix. So for example, if you're on a conventional Linux distribution and you want to install with your machine yet. And what you can conveniently the Nix package manager will automatically newcomers that there is, of course, the Nix operating system, or if you're not familiar to option to use NixOS. The same thing, for example, manage services. And then I can, of course, also Nix process management framework. So it's similar conventions to how packages are organized function invocations. And a nice small extension process dependencies with the same formalism as with solutions that are not qualified as process also use it for unprivileged user installations. that are commonly used for containers. So the framework works. I developed a very simple application process instances based on the order to automate the deployment of this system, process and a specific service manager, such as an abstraction function that allows you to the framework also has abstraction functions for functions for all kinds of process managers. So that different. They're still mostly the same. So translated to function invocations to the target can even deploy Docker containers for the process example, Apache Tomcat can be managed as a multiple instances of processes, because modules specifications, support multiple process So socket activation, for example, that is a Disnix as a process management backend. It uses dependencies trees itself. It only tries to utilize the features of the process manager