NixOS me hizo volver a Windows

Te relato mi experiencia utilizando NixOS y por qué decidí cambiar de Linux a Windows tras 2 décadas.

Introducción

Aún recuerdo aquel día hace más de 20 años cuando, siendo un adolescente con un Intel 486 que funcionaba a pedales, me acerqué al cibercafé del barrio donde pasaba bastantes tardes jugando a Age of Empires. Pregunté al dueño si sabía como instalar Linux, ya que había oído que era capaz de funcionar en máquinas poco exigentes. Estuvimos charlando un rato y me regaló un CD de esos que venían con las revistas de informática. Contenía Red Hat Linux 5. Al entregármelo me dijo: "toma, pero Linux no es la panacea".

Salí contento y deseoso de llegar a casa para decirle adiós a un Windows 95 que tenía que formatear todas las semanas. Efectivamente, no era la panacea. De hecho no entendí nada, pero esa experiencia sembró en mí una semilla que he ido cultivando desde entonces. Con el tiempo aprendí a utilizar Linux y cada cierto tiempo probaba una distribución nueva.

Pasé bastantes años utilizando Gentoo, ya que me permitía optimizar los recursos al máximo a cambio de compilar cada paquete del sistema. Cuando mi ordenador ya era suficientemente potente y moderno, empecé a utilizar distribuciones como SUSE o Ubuntu. Por último me asenté en Arch Linux, donde he estado probablemente más de una década. Era relativamente estable, moderno y seguramente la mejor experiencia para utilizar Linux en tu casa.

A principios de 2023 apareció en mi radar NixOS. Estaba de moda. Le eché un ojo y tenía muy buena pinta, así que unos meses después, en verano, me animé a probarlo. ¿Por qué lo hice si Arch Linux funcionaba bien? Principalmente por dos motivos:

  1. Me gustan los retos y las cosas nuevas. Aunque no soy un distrohopper, NixOS parecía ofrecer una experiencia de Linux totalmente distinta al resto de distribuciones, bastante alineada con mi forma de programar. Arch Linux era lo más moderno hace unos años, pero hoy por hoy diría que da lo mismo si utilizas Arch, Ubuntu, Fedora... todas son más o menos iguales.
  2. Sentía que mi instalación de Arch Linux era un caos. Ya no sabía que archivos había modificado. Si por algún motivo tenia que reinstalar algún día, iba a ser un problema volver a poner todo como lo tenía.

Pero definitivamente, NixOS tampoco es la panacea.

Lo he utilizado unos 6 meses hasta que me he rendido y he instalado Windows 11 para utilizarlo en el día a día. Sí, has leído bien, mi experiencia con NixOS ha terminado con el USB de instalación de Windows. En realidad casi siempre he tenido Windows instalado en otra partición para jugar y hacer otras cosas que Linux no permitía, aunque rara vez lo utilizaba. Y por otro lado, aunque no me considero un experto en NixOS, creo que he alcanzado un nivel suficiente como para compartir mi experiencia tras haberlo intentarlo con todas mis ganas.

En este artículo te voy a contar mi fructuosa y a la vez desastrosa experiencia utilizando NixOS y, para continuar con el meme, creo que 2024 tampoco va a ser el año de Linux en el escritorio.

Satírica portada de periódico declarando el año 1812 como el año de Linux en el escritorio

Lo bueno de NixOS

Voy a empezar contando lo bueno de NixOS porque lo de fructuosa experiencia no era sarcasmo. El núcleo de NixOS (Nix store) es una genialidad y abre las puertas a muchas otras herramientas y características. Voy a describir un par aunque NixOS es verdaderamente una gran distribución.

Configuración declarativa

Sin duda es la característica de NixOS que más me ha gustado. Nix es, además de un gestor de paquetes, un lenguaje de programación puro, funcional, lazy, declarativo y reproducible para expresar (en archivos .nix) el estado de tu sistema. En otras palabras, no tendrás que ejecutar comandos que luego olvidarás ni tendrás que modificar ficheros esparcidos por el sistema. Realizarás toda la configuración de tu sistema mediante unos archivos que podrás guardar en Git o donde quieras.

En estos archivos puedes especificar los paquetes que deben estar instalados, la configuración de hardware (kernel, particiones, encriptación, ...), los usuarios del sistema, las extensiones de Google Chrome que se instalarán automáticamente, el tema de GTK... prácticamente todo. Alterar la configuración del sistema será tan sencillo como modificar estos archivos y cambiar el sistema al nuevo estado. Además, podrás modularizar la configuración a tu gusto gracias a la función import. Una maravilla.

Ejemplo de configuración declarativa para Firefox
  • Nix
{ pkgs, lib, ... }:

