We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Nix Flakes in production

00:00

Formale Metadaten

Titel
Nix Flakes in production
Untertitel
What? Why? How?
Serientitel
Anzahl der Teile
19
Autor
Lizenz
CC-Namensnennung 3.0 Unported:
Sie dürfen das Werk bzw. den Inhalt zu jedem legalen Zweck nutzen, verändern und in unveränderter oder veränderter Form vervielfältigen, verbreiten und öffentlich zugänglich machen, sofern Sie den Namen des Autors/Rechteinhabers in der von ihm festgelegten Weise nennen.
Identifikatoren
Herausgeber
Erscheinungsjahr
Sprache

Inhaltliche Metadaten

Fachgebiet
Genre
Abstract
At Serokell, we have been using Nix for over 5 years. In the last half-year, we have started an effort to use flakes to build our infrastructure and projects. In this talk, I would like to give a refresher of what flakes are, explain why we are so excited about them, and share experiences, thoughts, and advice on the matter.
DualitätssatzProdukt <Mathematik>EinsZahlenbereichCoxeter-GruppeGeradeProjektive EbeneEigentliche Abbildung
DualitätssatzQuellcodeCoxeter-GruppeAbstraktionsebeneMechanismus-Design-TheorieLeistungsbewertungMultiplikationDokumentenserverDatenstrukturVerzeichnisdienstWurzel <Mathematik>DefaultProjektive EbeneVersionsverwaltungPhysikalisches SystemProdukt <Mathematik>VerschlingungArithmetischer AusdruckElektronische PublikationSoftwareentwicklerRechenwerkEin-AusgabeAggregatzustandProgrammierumgebungDeskriptive StatistikKnotenpunktArithmetisches MittelDifferenteResultanteQuellcodeDemoszene <Programmierung>FunktionalCoxeter-GruppeAbstandÄußere Algebra eines ModulsEnergiedichtePhasenumwandlungVierzigMultiplikationsoperatorRoutingDienst <Informatik>InformationsspeicherungEvoluteWurzel <Mathematik>Schreiben <Datenverarbeitung>Attributierte GrammatikGebäude <Mathematik>Poisson-KlammerWinkelSchnittmengeURLVariableDokumentenserverVerzweigendes ProgrammFunktion <Mathematik>VerzeichnisdienstComputeranimation
DefaultFunktion <Mathematik>ParametersystemEin-AusgabeQuellcodeDatenstrukturStandardabweichungLeistungsbewertungDesintegration <Mathematik>Skeleton <Programmierung>Nabel <Mathematik>Installation <Informatik>Interface <Schaltung>CLIBenutzeroberflächeWiderspruchsfreiheitDatenverwaltungWeb logCachingVersionsverwaltungStandardabweichungProjektive EbeneFahne <Mathematik>DatenstrukturEin-AusgabeQuick-SortWinkelBitDefaultFunktion <Mathematik>DatenverwaltungExtreme programmingVersionsverwaltungLeistungsbewertungSchnittmengeSystemplattformStrömungsrichtungMailing-ListeIntegralArithmetisches MittelHash-AlgorithmusDokumentenserverElektronische PublikationNabel <Mathematik>Äußere Algebra eines ModulsMultiplikationsoperatorLesen <Datenverarbeitung>URLDateiformatFunktionalEntscheidungstheorieInterface <Schaltung>Mechanismus-Design-TheorieOrdnung <Mathematik>Auflösung <Mathematik>Poisson-KlammerRichtungKategorie <Mathematik>Physikalisches SystemHoaxMaßerweiterungAusnahmebehandlungGeradeParametersystemNeunzehnDifferenteNeuroinformatikAdditionBestimmtheitsmaßMenütechnikGemeinsamer SpeicherStochastische AbhängigkeitRobotikAntwortfunktionExpertensystemFächer <Mathematik>MereologieDistributionenraumRPCDivisionGenerator <Informatik>EINKAUF <Programm>Schreiben <Datenverarbeitung>InformationComputeranimation
DefaultDefaultFunktion <Mathematik>SystemplattformAttributierte GrammatikEin-AusgabeFunktionalSchnittmengeParametersystemGeradeGanze FunktionVerzeichnisdienstDeskriptive StatistikProjektive EbeneSkeleton <Programmierung>DokumentenserverDerivation <Algebra>ZeichenketteQuellcodeInformationPhysikalisches SystemMAPElektronische PublikationKonfigurationsdatenbankStrömungsrichtungDatenflussSystemaufrufMultiplikationsoperatorBenutzerfreundlichkeitGenerator <Informatik>Zellularer AutomatVerkehrsinformationRichtungAntwortfunktionZentrische StreckungTouchscreenReibungswärmeNeuroinformatikGruppenoperationStreaming <Kommunikationstechnik>
TopologieComputervirusNabel <Mathematik>Funktion <Mathematik>MultiplikationsoperatorResultanteNabel <Mathematik>SystemplattformErwartungswertArithmetisches MittelStrömungsrichtungDefaultElektronische PublikationBinärcodeGebäude <Mathematik>VersionsverwaltungTopologieBenutzerschnittstellenverwaltungssystemParametersystemSchnelltasteComputeranimation
TopologieEin-AusgabeSystemprogrammierungPhysikalisches SystemSystemplattformFehlermeldungDateiformatSystemplattformHoaxGebäude <Mathematik>Funktion <Mathematik>Ein-AusgabePhysikalisches SystemMeterSchlussregelDefaultFunktionalMultiplikationsoperatorGoogolSichtenkonzeptLeckVierzigSubstitutionCASE <Informatik>Quick-SortFehlermeldungDateiformatBinärcodeMailing-ListeVersionsverwaltungAttributierte GrammatikCachingComputeranimation
ComputervirusPhysikalisches SystemProgrammierungApp <Programm>FunktionalDefaultSchnittmengeMapping <Computergraphik>MultifunktionAttributierte GrammatikSystemplattformSoftwaretestBildschirmmaskeTypentheorieSuite <Programmpaket>Kartesische KoordinatenFunktion <Mathematik>Physikalisches SystemGeradeBus <Informatik>OrtsoperatorFormale SpracheBootenSystem FFormation <Mathematik>DivisionHoaxExpertensystemComputeranimation
FehlermeldungFunktion <Mathematik>ErwartungswertGeradeCodeComputervirusDerivation <Algebra>VerzeichnisdienstInformationPhysikalisches SystemOverlay-NetzDienst <Informatik>BimodulKonfiguration <Informatik>TypentheorieVersionsverwaltungDefaultSoftwareentwicklerGebäude <Mathematik>Repository <Informatik>Ein-AusgabeBootenSystemprogrammierungVersionsverwaltungProzess <Informatik>LeistungsbewertungProgrammierumgebungOverlay-NetzVariableMathematikSichtenkonzeptMultiplikationsoperatorGebäude <Mathematik>Generator <Informatik>Kartesische KoordinatenSpitze <Mathematik>Web SiteFunktionalKälteerzeugungSprachsyntheseBeobachtungsstudieBimodulQuellcodeDivisionInformationsspeicherungAutomatische IndexierungStellenringAdditionDienst <Informatik>RichtungForcingParametersystemUnrundheitGesetz <Physik>HoaxOrdnung <Mathematik>Mereologiet-TestURLAttributierte GrammatikArithmetischer AusdruckFormation <Mathematik>Funktion <Mathematik>Rechter WinkelPhysikalisches SystemDeskriptive StatistikDämpfungAntwortfunktionProjektive EbeneExistenzsatzDokumentenserverServerEntscheidungstheorieKonfiguration <Informatik>Elektronische PublikationResultanteOpen SourcePunktDefaultSoftwareindustrieEin-AusgabeDatenverwaltungDateiverwaltungARM <Computerarchitektur>CodeOktaederFahne <Mathematik>SchnittmengeBitKonfigurationsraumService providerComputervirusVerzeichnisdienstSoftwareentwicklerBootenDerivation <Algebra>Inhalt <Mathematik>MatchingKontrollstrukturComputeranimation
Overlay-NetzDienst <Informatik>Nabel <Mathematik>ComputervirusSystemprogrammierungMarketinginformationssystemEin-AusgabeVersionsverwaltungDualitätssatzBeweistheorieOffene MengeFontProgrammbibliothekGoogolCoxeter-GruppeGamecontrollerEntscheidungstheorieKomplexe DarstellungBimodulBitFormale SemantikMultiplikationsoperatorVirtuelle MaschineReelle ZahlMereologieGüte der AnpassungVerzeichnisdienstComputervirusFunktion <Mathematik>Elektronische PublikationKonfigurationsraumPlug insinc-FunktionParametersystemVersionsverwaltungRPCSubstitutionDienst <Informatik>Metrisches SystemEin-AusgabeMailing-ListeLoginAdressraumAttributierte GrammatikHash-AlgorithmusOverlay-NetzStrömungsrichtungStellenringTaskNabel <Mathematik>Fahne <Mathematik>HardwareProdukt <Mathematik>Physikalisches SystemMessage-PassingWurzel <Mathematik>Endliche ModelltheorieRoutingGrenzschichtablösungEinfügungsdämpfungKreisflächeAutomatische HandlungsplanungSystemintegrationOrtsoperatorHoaxDeterminanteVerknüpfungsgliedGebäude <Mathematik>Ordnung <Mathematik>Schnitt <Mathematik>ZweiVorzeichen <Mathematik>SchießverfahrenVirtualisierungCodeTrennschärfe <Statistik>StandardabweichungAntwortfunktionQuellcodeMaßerweiterungGruppenoperationDatenflussDämpfungComputeranimation
KontrollstrukturMultiplikationsoperatorDiskrete-Elemente-MethodeRechter WinkelComputeranimation
MultiplikationsoperatorRechter WinkelARM <Computerarchitektur>Computeranimation
Transkript: Englisch(automatisch erzeugt)
Hello, everyone. We are back with a new talk. This is talk number 4, Nixflakes in production, what, why, and how. Our presenter is Alexander Bandtiv. I hope I'm saying that properly. If I am not, I apologize. The topic of this talk is Alexander's experience using Nixflakes in production at the company Sorokil.
They've been using Nixflare for over five years, and they've just started their internal effort to use flakes in build infrastructure and projects. This talk is going to have a refresher on what flakes are also. If you've been confused by them, here's your chance to learn and have explained why flakes are so promising,
and just sharing general experiences and vice on the matter. I did forget the announcement. I forgot to say thank you properly in the chat to get a real last talk. Also, you don't have to put ones if you are non-binary, you can put hexadecimals because the binary doesn't apply to you. Anyways, go ahead.
Hi, I'm Alexander, and I write Nix at Sorokil. Once again, because of some GC issues, you won't be able to see my face, but that's probably for the better.
Lately, we have started switching to flakes, switching our production system pipelines to flakes, and I would like to explain why we do that and how we do that. You can find sources for this talk at this URL. It will go public after I finish the talk. I encourage you to build this and follow it along by yourself.
For quick reminder, what are those flakes again? Well, flakes were proposed by Ilko Dolstra in RFC 49. RFC is currently with their own, but nevertheless, the work on flakes is ongoing in Nix master branch and hopefully, the RFC will be resubmitted soon.
Okay, but what are they? A flake is basically a directory that contains a flake.nix file at the root. That's it. The repository that this talk is built from is a flake. Nix packages is a flake. Nix itself is a flake. Flakes also tend to contain a flake.logs file in them. For example, this presentation is a flake.
What's in those files? Flake.nix file is a simple Nix file that contains an attribute set. The notable attributes are description, inputs, and outputs. I'll explain what those means later in the talk. Flake.log is a JSON file that pins all the versions of the inputs.
Now that you have hopefully been reminded about what flakes are, and you hopefully understand about at least something about them, I would like to explain why we use them in production. First, let's take a look at what we need from our production build systems.
First of all, we need our production build systems to be reproducible. That means that two people building the same project should get approximately the same results. We need our build system to be easy to use, which means that people who have little experience with Nix should be able to easily at least build the project and run it. We need our build system to be cross-platform because our developers
use all three major OSes and we need to support all of them. So to understand why we are so excited about flakes, let's take a look at the alternatives. We'll use a simplified version of this talk, Nix Expression, as an example. First alternative is channels. Channels are a very simple idea. You have a separate command called nix-channel. It downloads and unpacks tarballs somewhere in your Nix path.
The benefits are quite obvious. You have a very easy way to write standalone Nix expressions using angle bracket syntax. So import Nix packages. This will give you, well, the Nix packages' dependency.
It's very easy to update dependencies using nix-channel update. And it's very easy to override dependencies by changing nix-path environment variable. The drawbacks are quite obvious as well. Nix-channel is stateful because every user has to run nix-channel add before they can build your project. Channels are not composable because channels
can depend on other channels. And the channels are not reproducible at all. Because unless you jump through some hoops, your users will get different versions of dependencies than you. Second alternative is spinning with fetch-tarball. The idea is quite simple as well.
You use a built-in fetch-tarball function. And Nix will fetch that tarball that you specify in URL at evaluation time. And then you can use it as usual, for example, with imports. The benefits are that this is somewhat reproducible because you can specify the git revision
and the hash of the tarball. It's somewhat composable because your dependencies can use the same mechanism to fetch their dependencies and its status. Your users don't have to run any comments other than nix-build. The drawbacks are that it's quite cumbersome.
You have to manually get the commit versions. You have to manually get the hashes. And to update them, you need to manually edit the default dot nix file or wherever you have those arguments, wherever you have specified those arguments. A third alternative is niv. Niv was created by Anmatia.
And it promises easy dependency management for Nix projects. And it does deliver on that promise. We have switched most of our projects to use niv before we started experimenting with flakes. And that wasn't any time wasted because transitioning from niv to flakes is easier than transitioning from channels to flakes. The benefits are that it's once again reproducible, composable, and stateless.
However, unlike fetch tarball, it's also very easy to update dependencies using the niv command. And it is to override dependencies. The only drawback is that it requires a separate tool, which doesn't integrate into other Nix commands too well. Actually, all of those approaches
have a set of common drawbacks. First of all, lack of standard structure. There is no standard format for default dot nix, meaning that every project implements their own. I have seen a default dot nix with a single derivation, another set of derivations, a list of derivations, and so on. And so it's not very easy to use because your users have
to guess which format your project uses. Second drawback is extreme reliance on Nix packages. Many of the old sort of Nix commands somehow implicitly rely on Nix packages. For example, nix shell. This is bad for projects that simply can't be added to Nix packages or that don't use Nix packages or the projects that
want to use a custom Nix packages version. There is no simple way to enforce hermetic evaluation because both vegetable and Nix are somewhat reproducible, but they don't enforce reproducibility. You can still import from imperial locations, use angle bracket syntax, et cetera.
And finally, there is no integration with the rest of Nix because there is no easy way to, for example, overwrite inputs from Nix commands directly. This is, again, not very easy to use. Well, as you might have guessed, Flakes solve all of these problems. They impose a standard structure on flake.nix, remove any reliance on Nix packages and tooling,
at least mostly, enforce hermetic evaluation by removing Nix path and built-ins current system, among other things, and integrate tightly with other Nix tooling. Let's take a look at an example of a simple flake. There is quite a lot happening in this file, and we'll get back to what everything means exactly later in the talk. For now, note that we specify inputs
by describing their locations but not their versions. So here we describe the location from which we want to fetch Nix packages. And that outputs depend on inputs. So here outputs depends on the input Nix packages. Also note that we never use built-ins current system, and instead we map over all of the platforms exported
by Nix packages. I'll explain how to do this a bit later as well. Updating and overriding is very easy, using the update input and override input command line flags that almost all Nix commands accept now.
With Flakes comes a new Nix command line interface. It is, in my opinion, it is more concise, and it is definitely more uniform. Now all of the Nix commands accept the same format of arguments, a flake reference, and they also share a lot of common flags, unlike the old interface. Old interface is still kept for compatibility.
Flakes also introduce flake references. Flake references dramatically simplify fetching remote projects. So for example, instead of writing this monstrosity, we can now just tell Nix this simple location, and it will do what you would expect. It will fetch this repository from this host
and open a shell with the default package of it. So to reiterate, Flakes solve many problems that we were facing with Nix, which is why we have decided to use them for many of our projects.
Now that you are hopefully about as excited about Flakes as we are, I would like to explain how you can use them in your current projects and integrate them into your current infrastructure. First of all, in order to manage Flakes, you will have to use a recent enough Nix. The easiest way to get such a version of Nix
is to use Nix shell pNix Unstable, which will fetch a pre-built Nix version. So you don't actually have to build anything. I also encourage you to read Ilko's blog posts on the matter. He explains in a lot more detail the decisions that were put into Flakes
and some other things. And they are a great read. There is quite a lot of information about Flakes on the web, but I haven't found much advice on how to actually write or integrate Flakes into already existing projects. I would like to remedy that by explaining what everything means in a Flake
and sharing my experiences of flaky-fying our infrastructure. Let's start by initializing our first Flake. To do that, we will create a new directory and run Nix Flake init in it. Nix Flake init is a command that places a skeleton flake.nix file in current directory. So it doesn't do anything fancy. Then we create a new Git repository
and add everything to index. I'll explain why that is important in a bit. Let's take a look at the skeleton file that Nix placed in our new directory. Description is pretty obvious. It's just a string describing the current Flake. Outputs is a function which returns
as an attribute set of well outputs. There are some standard outputs. For example, both packages and default package are standard. But you may also provide your own. Argument of outputs is an attribute set of inputs.
Self is always an input. And it refers to this very Flake that Nix is currently evaluating. Nix packages here is an indirect input, meaning its value is taken from a Flake registry and not from inputs attribute of this Flake. Don't worry about it too much for now. As you can see, all of the Flake outputs,
for example, legacy packages is an output of Nix packages, are available as top level attributes of those inputs. So Nix packages.legacy packages is an output of Nix packages. You can also access other Flake attributes, such as description or inputs. And also some Flakes provide some information
about their source. If you cast any Flake input to a string, it will return the path to that Flake source from which it was evaluated. Now let's take a closer look at this line of outputs. Packages is an output which is an attribute set where attribute names are systems, and attribute values
are attribute sets in and of themselves, but where attribute sets of derivations. So let me say that again. Packages is an attribute set where attribute names are platforms or systems, and attribute values are attribute sets of derivations.
Legacy packages is an output designed specifically for Nix packages. It's an attribute set of, once again, where attribute names are platforms or systems, but attribute values are arbitrary attribute sets. This allows us to stuff the entire Nix packages into this output.
Default package is an output which is an attribute set of derivations. And names are, once again, systems or platforms. And here we set the default package for x86.64 Linux to the output we have defined one line earlier. So self.packages.x86.66.hello is, well, it's exactly this.
Nix doesn't do any fancy notifying here. Now that we hopefully understand what everything means in a Flake.Nix file, it's time to use it. First of all, let's build the default package
for the current platform. To do that, we can just run Nix build. The first time we run Nix build, Nix will fetch all of the dependencies that we have specified in inputs and pin their versions in the Flake.log file. It also warns us that the Git tree is dirty, meaning that other people won't be able to easily reproduce our results.
We can also specify the default package for the current platform explicitly using Flake references. So dot is the URL, then hash, and then we can specify any output we like. If we run the results in binary, it outputs hello world as expected. We can also use Nix shell to get ourselves a shell
with the binary in path. Once again, when called without arguments, it will drop us in the shell with default package for current platform, but we can also run it with any arguments. And now we can just run hello because the binary is in path. Now let's examine our Flake.
Now let's examine our Flake. We can use Nix Flake list inputs command to tell Nix to list all of the inputs of our Flake. In our case, our Flake has just one input. It's Nix packages.
And as you can see, Nix tells us exactly the version, the Git revision of Nix packages we're using. You can also run Nix Flake show, which will show all of the outputs of the current Flake. So here our Flake has two outputs, default package and packages.
Now let's improve our Flake by providing outputs for all systems instead of just x86-664 Linux. As you can remember, we have specified x86-664 Linux here explicitly, which means that currently our Flake doesn't provide outputs for other systems. To do that, we will use built-ins mapatras function
to map over all of the available systems in Nix packages legacy packages. For every such system, we will return an attribute set where hello will be equal to hello from package set for that particular system. Default package is very similar. We once again use mapatras function,
but this time we map over the package's output of this Flake. And for every per platform package set, we choose hello from that package set, which we have defined here. So this means that default package for every platform
is equal to hello output for every hello package for that particular platform. Now let's try building our package for another platform. To do that, we will run a nix build command,
but this time we will explicitly specify that we want to build hello package for x86-64 Darwin. And quite surprisingly, it builds. That's because Hydra actually pre-builds a lot of packages for Darwin, and they are available for download on cache.nixos.org. So Nix just substitutes that binary instead of building it.
But obviously, if we try to run it, we get an exact format error that signifies that we're trying to run a binary for a different platform. So it works. Now let's add a runnable application. Applications are feature introduced by Flakes. They are implemented with apps and default app outputs,
which are similar to packages and default package correspondingly. Both of them are, once again, similar to packages and default package. But instead of derivations, they have attribute sets of form type equals app and program equals to the executable you want to run. So here, we define default app by mapping
over all of the platforms or packages exported by default package. And for every platform, we run the executable for the platform as a program. To run Flake applications, you use the nix around command. And if we do this here, it just outputs hello world,
just as we would expect. Now let's add a test. Here, I will add an inline bash test. But obviously, you should use the test suite provided by your actual language. Here, I will map over all of the package set
in legacy packages. And for every platform, I will use run command function to run some bash. And in that bash, we basically just execute the hello package from this current Flake for the system that we're currently checking for and compare the output to hello world, which we expect.
To check, we need to run nix Flake check. But if we run it without any flags, we won't see any output. Because by default, nix hides all of the output. If we run it with an L flag, it will show us the successful output. So it tells us that hello output is hello world.
And it expected hello world, which matches. Now let's break it and try to check. So we just replace the expected output with an incorrect one and run nix Flake check again. And now it fails, just as we would hope. Now let's package a local application instead of reexporting one from nix packages.
I'll package a simple Haskell application. I won't go over into what happens on the Haskell site. It's pretty simple. It just prints hello world. OK, yeah, for some reason, I am talking quite slowly now. I guess I'll skip the unimportant parts. So this is a very simple called packagable expression.
It just takes to argument a steady n from GHC. And it uses MK derivation to quite simply build the package using GHC with no fancy cabal or anything. So it should be quite easy to understand.
Changes to Flake.nix are very trivial as well. We just replace packages.hello with packages.go package, hello.nix, and no overrides. And if we try to build it, it doesn't work for some reason. Why is that? Well, remember, I told you that it's important to add everything to git index. That's why. Because before evaluating Flake, nix copies its content
to nix store. And it also removes everything that's not in git index. So if we add everything to git index and then try building again, now it works. And if we try to run in our application, it outputs hello world just as we have expected. And nix run also works.
Nix Flake check will break because GHC is not available for some platforms and for some complicated reasons, which I won't go into here. So nix Flake check will fail now. Let's now add an overlay. Because providing a buildable derivation is nice, but providing an overlay that can be used with any nix packages version is even nicer.
Overlays is an output which is an attribute set of overlays. And overlay is an output which is just one overlay. Overlays are functions of two arguments, final and previous. Please note that here in Flakes, nix actually enforces that the arguments are called final and previous. And here, we just overlay every simple override.
We set hello in the particular package set to the hello.nix, to call packaged hello.nix from the current directory. Let's also ship a Nix OS module with our application. This is a very simple Nix OS module. It just provides two options and a systemd one-shot service,
which just runs our hello application. Importing it into flake.nix is pretty trivial as well. Nix OS modules is an output which is an attribute set of nix OS modules. And here, we set the hello nix OS module of this flake to module.nix.
Now, let's add a version flag. I won't go into changes in the Haskell side again.
But note that it takes an environmental variable called version at build time and uses that as a version output. Changes to nix built inside are very trivial too. We just pass it a version environment variable here. Changes to flake.nix are a bit more interesting. Here, we use the fact that flake inputs actually
provide more attributes than just outputs. For example, all git flakes provide revision attribute when they are not dirty and last-modified date when they are dirty. So here, we set the version to be hello,
and then either the revision or last-modified date of this flake and nix packages with either the revision or last-modified date of nix packages.
And obviously, now, if we try it, it actually works first time. It tells us that hello was last modified on October 7, 2020, and that nix packages is this revision.
It tells us the date here because remember that we haven't committed anything to git yet. Let's do that, by the way. Now, we commit everything to git and push our current repository to some hosting. This basically meant that we have just published our first flake. We can now use flake references
to do everything we have done locally, but now remotely. So for example, if we want to run the application that we have exported, we can just use nix run and then pass in the flake reference to which we have pushed our flake.
If you don't want to publish your repo, you can just try nix run, github, balsof, hello, flake. It contains all of the files that I have discussed earlier. But wait, you might say. Unstable nix is not an option for CI developers. That's true, but worry not. You'll have a solution for you. It's called flake-compat. You can put this very simple nix expression in default.nix file.
It actually just fetches the latest version of flake-compat and then calls it with the src argument of the current flake. This has the result that now we can use all the nix version. So for example, 2.3.7 to build our flake. Some features like flake applications won't be available, and you can't manage the inputs.
But that's at least something you can easily tell your users or CI to do. Now let's deploy our application. This is a fairly common situation for software companies where you are developing an open source project, and you want to deploy it to your servers.
Obviously, including server definitions in the project would be a bad idea because other people may have their own deployments, and they obviously will have their own server definitions. So let's create a new project or repository with infrastructure for our flake. We just initialized a new Git repository. We want to do nix-flake init this time
because we will write our own nix-flake from scratch. And let's go over what I have written here. Description is, once again, a very simple description of the current flake. Inputs, we haven't encountered this before, so I'll go over what happens here. Inputs.hello.url is a URL from which
nix will fetch the flake. Here, I just set it to the URL to which we have published our flake earlier. This line, inputs.hello.inputs.nixpackages.follows tells nix to substitute whatever version hello flake, whatever version of nix packages hello flake has in its log file with the nix packages version
that this infrastructure flake is using. This allows us to reduce the amount of nix packages version we depend on, but at the same time, it removes some of the reproducibility. You can also specify flake equals true, but this is the default. If you specified flake equals false here, nix wouldn't interpret the hello flake as a flake,
and it will just give you the path to the source. This is useful for depending on projects that aren't flakes yet. Now, let's write our server definition. We'll start with a simple shim because we will be running our server in the nixOS container, and nixOS containers don't need any bootloaders
or file systems. So here, I just set both of those to dummy values so that nixOS configuration, nixOS evaluation succeeds. Now, let's look at our flake.nix. Here, we use nixOS configuration output, which is obviously an attribute set of nixOS configurations.
We set the hello nixOS configuration to the output of nix packages sleeping system, which is a function that builds nixOS configuration from some modules. We tell that nixOS system that we will be building an x86-64 Linux, nixOS system, and we also pass it a list of modules.
Modules has the same semantics as inputs in your configuration.nix file. Here, we will pass these three modules. First of all, the nixOS modules hello, which we have defined in our hello flake. We also pass it the shim.nix, which we have defined earlier, and we also pass it an attribute set, which contains an overlay from our hello flake,
and it also enables the service which we have defined in our nixOS module in the hello flake. Now, let's try it in a nixOS container. To do that, you will need a recent enough version of nixOS container that understands flakes. You can get that using nix shell, nix packages, hash nixOS container.
You can then use a pseudo nixOS container create and pass it a flake argument. This is a flake reference, so current directory and output of hello. This will actually resolve to nixOS configurations.hello. And we name our container hello. If you want to update the configuration, you just replace create with update here.
Now, you can start the container. For reasons unrelated to flakes, it will hang during boot, but you can just ignore that. Just kill it after five to 10 seconds, and you can move on. You can then run pseudo nixOS container root, looking hello to get a root shell inside the container.
Inside the container, you can restart the service to make sure that it actually outputs something, and then look at the logs for this service using journal control. And as you can see, here, it actually does output hello world to the standard output, which is great. It means that our deployment is working perfectly. Obviously, nixOS containers are in production radium,
so you want to deploy to either real hardware or virtual machines. There are plenty of tools that allow you to do that. For example, nixOS rebuild is now flake aware, so you can pass the flake argument just as you can to nixOS container.
Also, at Cerakil, we have been developing our own deploy system, which is just called deploy, quite unimaginatively. And it uses a deploy output of a flake to determine where and what it needs to deploy. You can read more about it here. And you can also use pretty much any deployment tool you have been using previously
by just building the nixOS configuration manually and then passing that pre-built nixOS configuration to your deploy tool. While I have been experimenting with flakes, I have read some of nix's source. And so I have found some features which are rather poorly documented or not documented at all
or are otherwise not obvious, at least to me. If you want to override dependency of a dependency, you can use slash. So for example, to override the hello. Oops, this is a mistake. If you want to override the hello dependency of this hello intro flake, you can pass nix flag of override inputs,
hello slash nix packages, and then pass it some flake reference. So for example, this is a local nix packages checkout. If you want to update all dependencies of some flake to latest versions, you can use a recreate log file. Actually, all nix commands accept this. This will just delete the old log file, download everything from scratch, and create a new log file.
If you want to forcefully refresh the latest version of a flake, for example, if you're using nix shell, nix will cache that. If you're using nix shell with a remote flake reference, nix will actually cache it. And so if you want to make sure you're using the latest version and not the cached one, you can run nix flake update then the flake reference, and nix will refresh that flake.
And finally, if you don't want to write the built-ins map address thing every time you're writing a flake, you can use the very awesome flake utils, flake by NumTight. And so that about wraps it up. Thank you very much for listening.
And if you have any questions, I guess there is no time left, unfortunately, because I was... And you can ask me questions, I guess, on metrics or on IRC. So one second here. I think you actually will have time for a community portion. We decided that you can... Let me check with the chat really quick.
Yes, you have time for questions for sure. So we're going to give you the full Q&A. Wow, that's awesome. Thank you. Okay, let me forward you the first question if you're ready. Yeah, I'm ready. Okay, so we have Farleyan said, what's the most challenging part about using flakes right now? And another bullet is, if you could change one thing,
what would it be? So I think you can answer the first one. I mean the second one. Yeah, the first one is that the most challenging part is I think that as with most things in nix, the documentation is... Well, it's there, but it's not complete. And another problem, I guess, is that flakes are... And the unstable nix is quite unstable actually.
So before in August, I've had, and in September, I've had quite a lot of issues with nix, which I had to somehow mitigate myself. But now the documentation actually improved a bit because there are more and more articles on flakes being published. And also unstable nix is a lot more stable now.
And actually all of our team at Serokell are mostly switching to unstable nix now. So I guess a lot of the challenges are going away. And if I would change one thing, it would be, well, my suggestion,
which I'm currently working on actually, it's that I would create a plugin that allowed built-in Svechter ball and built-in Svechget inside nixflakes to work without hash or git revision, but so that they would lock their versions in, lock the versions of what they're fetching in flake.lock.
It's a bit hard to explain, I guess, but I hope to have a PR ready soon. Or, well, maybe not soon. I don't know. It's quite a hard task. Okay. I have another question, several questions actually from Risen. The first one is, I have found that substituting inputs isn't recursive.
Do you know if that is intended? Well, actually I don't know if that's intended, but I think this should actually answer your question. So you can use this slash syntax. You can actually also use it. If you know a bit more about flakes, you can actually also use it in...
Oh, no, actually disregard. He has a couple other bullets I think that I can share. So he extends the question a little bit. So it also says, to extend on my question, let's say I want to use flake B. Flake B uses flake C and substitutes its nix packages. Now I'm in flake A
and substituting flake B's nix packages as my own. Then lastly, flake B's nix packages get substituted, but flake C's nix packages say the same as the origin of flake B's nix packages. I hope you can understand that. Yeah, I can understand that. You can do some clever things with follows to mitigate this.
So basically if you... Oh, that's a very good question. It's very hard to explain without writing some code, but you can substitute those inputs recursively actually. It's possible. We have faced this once and there is a solution,
but it's quite hard. Okay. You probably can go into the breakout room and maybe Racine can be there and you can talk about it. Yeah, that would be it, I think. So let me see if there's any other questions. And if there's not, then I will just sign off.
Okay. I don't think there's any other questions. Thank you so much. We made the last minute decision to extend yours to all the way to have the extra time we have. And I think that was a good use of time since this talk will probably be really popular for people since Nix is a hot topic to at least know about
since, as you mentioned, the situation of the documentation. So thank you excellently. Yeah. Okay. Thank you very much. Yep. And if everyone could please, please, I will beg you to put those claps, put those ones, put whatever to say thank you because in person you can't actually... You'll be able to hear the clapping,
but here we cannot hear, so just show the love. Okay. So onto announcements. Due to logistical issues, I believe this has changed over the duration our food break is. I'm sorry. I have to see what exactly that time would be. Nick, do you think you could share that
with everyone while you're in here? Like per example, maybe like you said, localize it. Yeah. Maybe something like that. I'm still not really sure since we had to update that. Do you think you could speak on that? I'm sorry to put you on the spot like this.
That's okay. Can you hear me? Yeah, I can hear you. Yep. So Dilman's talk will start at the time that it was originally scheduled for. Right. Which is 1500 UTC. So we'll be back on air approximately two hours from now at 1445 UTC.
So like four minus four, that's 11 AM. Okay, great. Thank you. I hope that explains everything to everyone. And if it isn't still clear, we can follow up something into the topic in the channels. Okay, great.
See you all then. Thanks for watching.