{"id":44097403,"url":"https://github.com/drupol/nix-hands-on","last_synced_at":"2026-02-20T11:00:45.107Z","repository":{"id":321856626,"uuid":"1087417542","full_name":"drupol/nix-hands-on","owner":"drupol","description":null,"archived":false,"fork":false,"pushed_at":"2026-02-14T17:56:57.000Z","size":47,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-15T01:44:59.451Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Nix","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/drupol.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-31T21:55:08.000Z","updated_at":"2026-02-14T17:57:03.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/drupol/nix-hands-on","commit_stats":null,"previous_names":["drupol/demo-devshells-and-wrappers"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/drupol/nix-hands-on","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drupol%2Fnix-hands-on","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drupol%2Fnix-hands-on/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drupol%2Fnix-hands-on/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drupol%2Fnix-hands-on/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/drupol","download_url":"https://codeload.github.com/drupol/nix-hands-on/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drupol%2Fnix-hands-on/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29648419,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T09:27:29.698Z","status":"ssl_error","status_checked_at":"2026-02-20T09:26:12.373Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-02-08T13:00:32.362Z","updated_at":"2026-02-20T11:00:45.096Z","avatar_url":"https://github.com/drupol.png","language":"Nix","funding_links":["https://github.com/sponsors/drupol"],"categories":["Nix"],"sub_categories":[],"readme":"[![GitHub stars][github stars]][1] [![Donate!][donate github]][5]\n\n# Nix Hands-On Tutorial\n\nThis repository is a step-by-step Nix tutorial designed to help you master core\nconcepts in Nix: development shells (`devShells`), packages (`packages`), and\nNixOS configurations (`nixosConfigurations`). Each concept is demonstrated\nthrough practical examples, with each step implemented in its own commit and\nbranch for easy comparison.\n\n- **Development shells**: Temporary, reproducible environments that provide the\n  tools and dependencies in a shell. With Nix, you can define these environments\n  declaratively, ensuring consistency across different machines and\n  collaborators, and facilitating development and onboarding.\n- **Packages**: Software and tools built and managed by Nix. A package can be a\n  script, the result of a compilation, a container image, and more. Packages can\n  be reused in development shells, container images, or system configurations.\n  This tutorial shows how to create and customise your own packages within a\n  flake.\n- **NixOS configurations**: Declarative operating system configurations. These\n  configurations specify everything about your operating system, from installed\n  packages to services and system settings, making it easy to reproduce and\n  manage entire machines.\n\nBy following this tutorial, you will learn how to create and manage development\nshells, build custom packages, and define NixOS system configurations, all using\nmodern Nix workflows such as [`flakes`] and the [`flake.parts`] framework.\n\nEach step is implemented in its own branch. Use the links below to open a branch\nor view the diff between steps.\n\n---\n\n## How to Use This Tutorial\n\n- Ensure you have [Nix] installed with [`flakes`] enabled.\n- Clone this repository.\n- Switch between branches using `git checkout stepN` (replace `N` with the step\n  number).\n- Use the provided GitHub links to view branch contents or compare changes\n  between steps.\n\n---\n\n## Resources\n\n- Wombat's Book of Nix: https://mhwombat.codeberg.page/nix-book/\n- nix.dev: https://nix.dev/\n- Nix Pills: https://nixos.org/guides/nix-pills/\n- NixOS Manual: https://nixos.org/manual/nixos/stable\n- Nixpkgs Manual: https://nixos.org/manual/nixpkgs/stable\n- The Nix lectures: https://ayats.org/blog/nix-tuto-1\n\n---\n\n## Step 1: Shell script\n\nThe first step creates a shell script named `active-php-version.sh`. It will\nprint the currently active PHP version in a fancy way.\n\nThis is the starting point for the tutorial.\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step1\n- Diff: https://github.com/drupol/nix-hands-on/compare/step0..step1\n\n## Step 2: Portable Shebang\n\nIn this step, we update the shell script's Shebang (`#!`) to make it work on\nany machine with Nix installed.\n\nYou will notice that the script now works on any system with Nix, regardless of the\ninstalled software.\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step2\n- Diff: https://github.com/drupol/nix-hands-on/compare/step1..step2\n\n## Step 3: Development shells\n\nThis step introduces a `shell.nix` file to the project. This file defines a\nreproducible development environment.\n\nTo enter this environment, run `nix-shell`. It will provide the [PHP]\ninterpreter and its package manager, [Composer], regardless of what is installed\non your host system. The command `nix-shell` will first look for a `shell.nix`\nor `default.nix` file in the current directory. If found, it will set up the\ndevelopment environment by instantiating the Nix expression defined in the file.\n\nUnder the hood, `nix-shell` implicitly runs a Nix expression similar to:\n\n```shell\nnix-shell \\\n  --expr 'import (if builtins.pathExists ./shell.nix then ./shell.nix else ./default.nix)'\n```\n\nThis _legacy_ method of defining development shells is still widely used because\nof its simplicity and brevity. It is suitable for small projects or when you\nneed a quick development environment without complex requirements.\n\nIt is also possible to run `nix-shell` against a remote repository:\n\n```shell\nnix-shell \\\n  --expr '\n    let\n      repo = fetchTarball \"https://github.com/drupol/nix-hands-on/archive/refs/heads/step3.zip\";\n    in\n      import (if builtins.pathExists \"${repo}/shell.nix\" then \"${repo}/shell.nix\" else \"${repo}/default.nix\")'\n    '\n```\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step3\n- Diff: https://github.com/drupol/nix-hands-on/compare/step2..step3\n\n## Step 4: Adding a new development shell\n\nIn this step, we introduce an additional development shell, this time for the Go\nprogramming language.\n\nWhen creating multiple development shells within the same project using\n`shell.nix`, you must return an attribute set of shells rather than a single\none.\n\nTo enter a specific shell, use the `--attr` flag.\n\nTo enter the PHP shell:\n\n```shell\nnix-shell --attr php\n```\n\nTo enter the Go shell:\n\n```shell\nnix-shell --attr go\n```\n\nTo run one of these shells remotely:\n\n```shell\nnix-shell \\\n  --expr '\n    let\n      repo = fetchTarball \"https://github.com/drupol/nix-hands-on/archive/refs/heads/step4.zip\";\n    in\n      import (if builtins.pathExists \"${repo}/shell.nix\" then \"${repo}/shell.nix\" else \"${repo}/default.nix\")\n    ' \\\n  --attr go\n```\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step4\n- Diff: https://github.com/drupol/nix-hands-on/compare/step3..step4\n\n## Step 5: Migrating to flakes\n\nThis step converts the project to a _flake_. A _flake_ refers to a project\ncontaining a `flake.nix` file and exposing _output(s)_ following a specific\nstructure, based on a well-defined schema. A [`flake`] can define multiple\noutputs: development shells, packages, checks, etc.\n\n[`Flake`] is an experimental feature that provides a more reproducible and\nshareable way to define Nix projects. You will notice that a `flake.lock` file\nhas been created to lock the dependencies' versions, ensuring the development\nshell remains consistent over time. You cannot use a flake without this lock\nfile.\n\nTo enter a development shell, run `nix develop .#\u003cattribute\u003e` where `attribute`\nis the name of the development shell (e.g., `go`, `php`). If you omit the last\npart, Nix will attempt to use the `default` development shell, if one is\ndefined. This allows you to quickly access the main shell configuration, while\nstill being able to specify alternative shells by name when needed.\n\nNow that our project contains a `flake.nix` file, it can be shared and reused,\njust by sharing the repository URL.\n\nTo list the available outputs (development shells, packages, etc.):\n\n```shell\nnix flake show github:drupol/nix-hands-on\n```\n\nAlternatively, to do this locally, after cloning the repository:\n\n```shell\ngit clone https://github.com/drupol/nix-hands-on\ncd nix-hands-on\nnix flake show\n```\n\nTo \"remote-enter\" the PHP shell:\n\n```shell\nnix develop github:drupol/nix-hands-on#php\n```\n\nThe old `nix-shell` commands still work thanks to [`NixOS/flake-compat`] which\nprovides backward compatibility for flake projects.\n\n```shell\nnix-shell --attr go\n```\n\n\u003e [!IMPORTANT]\n\u003e\n\u003e The original `shell.nix` provided by [`NixOS/flake-compat`] has been slightly\n\u003e modified to use `builtins.currentSystem` to determine the current system\n\u003e dynamically. This ensures that legacy `nix-shell` commands function correctly\n\u003e across different architectures without needing to hardcode the system name in\n\u003e the command line attribute.\n\u003e\n\u003e The usage of `builtins.currentSystem` is generally restricted in `flake.nix`,\n\u003e unless the `--impure` flag is used. This is because [`flakes`] are designed to\n\u003e be pure and reproducible, and relying on the host system's details introduces\n\u003e \"impurity\", making the build dependent on the machine running it.\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step5\n- Diff: https://github.com/drupol/nix-hands-on/compare/step4..step5\n\n## Step 6: Multi-system support\n\nThis step focuses on making the flake compatible with multiple system\narchitectures (e.g., `x86_64-linux`, `aarch64-darwin`).\n\nWe explicitly list supported systems and use a helper function to generate\noutputs for each of them, replacing the single-system definition.\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step6\n- Diff: https://github.com/drupol/nix-hands-on/compare/step5..step6\n\n## Step 7: Using an existing systems list\n\nIn this step, we remove the hardcoded list of systems. Instead, we use the\ndefault list of supported systems provided by [`NixOS/nixpkgs`]. This reduces\nmaintenance burden and ensures we support standard architectures.\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step7\n- Diff: https://github.com/drupol/nix-hands-on/compare/step6..step7\n\n## Step 8: Introducing `flake.parts`\n\nThis step introduces [`flake.parts`], a framework designed to structure Nix\nfiles. As projects grow, a single `flake.nix` file can become difficult to\nmanage. [`flake.parts`] solves this by providing a modular structure that\nsimplifies the organisation of your files into \"parts\". The framework does not\nenforce a specific project structure; instead, it offers flexibility while\npromoting best practices.\n\nA key benefit is that it abstracts away boilerplate code. For instance, it\nhandles the multi-system logic that we had to write manually in the previous\nsteps. By promoting a clean, modular design, [`flake.parts`] makes Nix\nconfigurations easier to manage and scale the flake outputs, making it an\nexcellent choice for this tutorial and beyond.\n\n[`flake.parts`] does not invent something entirely new _per se_. Instead, it\nleverages Nix's inherent modularity by bringing the concept of [modules] to\n[`flakes`]. This allows you to break down complex configurations into smaller,\nmanageable, and reusable pieces.\n\n\u003e **Note**: Using [`flake.parts`] is intentional and opinionated. However, there\n\u003e are many other Nix frameworks available. Feel free to explore alternatives and\n\u003e choose the one that best fits your needs and preferences.\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step8\n- Diff: https://github.com/drupol/nix-hands-on/compare/step7..step8\n\n## Step 9: Splitting into modules\n\nThis step demonstrates how to split the `flake.nix` file into multiple files\n(modules), leveraging the benefits of [`flake.parts`]. Each file (or module) is\nresponsible for a specific aspect of the [`flake`], such as defining development\nshells or packages. This modular approach enhances maintainability and\nreadability.\n\nNix will automatically merge these modules together to create the final\nconfiguration of the [`flake`]. This allows you to organise your code logically,\nmaking it easier to navigate and manage as the project grows.\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step9\n- Diff: https://github.com/drupol/nix-hands-on/compare/step8..step9\n\n## Step 10: Autoloading modules\n\nWe introduce [`vic/import-tree`] to automatically load all modules from a\ndirectory. This means you no longer need to manually import every new file you\ncreate; simply adding a file to the `modules/` directory includes it in the\nconfiguration.\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step10\n- Diff: https://github.com/drupol/nix-hands-on/compare/step9..step10\n\n## Step 11: Using make-shell\n\nWe adopt the [`make-shell`] component from [`flake.parts`]. This allows us to\ndefine development shells in a more declarative and concise way within our\nmodules.\n\n\u003e **Note**: Thanks to [`flake.parts`], a single development shell can be\n\u003e declared across multiple files and parts. Nix will merge these modules\n\u003e together to create the final configuration of the development shell. This\n\u003e feature is not demonstrated in this tutorial.\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step11\n- Diff: https://github.com/drupol/nix-hands-on/compare/step10..step11\n\n## Step 12: Defining multiple shells\n\nWe add shells for additional languages: Go (`go`), NodeJS (`node`), Python\n(`python`), and Rust (`rust`).\n\nThanks to [`vic/import-tree`], adding these was as simple as creating a new\nfile. You can enter any of these shells with:\n\n```shell\nnix develop .#rust\n```\n\nYou can also enter these shells remotely without cloning the repository:\n\n```shell\nnix develop github:drupol/nix-hands-on#rust\n```\n\nAnd obviously, you can still use the old `nix-shell` command if you prefer\nbecause the backward compatibility is still provided by [`NixOS/flake-compat`]:\n\n```shell\nnix-shell --attr rust\n```\n\nAnd finally, we can also do that remotely with `nix-shell`:\n\n```shell\nnix-shell \\\n  --expr '\n    let\n      repo = fetchTarball \"https://github.com/drupol/nix-hands-on/archive/refs/heads/step12.zip\";\n    in\n      import (if builtins.pathExists \"${repo}/shell.nix\" then \"${repo}/shell.nix\" else \"${repo}/default.nix\")\n    ' \\\n  --attr rust\n```\n\nNotice how verbose the last command is! This is one of the reasons why using\n[`flake`] is more convenient in such cases.\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step12\n- Diff: https://github.com/drupol/nix-hands-on/compare/step11..step12\n\n## Step 13: Creating a wrapper\n\nThis step demonstrates how to create a \"wrapper\" around an existing program.\nWrappers are useful for setting environment variables or passing default\narguments to a program without modifying the upstream package source. Here, we\ncreate a wrapped version of a tool with custom defaults.\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step13\n- Diff: https://github.com/drupol/nix-hands-on/compare/step12..step13\n\n## Step 14: Using `lassulus/wrappers`\n\nWe replace our manual wrapper code with the [`lassulus/wrappers`] library. This\nlibrary provides convenient, reusable functions for simplifying wrapper\ncreation, making our code cleaner and less error-prone.\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step14\n- Diff: https://github.com/drupol/nix-hands-on/compare/step13..step14\n\n## Step 15: From a script to a package\n\nIn this step, we take the shell script from Step 2 (`active-php-version.sh`) and\npackage it properly. This makes the script available as a buildable Nix package\nthat can be run on any system.\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step15\n- Diff: https://github.com/drupol/nix-hands-on/compare/step14..step15\n\n## Step 16: Add another custom package\n\nWe add another custom package, `nodejs14-bin`, to the flake. This demonstrates\nhow to package binaries or other software.\n\nYou can run this package without installing it:\n\n```shell\nnix run .#nodejs14-bin -- --version\n```\n\nOr build it:\n\n```shell\nnix build .#nodejs14-bin\n\n# ./result/bin/node --version\n# v14.21.3\n```\n\nSince we want to ensure compatibility with legacy Nix (non-flake), the\nfile `default.nix` has been added. This file comes from [`NixOS/flake-compat`]\nand is a bridge between the flake and non-flake worlds as we have seen before.\nIt exposes the same outputs as the flake itself. Therefore, you can also build\nthe package with the `nix-build` command:\n\n```shell\nnix-build --attr nodejs14-bin\n\n# ./result/bin/node --version\n# v14.21.3\n```\n\nOr remotely:\n\n```shell\nnix-build \\\n  --expr '\n    let\n      repo = fetchTarball \"https://github.com/drupol/nix-hands-on/archive/refs/heads/step16.zip\";\n    in\n      import (if builtins.pathExists \"${repo}/shell.nix\" then \"${repo}/shell.nix\" else \"${repo}/default.nix\")\n    ' \\\n  --attr nodejs14-bin\n\n# ./result/bin/node --version\n# v14.21.3\n```\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step16\n- Diff: https://github.com/drupol/nix-hands-on/compare/step15..step16\n\n## Step 17: Patch an existing package with a custom patch\n\nIn this step, a new package `vhs-asciinema` will be created. This package is\nderived from the `nixpkgs` package [`vhs`] where a patch will be applied to add\na new feature to support the `asciinema` output format.\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step17\n- Diff: https://github.com/drupol/nix-hands-on/compare/step16..step17\n\n## Step 18: Using custom packages in development shells\n\nIn this step, two new development shell environments will be created.\n\nThe `node14` development shell specifically includes the custom `nodejs14-bin`\npackage. This shows how to consume your own flake outputs within your own\ndevelopment environments.\n\nThe `vhs` development shell includes the custom package `vhs-asciinema`.\n\n```shell\nnix develop .#node14\n```\n\nAlternatively, remotely:\n\n```shell\nnix develop github:drupol/nix-hands-on#node14\n```\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step18\n- Diff: https://github.com/drupol/nix-hands-on/compare/step17..step18\n\n## Step 19: Adding formatting checks with `treefmt`\n\nWe integrate [`treefmt-nix`] to enforce code formatting. This allows us to\nformat the entire project (Nix files, Markdown, etc.) with a single command:\n\n```shell\nnix fmt\n```\n\nIt also adds a check to `nix flake check` to ensure CI pipelines fail if code is\nnot formatted.\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step19\n- Diff: https://github.com/drupol/nix-hands-on/compare/step18..step19\n\n## Step 20: Building a container image\n\nWe define a new package, `nodejs14-bin-image`, which builds a container image\n(OCI-compliant) containing our custom `nodejs14-bin` package using\n`dockerTools`.\n\n```shell\nnix build .#nodejs14-bin-image\npodman load -i ./result\npodman run -ti --rm localhost/nodejs14-bin-image:latest node --version\n\n# v14.21.3\n```\n\nThis image is bit-for-bit reproducible.\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step20\n- Diff: https://github.com/drupol/nix-hands-on/compare/step19..step20\n\n## Step 21: Adding a NixOS configuration\n\nFinally, we show that a flake can also define full NixOS system configurations.\nWe add a `nixosConfigurations` output for a custom system.\n\nTo build the system `my-custom-config`:\n\n```shell\nnix build .#nixosConfigurations.my-custom-config.config.system.build.toplevel\n```\n\nOr run it in a VM:\n\n```shell\nexport QEMU_NET_OPTS=\"hostfwd=tcp:127.0.0.1:2222-:22,hostfwd=tcp:127.0.0.1:8080-:80\"\nnix run .#nixosConfigurations.my-custom-config.config.system.build.vm\n```\n\n\u003e [!NOTE]\n\u003e\n\u003e The `QEMU_NET_OPTS` environment variable is used to forward ports from the\n\u003e host machine to the virtual machine. In this example, port `2222` on the host\n\u003e is forwarded to port `22` (SSH) on the VM, and port `8080` on the host is\n\u003e forwarded to port `80` (HTTP) on the VM. Try connecting via SSH\n\u003e (`ssh -p 2222 your-username@127.0.0.1`) or visiting the HTTP server\n\u003e (`http://127.0.0.1:8080/`)!\n\nTo deploy it to your system:\n\n```shell\nnixos-rebuild switch --flake .#my-custom-config\n```\n\nTo deploy onto another machine via SSH:\n\n```shell\nnixos-rebuild switch --flake .#my-custom-config --target-host user@remote-host\n```\n\n- View branch: https://github.com/drupol/nix-hands-on/tree/step21\n- Diff: https://github.com/drupol/nix-hands-on/compare/step20..step21\n\n---\n\n## Useful Commands\n\n- Show all available outputs from the flake (shells, packages, etc.):\n  `nix flake show`\n- Update all flake inputs and re-generate the lock file: `nix flake update`\n- Enter the `default` development shell: `nix develop` (this is equivalent to\n  running `nix develop .#default`)\n- Enter the `go` development shell: `nix develop .#go`\n- Run a command from a package without installing it:\n  `nix shell .#treefmt --command treefmt`\n- Clean up old, unused packages from the Nix store: `nix-collect-garbage -d`\n\n---\n\n[1]: https://github.com/drupol/nix-hands-on\n[5]: https://github.com/sponsors/drupol\n[github stars]:\n  https://img.shields.io/github/stars/drupol/nix-hands-on.svg?style=flat-square\n[donate github]:\n  https://img.shields.io/badge/Sponsor-Github-brightgreen.svg?style=flat-square\n[Nix]: https://nixos.org/download.html\n[PHP]: https://www.php.net/\n[Composer]: https://getcomposer.org/\n[`nixpkgs`]: https://github.com/NixOS/nixpkgs\n[`flake`]: https://wiki.nixos.org/wiki/Flakes\n[modules]: https://nix.dev/tutorials/module-system/\n[`flakes`]: https://wiki.nixos.org/wiki/Flakes\n[`flake.parts`]: https://flake.parts\n[`vic/import-tree`]: https://github.com/vic/import-tree/\n[`make-shell`]: https://flake.parts/options/make-shell.html\n[`treefmt-nix`]: https://treefmt.com\n[`lassulus/wrappers`]: https://github.com/lassulus/wrappers\n[`NixOS/flake-compat`]: https://github.com/NixOS/flake-compat\n[`NixOS/nixpkgs`]: https://github.com/NixOS/nixpkgs\n[`vhs`]: https://github.com/charmbracelet/vhs\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrupol%2Fnix-hands-on","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdrupol%2Fnix-hands-on","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrupol%2Fnix-hands-on/lists"}