{
  # Configuración de Firefox: perfiles, extensiones, etc...
  programs.firefox = {
    enable = true;
    package = pkgs.firefox-devedition;
    profiles.felixsanz = {
      extensions = with inputs.firefox-addons.packages.${pkgs.system}; [
        ublock-origin
        decentraleyes
      ];
      settings = {
        "browser.tabs.warnOnClose" = true;
        "extensions.pocket.enabled" = false;
        "media.ffmpeg.vaapi.enabled" = true;
        # ...
      };
      userChrome = builtins.readFile ./userChrome.css;
      userContent = builtins.readFile ./userContent.css;
    };
  };

  # Variables de entorno asociadas a Firefox
  home.sessionVariables = {
    MOZ_DISABLE_RDD_SANDBOX = 1;
    MOZ_ENABLE_WAYLAND = 1;
  };

  # Configurar Firefox como el navegador por defecto
  xdg.mimeApps.defaultApplications = lib.genAttrs [
    "text/html"
    "text/xml"
    "application/xml"
    "x-scheme-handler/http"
    "x-scheme-handler/https"
    "x-scheme-handler/about"
    "x-scheme-handler/unknown"
  ] (_: "firefox.desktop");
}

Genial, ¿verdad? Sigue leyendo, no es oro todo lo que reluce.

Nix shell

La Nix shell es una potente herramienta que te permite modificar temporalmente el estado de la máquina. Por ejemplo, podrás ejecutar una aplicación sin tenerla instalada. Creas una shell con ese paquete/configuración y cuando la cierres, no dejará ni rastro.

Podrías no tener instalado Python ni Node.js de manera global, si no que cuando vas a trabajar en un proyecto específico, abres una shell con lo que necesitas y empiezas a trabajar. Esta configuración se puede guardar en un fichero shell.nix como el siguiente:

Ejemplo de Nix shell
  • Nix
{ pkgs ? import <nixpkgs> {}}:

# La shell podrá utilizar Python 3.10 y las librerías flask, requests y yeelight

pkgs.mkShell {
  packages = with pkgs; [
    python310Full
    python310Packages.flask
    python310Packages.requests
    python310Packages.yeelight
  ];
}

La utilidad aumenta cuando se combina con nix-direnv, una aplicación que instanciará automáticamente el fichero shell.nix cuando entremos al directorio del proyecto mediante cd. Se encargará de que todo esté disponible.

La comunidad

Quizás la parte más valiosa de NixOS no sea la técnica, si no la humana. En los canales de ayuda como su comunidad de Discord o el foro encontrarás gente amable siempre dispuesta a ayudar, con una paciencia que pocas veces he visto. Desde el primer momento me sentí parte de la comunidad y en todo momento recibí ayuda cuando lo necesitaba.

Lo feo

NixOS tiene bastantes cosas feas que se supone que son virtudes, pero en mi opinión están mal ejecutadas. Al menos de momento, me parecen una fuente de frustración y problemas.

Desaprendiendo todo lo que sabes

¿Tienes experiencia en Linux? Pues enhorabuena, pero no te va a servir de mucho. Crear usuarios mediante useradd, encriptar particiones con cryptsetup, configurar discos en /etc/fstab, crear alias de shell, configurar servicios de systemd... nada te va a servir. Idealmente, toda la configuración del sistema la realizarás de manera declarativa. Esto, como dije, es lo que más me gustó de NixOS pero volver a aprender todo de nuevo es un proceso lento y doloroso. Es lo que es.

Home Manager

NixOS gestiona la configuración del sistema pero no la relacionada con el usuario. Home Manager, un proyecto creado por terceros, se encarga de ello. Rellena un hueco que no debería existir. Aunque en términos generales funciona bien y es una gran solución, tiene algunos defectos que añade más frustración a la puesta a punto del sistema. El consejo que más me solían dar era que cuanto menos lo use, mejor.

Inconsistencia en las opciones

En NixOS mucha configuración se realiza utilizando opciones, por ejemplo:

Ejemplo de configuración mediante opciones
  • Nix
{
  programs.zsh = {
    enable = true;
    dotDir = ".config/zsh";
    history = {
      path = "${config.xdg.dataHome}/zsh/history";
      extended = true;
      share = false;
      ignoreAllDups = true;
    };
    initExtra = "setopt INC_APPEND_HISTORY_TIME";
    syntaxHighlighting.enable = true; # <----- X.enable y enableX, ¿por qué?
    enableAutosuggestions = true;     # <----/
    shellAliases = import ./aliases.nix { inherit config; };
  };
}

El problema es que a veces necesitas opciones que no existen y tienes que recurrir a la configuración manual utilizando xdg.configFile y derivados.

En otras ocasiones encontrarás opciones del mismo paquete tanto en NixOS como en Home Manager. ¿Sway se instala mediante programs.sway.enable o wayland.windowManager.sway.enable? Para un principiante todo esto es un muro. Otro ejemplo, hay opciones para Google Chrome pero no para Brave.

