├── .gitignore
├── README.md
├── flake.lock
├── flake.nix
└── system-path.nix
/.gitignore:
--------------------------------------------------------------------------------
1 | result
2 | *.qcow2
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NixOS (but super-minimal)
2 | ## What is this?
3 | This started as a response to the Nixpkgs issue [#21315](https://github.com/NixOS/nixpkgs/issues/21315) - a request for a super-minimal NixOS image with almost nothing in `$PATH` but strictly neccesary software. Everything else could be installed separately.
4 |
5 | ## What's the progress?
6 | Right now the `PATH` is a little bit smaller - 90 packages vs. 117. You can see the following `nix-repl` snippet to investigate the results of this work.
7 |
8 | ```nix
9 | nix-repl> self = builtins.getFlake "github:kisik21/nixos-super-minimal"
10 |
11 | nix-repl> deprecatedPrograms = ["bash" "info" "man" "oblogout" "way-cooler"]
12 |
13 | nix-repl> deprecatedServices = ["beegfs" "beegfsEnable" "buildkite-agent" "cgmanager" "chronos" "d
14 | eepin" "dnscrypt-proxy" "fourStore" "fourStoreEndpoint" "marathon" "mathics" "meguca" "mesos" "openvpn" "osquery" "prey" "rmilter" "seeks" "winstone"]
15 |
16 | nix-repl> closure = self.inputs.nixpkgs.lib.nixosSystem ({ modules = [ self.nixosModule ]; })
17 |
18 | nix-repl> builtins.length closure.config.environment.systemPackages # profiles/minimal.nix == 117
19 | 90
20 |
21 | nix-repl> nix-repl> builtins.filter (name: system.config.programs.${name}.enable or false) (builtins.attrNames (builtins.removeAttrs system.config.programs deprecatedPrograms))
22 | [ "command-not-found" ]
23 |
24 | nix-repl> builtins.filter (name: system.config.services.${name}.enable or false) (builtins.attrNames (builtins.removeAttrs system.config.services deprecatedServices))
25 | [ "dbus" "nscd" "timesyncd" ]
26 | ```
27 |
28 | ## How to use this?
29 | Use this repository as a flake. `nixosModule` contains the NixOS module, based upon the [minimal.nix](https://github.com/NixOS/nixpkgs/blob/nixos-unstable/nixos/modules/profiles/minimal.nix) in Nixpkgs.
30 |
31 | The `checks.x86_64-linux` attribute set contains `toplevelClosure` - the top-level closure for evaluating closure size - and `vm` - a VM that autologins you as `root` and allows you to inspect the system to ensure things don't break.
32 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "nixpkgs": {
4 | "locked": {
5 | "lastModified": 1603758576,
6 | "narHash": "sha256-xKFmWVx+rFInBao3gtmvXcd0LjHD1+0c1Ef5PJDrbuM=",
7 | "owner": "NixOS",
8 | "repo": "nixpkgs",
9 | "rev": "1dc37370c489b610f8b91d7fdd40633163ffbafd",
10 | "type": "github"
11 | },
12 | "original": {
13 | "owner": "NixOS",
14 | "ref": "nixos-unstable",
15 | "repo": "nixpkgs",
16 | "type": "github"
17 | }
18 | },
19 | "root": {
20 | "inputs": {
21 | "nixpkgs": "nixpkgs"
22 | }
23 | }
24 | },
25 | "root": "root",
26 | "version": 7
27 | }
28 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | description = "Super-minimal NixOS module";
3 | inputs = {
4 | nixpkgs = {
5 | type = "github";
6 | owner = "NixOS";
7 | repo = "nixpkgs";
8 | ref = "nixos-unstable";
9 | };
10 | };
11 | outputs = inputs: let
12 | inherit (inputs) self nixpkgs;
13 | in {
14 | nixosModule = { config, pkgs, lib, ... }: {
15 | imports = [
16 | (inputs.nixpkgs + "/nixos/modules/profiles/minimal.nix")
17 | ./system-path.nix
18 | ];
19 | disabledModules = ["config/system-path.nix"];
20 | services.udisks2.enable = false;
21 | services.nscd.enable = false;
22 | # Turn off if you want to disable command-not-found
23 | programs.command-not-found.enable = lib.mkDefault true;
24 | };
25 |
26 | # The closure to check
27 | checks.x86_64-linux.toplevel-closure = let
28 | additionalConfig = { config, pkgs, lib, ... }: {
29 | nixpkgs.localSystem.system = "x86_64-linux";
30 | boot = {
31 | loader = {
32 | grub = {
33 | enable = true;
34 | efiSupport = true;
35 | device = "nodev";
36 | };
37 | };
38 | };
39 | fileSystems = {
40 | "/" = {
41 | device = "/dev/sda2";
42 | fsType = "ext4";
43 | };
44 | "/boot" = {
45 | device = "/dev/sda1";
46 | fsType = "vfat";
47 | };
48 | };
49 | };
50 | in (nixpkgs.lib.nixosSystem {
51 | modules = [
52 | self.nixosModule
53 | additionalConfig
54 | ];
55 | }).config.system.build.toplevel;
56 | checks.x86_64-linux.vm = (nixpkgs.lib.nixosSystem {
57 | modules = [
58 | self.nixosModule
59 | ({...}: { nixpkgs.localSystem.system = "x86_64-linux"; services.mingetty.autologinUser = "root"; })
60 | ];
61 | }).config.system.build.vm;
62 | };
63 | }
64 |
--------------------------------------------------------------------------------
/system-path.nix:
--------------------------------------------------------------------------------
1 | # This module defines the packages that appear in
2 | # /run/current-system/sw.
3 |
4 | { config, lib, pkgs, ... }:
5 |
6 | with lib;
7 |
8 | let
9 |
10 | requiredPackages = map (pkg: setPrio ((pkg.meta.priority or 5) + 3) pkg) [
11 | # Packages that are absolutely neccesary
12 | pkgs.bashInteractive # bash with ncurses support
13 | # Packages that are probably neccesary
14 | pkgs.su # Requires a SUID wrapper - should be installed in system path?
15 | pkgs.coreutils-full # I've seen environments that don't even need coreutils - they are pulled with Nix instead
16 | pkgs.ncurses # Includes things such as `reset` - you should have it nearby
17 | pkgs.stdenv.cc.libc # I sure hope it's here for a reason
18 | # Packages that might not be so neccesary
19 | #pkgs.acl # I don't remember the last time I used one of these
20 | #pkgs.curl # Can be pulled in case network access is required
21 | #pkgs.attr # see pkgs.acl note
22 | #pkgs.bzip2
23 | #pkgs.cpio
24 | #pkgs.diffutils
25 | #pkgs.findutils
26 | #pkgs.gawk
27 | #pkgs.getent # Clearly this is a useless utility for me
28 | #pkgs.getconf # What is this?
29 | #pkgs.gnugrep
30 | #pkgs.gnupatch
31 | #pkgs.gnused
32 | #pkgs.gnutar
33 | #pkgs.gzip
34 | #pkgs.xz
35 | #pkgs.less
36 | #pkgs.libcap
37 | #pkgs.nano # Could be downloaded separately
38 | #pkgs.netcat # Totally unneccesary in a minimal system - NixOS is generous in even providing this in the default closure, some systems don't do that
39 | #config.programs.ssh.package # Don't install by default, but include in system closure since it can't be separated from the daemon
40 | #pkgs.mkpasswd # We manage users declaratively
41 | #pkgs.procps # It can be useful, but not strictly neccesary
42 | #pkgs.time # we can measure time using the shell builtin
43 | #pkgs.utillinux
44 | #pkgs.which
45 | #pkgs.zstd
46 | ];
47 |
48 | defaultPackages = map (pkg: setPrio ((pkg.meta.priority or 5) + 3) pkg)
49 | [ pkgs.perl
50 | pkgs.rsync
51 | pkgs.strace
52 | ];
53 |
54 | in
55 |
56 | {
57 | options = {
58 |
59 | environment = {
60 |
61 | systemPackages = mkOption {
62 | type = types.listOf types.package;
63 | default = [];
64 | example = literalExample "[ pkgs.firefox pkgs.thunderbird ]";
65 | description = ''
66 | The set of packages that appear in
67 | /run/current-system/sw. These packages are
68 | automatically available to all users, and are
69 | automatically updated every time you rebuild the system
70 | configuration. (The latter is the main difference with
71 | installing them in the default profile,
72 | /nix/var/nix/profiles/default.
73 | '';
74 | };
75 |
76 | defaultPackages = mkOption {
77 | type = types.listOf types.package;
78 | default = defaultPackages;
79 | example = literalExample "[]";
80 | description = ''
81 | Set of packages users expect from a minimal linux istall.
82 | Like systemPackages, they appear in
83 | /run/current-system/sw. These packages are
84 | automatically available to all users, and are
85 | automatically updated every time you rebuild the system
86 | configuration.
87 | If you want a more minimal system, set it to an empty list.
88 | '';
89 | };
90 |
91 | pathsToLink = mkOption {
92 | type = types.listOf types.str;
93 | # Note: We need `/lib' to be among `pathsToLink' for NSS modules
94 | # to work.
95 | default = [];
96 | example = ["/"];
97 | description = "List of directories to be symlinked in /run/current-system/sw.";
98 | };
99 |
100 | extraOutputsToInstall = mkOption {
101 | type = types.listOf types.str;
102 | default = [ ];
103 | example = [ "doc" "info" "devdoc" ];
104 | description = "List of additional package outputs to be symlinked into /run/current-system/sw.";
105 | };
106 |
107 | extraSetup = mkOption {
108 | type = types.lines;
109 | default = "";
110 | description = "Shell fragments to be run after the system environment has been created. This should only be used for things that need to modify the internals of the environment, e.g. generating MIME caches. The environment being built can be accessed at $out.";
111 | };
112 |
113 | };
114 |
115 | system = {
116 |
117 | path = mkOption {
118 | internal = true;
119 | description = ''
120 | The packages you want in the boot environment.
121 | '';
122 | };
123 |
124 | };
125 |
126 | };
127 |
128 | config = {
129 |
130 | environment.systemPackages = requiredPackages ++ config.environment.defaultPackages;
131 |
132 | environment.pathsToLink =
133 | [ "/bin"
134 | "/etc/xdg"
135 | "/etc/gtk-2.0"
136 | "/etc/gtk-3.0"
137 | "/lib" # FIXME: remove and update debug-info.nix
138 | "/sbin"
139 | "/share/emacs"
140 | "/share/hunspell"
141 | "/share/nano"
142 | "/share/org"
143 | "/share/themes"
144 | "/share/vim-plugins"
145 | "/share/vulkan"
146 | "/share/kservices5"
147 | "/share/kservicetypes5"
148 | "/share/kxmlgui5"
149 | "/share/systemd"
150 | ];
151 |
152 | system.path = pkgs.buildEnv {
153 | name = "system-path";
154 | paths = config.environment.systemPackages;
155 | inherit (config.environment) pathsToLink extraOutputsToInstall;
156 | ignoreCollisions = true;
157 | # !!! Hacky, should modularise.
158 | # outputs TODO: note that the tools will often not be linked by default
159 | postBuild =
160 | ''
161 | # Remove wrapped binaries, they shouldn't be accessible via PATH.
162 | find $out/bin -maxdepth 1 -name ".*-wrapped" -type l -delete
163 |
164 | if [ -x $out/bin/glib-compile-schemas -a -w $out/share/glib-2.0/schemas ]; then
165 | $out/bin/glib-compile-schemas $out/share/glib-2.0/schemas
166 | fi
167 |
168 | ${config.environment.extraSetup}
169 | '';
170 | };
171 |
172 | };
173 | }
174 |
--------------------------------------------------------------------------------