Por no hablar de todo el tiempo que inviertes generando esta configuración, buscando las opciones, lidiando con sus inconsistencias, etc.

Lo de las opciones está muy bien pero me ha parecido en el fondo bastante caótico y poco de fiar.

Lo malo

Ahora vienen las cosas que me han hecho perder innumerables días buscando una solución para que a veces nisiquiera exista.

Pésima documentación

La documentación está a mitad de camino entre pésima e inexistente. Lo siento, un proyecto mal documentado pierde muchísimo valor.

La wiki está abandonada y todo el mundo te recomienda no utilizarla. No hay documentación, tienes que leer el código fuente de los paquetes constantemente. Tampoco hay guías prácticas de cómo hacer ésto o lo otro. Además, Los recursos que encuentras online (como blogs) suelen estar desactualizados.

En resumen, es desesperante. Menos mal que la comunidad es muy activa y se vuelca mucho en ayudar a los demás, pero llega un punto que te sientes mal de preguntar tanto y a veces te atascas y no tienes ayuda inmediata, ni manera de encontrar la solución por tu cuenta.

Sólo una versión, ¿en serio?

En Arch Linux podías instalar cualquier versión antigua de un paquete fácilmente. ¿Necesitas CUDA 11.7, 11.8 o 12.1? No hay problema. En NixOS olvídate, aunque hagas tu propio paquete necesitarás gestionar todas las dependencias a mano. Solo hay un par de paquetes que están versionados (como python310 / python311), el resto... nada. Si quieres una versión más antigua o más moderna de lo que te ofrece nixpkgs, estás condenado. Y si el paquete no existe, buena suerte creándolo. En otras distribuciones podrías apañartelas de alguna manera, aquí no.

Nix shells para desarollo en Python

Esto me ha cabreado, no lo voy a negar. No quiero ni contar la cantidad de horas que he tirado a la basura intentando hacer funcionar aplicaciones de Python en NixOS. Si intentas utilizar venv y pip para instalar las dependencias de tu proyecto, empezarás a tener un montón de problemas con librerías dinámicas del sistema. Por más que lo intentes no hay manera.

Como vimos en el ejemplo más arriba, muchos paquetes de Python se encuentran en nixpkgs:

shell.nix
  • Nix
{ pkgs ? import <nixpkgs> {}}:

pkgs.mkShell {
  packages = with pkgs; [
    python310Full
    python310Packages.flask
    python310Packages.requests
    python310Packages.yeelight
  ];
}

Tanto flask, como requests y yeelight están disponibles. En realidad la mayoría están. ¿Pero qué haces cuando la que necesitas no está disponible? Nada. Perder días intentando hacer funcionar tu aplicación. Puedes solicitar el paquete en el repositorio de GitHub para que alguien se encargue de ello, o tratar de colaborar manteniéndolo tú mismo, pero es una tarea muy complicada.

En este blog estoy compartiendo la experiencia que adquiero en torno a librerías de Inteligencia Artificial, pero NixOS me ha puesto tantas trabas que ni siquiera podía hacer lo mío. Estuve esperando varios meses para que publicasen onnxruntime-gpu, pero no hay manera, ahí sigue. Parece tan complicado que nadie sabe como hacerlo.

Además, volvemos a lo mismo... sólo una versión, ¿en serio? En Python es bastante normal instalar versiones específicas de las dependencias, por ejemplo gradio==3.41.2. Esto no puedes hacerlo en NixOS. ¡Es ridículo!

Hubo un momento que conseguí hacer funcionar una aplicación de este tipo a cambio de perder 3 días enteros intentándolo. Cabe destacar que estos paquetes se tienen que compilar en tu máquina, por lo que ciertas librerías tardaban en torno a 4 horas y necesitaban más de 32GB de RAM, así que tenía que recurrir al swap al no tener memoria suficiente. Preciosa experiencia, ¿verdad?

Pues por si no fuera suficiente, semanas después cuando actualicé las liberías, se volvió a romper y ya no fuí capaz de arreglarlo ni por muchas horas que le echase. Ahí fue cuando empecé a descargar la ISO de Windows 11.

Puedes evitar la actualización de estas librerías a través del mecanismo de pinning. De hecho si no lo haces es probable que todos los días tengas que recompilar librerías y esperar horas hasta que termine.

  • Nix
pkgs = import <nixpkgs> {};
pkgs = import (builtins.fetchTarball {
  url = "https://github.com/nixos/nixpkgs/archive/2c7f3c0.tar.gz";
  sha256 = "1yswzpqq384ia4k2lyi8qicp07bvk5lsdx2y7wsd3sdvj79g72za";
})

Pero, ¿que pasa cuando necesitas actualizar una librería en concreto? Pues que tienes que actualizar TODAS, ya que nixpkgs es un snapshot donde en cada momento, únicamente hay una versión para cada librería.

En resumen, si necesitas este flujo de trabajo huye de NixOS.

NixOS es Linux

Ahora mismo estoy en un punto de mi vida en el que quiero focalizarme en lo importante y no quiero perder el tiempo lidiando con el sistema operativo. Y NixOS, sigue siendo Linux. Cada cosa "rara" que quieras hacer te va a llevar tiempo y peleas. Desde emparejar unos auriculares bluetooth hasta formatear un disco duro. Y si encima en NixOS se hace de manera distinta... prepárate para dedicarle tiempo. Nada nuevo, llevo 20 años lidiando con Linux y ya se lo que es.

En parte también es culpa mía por utilizar siempre lo último de lo último. Por ejemplo, al utilizar wayland y sway, te estás condenando a tener problemas de aceleración gráfica con GPUs NVIDIA o de programas que directamente no funcionan en wayland (como gparted).

Pero por Dios, que estamos casi en 2024 y esto no avanza.

Veredicto de NixOS

Una gran idea pero una ejecución regular. Si la documentación no fuera tan mala y nixpkgs fuese más flexible, otro gallo cantaría.

NixOS tiene unos cuantos casos de uso que son ideales para utilizarlo, pero el de escritorio creo que no es el mejor. Aún así, si vas a utilizarlo como sistema operativo para navegar por internet o tareas simples, adelante, es una gran opción. Pero como pretendas hacer desarrollo, sobre todo en Python, es una locura.

Volviendo a Windows

Un par de amigos que siempre utilizaron Arch Linux migraron por estas fechas hacia macOS y Windows. Sus comentarios eran del tipo: "esto es otro mundo, no se por qué seguía enfrascado utilizando Linux". Entre esas opiniones y mis problemas con NixOS, decidí echar nuevamente un vistazo a Windows.

Instalé Windows en un disco secundario para probarlo. Al cabo de 48 horas ya estaba reinstalándolo, esta vez en el disco principal, borrando NixOS en el proceso.

Windows no es lo que era. Antes, Linux era más moderno y tenía cosas con las que Windows apenas podía soñar. Ya no es así, creo que Linux, en general, se ha quedado un poco atrás. Windows es estable y funciona bien. Ya no veo diferencia en el consumo de recursos con respecto a Linux. Es todo mucho más fácil y parece que la privacidad se respeta más que antes (al menos ya no es tan invasivo).

Windows ha adoptado muchas prácticas de Linux. Aparte del WSL que complementa Windows a la perfección, ahora Windows cuenta con su propio gestor de paquetes llamado winget. Funciona francamente bien, no he tenido necesidad de usar Chocolatey o Scoop.

En cuanto a la configuración declarativa que ofrece NixOS, en Windows podemos conseguir algo muy parecido utilizando comandos de PowerShell y el registro de Windows. En un script de PowerShell se puede automatizar tanto la configuración del sistema/usuario como la instalación de aplicaciones/paquetes.

En cuanto a la disponibilidad de aplicaciones, por suerte hace tiempo que suelo utilizar aplicaciones escritas en Rust como starship, fd, bat, ripgrep, eza o delta. Todas funcionan en Windows ya que Rust es multiplataforma. Todas las aplicaciones de consola que utilizaba en Linux las tengo aquí también disponibles sin necesidad de utilizar el WSL, ¿qué mas se puede pedir?

Y hablando de consola, Microsoft ha creado una maravilla llamada Windows Terminal. Me encanta. Es rápida, moderna y tiene todas las funcionalidades que se le puede pedir a una aplicación de Terminal.

Captura de pantalla de Windows Terminal mostrando varias pestañas y ejecutando winfetch

Otra cosa que echaba de menos era un explorador de archivos decente. En Linux utilizaba Thunar, Nemo o PCManFM, pero estas prehistoricas aplicaciones me daban problemas de lo más variados. PCManFM, por ejemplo, me borraba ficheros cuando los movía a otra carpeta.

Mención especial también a AltSnap, una fantástica aplicación que imita parte del comportamiento de gestores de ventanas como sway o bspwm. Permite mover y cambiar el tamaño de las aplicaciones utilizando una tecla y arrastrando con el ratón.

Aquí no acaban las cosas que me han gustado de Windows. Podría destacar también la aplicación nativa de Recortes, que funciona mucho mejor que las alternativas disponibles en Linux (otro pozo de horas perdidas, sobre todo en wayland). O las Microsoft PowerToys. O AutoHotkey.

En fín, mi paso por Linux ha sido un gran trayecto en el que he aprendido mucho, pero si no cambian bastante las cosas no se si volveré. Me estaré haciendo viejo...

Puedes apoyarme para que pueda dedicar aún más tiempo a escribir artículos y tener recursos para crear nuevos proyectos. ¡Gracias!