O que é Nix e como utilizá-lo como seu gerenciador de pacotes
Tags: nix, flakes, homemanager
Published: 2022-06-18 00:20:31

---
title: O que é Nix e como utilizá-lo como seu gerenciador de pacotes
published: true
description: Neste post ensino como configurar rapidamente o Nix com flakes e home manager para que você possa gerenciar seus dotfiles e pacotes de uma forma declarativa, reprodutível e segura
tags: nix, flakes, homemanager
cover_image: https://repository-images.githubusercontent.com/3386088/c9347c80-7b49-11e9-972e-03413da7be7a
series: Nix
---

Quando eu comecei a trabalhar na [Tweag](tweag.io), eu conheci uma ferramenta chamada Nix.
Na *Tweag* praticamente todo mundo ama e usa Nix e eu me senti encorajado a aprender mais sobre ele.
Então, depois de ler, estudar e de receber uma grande ajuda do pessoal do trabalho, eu comecei a usar Nix como meu gerenciador de pacotes e configurações e estou gostando bastante.

Antes de mais nada, vamos partir do princípio...

## O que é Nix?

Diretamente do [site oficial](https://nixos.org/)
> *Nix* is a tool that takes a unique approach to package management and system configuration. Learn how to make reproducible, declarative and reliable systems.

Traduzindo de forma livre
> *Nix* é uma ferramenta que possui uma forma única de gerenciamento de pacotes e configuração de sistema. Aprenda como criar sistemas reprodutíveis, declarativos e confiáveis.

Isso não nos diz muita coisa, que "forma única" é essa? Vamos falar sobre os 3 pontos descritos acima: configuração declarativa, reprodutiva e confiável

### Configuração declarativa

Em *Nix* não instalamos pacotes como em gerenciadores de pacotes tradicionais, isto é, executando um comando como `apt install` ou `brew install`. Até podemos fazer isso, mas não é a proposta, pois isso é um comando **imperativo**.

A proposta do *Nix* é configurar de forma **declarativa** e nós fazemos isso através de um arquivo de configuração aonde listamos todos pacotes e configurações, ou seja, nós **declaramos** quais pacotes queremos instalar e como configura-los.

Por exemplo, em vez de `sudo apt install git wget yarn`, eu tenho, em meu arquivo de configuração, as linhas:

```nix
home = {
  packages = with pkgs; [
    wget
    yarn
  ];
};
programs = {
  git = {
    enable = true;
    userName = "Douglas M.";
    userEmail = "douglasmassolari@hotmail.com";
  };
}
```

Repare que, além de listar os pacotes que quero instalar,  posso configurar, diretamente, o git, através do Nix. Essas configurações serão, automaticamente, aplicadas por ele no arquivo de configuração do git.

Isso, obviamente, não se limita ao git, sendo possível configurar diversas aplicações como zsh, gh, kitty etc.
Outro ponto interessante é que, olhando o arquivo de configuração, fica bem claro quais os pacotes eu instalei, diferentemente de eu fazer um `apt list`, onde ele lista tudo o que eu instalei **E** as dependências instaladas.

### Configuração reprodutível

Como temos esse arquivo de configuração, no qual declaramos os pacotes e temos a configuração dos mesmos, é fácil reproduzir um ambiente.

E é ainda mais fácil quando usamos o [**flakes**](https://nixos.wiki/wiki/Flakes)!

Flakes permite que, além dos pacotes do repositório oficial do Nix, nós possamos declarar pacotes diretamente do GitHub, além disso, ele cria um arquivo `flake.lock` que contém a versão exata do pacote. Assim você consegue reproduzir de forma mais fiel uma configuração.

### Configuração confiável

Umas das funcionalidades mais interessantes do *Nix* são as *generations*.

Para aplicar as suas configurações, você manda o *Nix* fazer o *build* do arquivo. Ele vai ler o seu arquivo, gerar os binários dos pacotes e o resultado disso é uma *generation*, cada vez que você faz um *build*  que difere do anterior, uma nova *generation* é criada. Sendo que ele não apaga a anterior automaticamente, o que torna possível que você consiga aplicar uma *generation* criada anteriormente!

Isso é muito útil porque, caso você desconfigure alguma coisa, você pode simplesmente voltar para a última configuração funcional que você tinha.

É útil também para testar qualquer coisa sem medo de quebrar alguma coisa.

Um ponto interessante disso tudo é que ele só gera os binários e arquivos daquilo que está no seu arquivo de configuração, ou seja, nunca mais você vai ter "lixo" no seu sistema de coisas que você instalou e desinstalou.

## Começando a utilizar o Nix

Depois de tanta explicação, vamos colocar a mão na massa e começar a instalar e a utilizar o *Nix* para gerenciar nossos pacotes.

### Instalação do Nix

Primeiro, claro, precisamos instalar o Nix.

Na [página oficial de instalação](https://nixos.org/download.html) tem as instruções bem claras, basta segui-las e vai dar tudo certo 🤞🏻

### Criação do arquivo `flake.nix`

Vamos começar a criar o arquivo que vai conter nossas configurações.

Esse arquivo precisa estar em um repositório, então, se você ainda não tiver um repositório de **dotfiles**, você pode criar com os comandos:

```sh
mkdir dotfiles
cd dotfiles
git init
```

Dentro dessa pasta, crie o arquivo `flake.nix` com o seguinte conteúdo:

```nix
{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
    home-manager = {
      url = "github:nix-community/home-manager/master";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { self, nixpkgs, home-manager, ... }:
    let
      system = "x86_64-linux"; # Se usa Mac: "x86_64-darwin"
      username = "USERNAME";
      pkgs = import nixpkgs {
        inherit system;
        config.allowUnfree = true;
      };
    in
    {
      homeConfigurations.${username} = home-manager.lib.homeManagerConfiguration {
        inherit pkgs;
        modules = [
          ({ config, ... }: {
            home = {
              stateVersion = "22.11";
              inherit username;
              homeDirectory = "/home/${username}"; # Se usa Mac: "/Users/${username}"

              file.".config/nix/nix.conf".text = ''
                experimental-features = nix-command flakes
              '';

              # Se usa Mac, descomente o código abaixo
              #
              # Isso vai fazer com que os programas instalados
              # apareçam no spotlight
              #
              # file."Applications/Home Manager Apps".source = let
              #   apps = pkgs.buildEnv {
              #     name = "home-manager-applications";
              #     paths = config.home.packages;
              #     pathsToLink = "/Applications";
              #   };
              # in "${apps}/Applications";

              packages = with pkgs; [ ];
            };

            programs = {
              home-manager.enable = true;
            };
          })
        ];
      };
    };
}
```

**Nota: Onde está `USERNAME` você precisa colocar o seu nome de usuário.**

Vamos falar um pouco sobre o conteúdo desse arquivo.
Você precisa saber algumas coisas sobre ele:

1. A linguagem na qual ele é escrita se chama *Nix*.
Pois é, além de um gerenciador de pacotes e um sistema operacional (*NixOS*), *Nix* também é a linguagem que usamos em arquivos `nix`.

2. Esse arquivo é um *flake* (você pode ter percebido pelo nome).
Mais acima, eu falei sobre flakes e deixei um *link* para a página oficial onde você pode saber mais sobre ele.
Resumidamente, ele facilita a maneira com que usamos o *Nix* para fazer nossa configuração.
Por causa dele é que temos `inputs` e `outputs` no arquivo.

3. Esse arquivo é como um arquivo JSON, é composto de chaves e valores, porém, diferentemente do JSON, ele permite que usemos alguma lógica enquanto escrevemos.
Podemos definir variáveis, usar `if` e mais.
[Aqui tem um pequeno tutorial](https://learnxinyminutes.com/docs/nix/) sobre a linguagem Nix.

Nos `inputs` nós declaramos nossas dependências que, neste caso, são duas:
1. O `nixpkgs`, que é o repositório de pacotes do Nix, estamos usando a versão *unstable*, mas você não precisa se preocupar com isso porque os pacotes lá são bem estáveis, apesar do nome.
2. O home-manager, que é uma ferramenta que permite gerenciarmos os pacotes e configuração à nível de usuário, que é o nosso objetivo. Você pode ler mais sobre ele na [wiki do NixOS](https://nixos.wiki/wiki/Home_Manager).

Nos `outputs` nós dizemos o que queremos gerar.

Vou explicar passo-a-passo o que estamos fazendo aqui, mas não vou explicar detalhes da linguagem, para isso que eu passei o *link* do tutorial acima.

A gente está passando uma função e, no corpo dela, começamos declarando 3 variáveis dentro do bloco `let/in` e definimos um valor no campo `homeConfigurations.${username}`.
O valor para esse campo é o retorno da função `home-manager.lib.homeManagerConfiguration`.
Para essa função, nós passamos um *set*.
`inherit nome` é a mesma coisa que `nome = nome`.
Em `modules` nós definimos os módulos que queremos configurar, que é onde fica nossa configuração de fato, com os pacotes e a configuração de cada um.
Nós definimos um módulo com duas chaves principais: `home`, que são as configurações relacionadas à pasta *HOME* e a lista de pacotes que queremos instalar, e `programs` que é onde vamos **habilitar** e configurar **programas** que são suportados pelo home-manager. Veremos isso melhor mais adiante. 
Começamos habilitando o `home-manager`, assim ele mesmo se gerencia. Com essa sintaxe `PROGRAMA.enable` dentro de `programs` podemos instalar e configurar vários pacotes.
Usamos `file` dentro de `home` para criar um arquivo do *nix* na nossa pasta *HOME* com uma definição para habilitar o *flakes* sempre, assim, não precisamos passar `--extra-experimental-features` o tempo todo.
Em `packages` dentro de `home` é onde vamos declarar os pacotes que queremos instalar, repare que iniciamos com `[]`, que é uma lista vazia.

### Instalando o home-manager

O primeiro passo é adicionar o arquivo `flake.nix` ao *staging* do repositório git, basta executar:

```sh
git add flake.nix
```

Depois, vamos fazer o *build* do nosso `flake.nix` executando:

```sh
nix --extra-experimental-features 'nix-command flakes' build .#homeConfigurations."\"$USER\"".activationPackage 
```

Esse comando é grande, mas não é complexo.
Na verdade, o comando é `nix build`, mas temos que passar o argumento `--extra-experimental-features` para habilitar o *flakes* e o *nix-command*, que são necessários dessa vez.
O que vem depois é só a gente passando o que ele tem que fazer o `build`, nesse caso, é o arquivo flake que existe na pasta atual, por isso `.` e, dentro desse arquivo (`#`) queremos executar a função `activationPackage` que existe dentro do `homeConfigurations.$USER`.

Não se preocupe porque só temos que executar esse comando uma vez.

Quando ele terminar, vai existir uma pasta `result` e, dentro dela, um binário chamado `activate`.
Vamos executá-lo:

```sh
./result/activate
```

Ao terminar, você já deve ter o comando `home-manager` disponível.

```sh
$ home-manager --version
22.05
```

A partir daqui, já conseguimos utilizar o *Nix* com home-manager para instalar e configurar nossos pacotes 🎉

### Instalando e configurando pacotes

#### Usando `programs`

Vamos começar com o `git`, que é um programa comum que todos devemos ter.

Adicione, dentro de `programs`, as linhas:

```nix
git = {
  enable = true;
  userName = "NOME DE USUÁRIO";
}
```

Salve o arquivo e, no terminal, execute:

```sh
home-manager switch --flake .
```

Esse é o comando que você vai usar sempre que alterar sua configuração.

Ele vai fazer o *build* e criar uma *generation*.

Ao executar `which git` você deve reparar que o caminho fica na pasta `.nix-profile/bin`, o que significa que o `git` foi instalado pelo nix.

Ao executar `git config user.name`, você deve receber como retorno o valor que você definiu em seu arquivo de configuração.

Repare que não usamos a lista de pacotes `packages` em `home`, isso porque o home-manager possui a **opção** `programs.git`.

Para saber quais opções do home-manager existem, como `programs.git`, você pode olhar [na página de opções do home-manager](https://rycee.gitlab.io/home-manager/options.html)

Na linguagem Nix, os dois códigos abaixo, para habilitar o `git`, são equivalentes:

```nix
programs = {
  git = {
    enable = true;
  };
};

programs.git.enable = true;
```

Na documentação de opções do home-manager, você vai ver as opções sendo descritas do segundo modo, mas você pode escrever do primeiro modo, ou até uma mistura do dois, como por exemplo:

```nix
programs = {
  git = {
    enable = true;
    userName = "userName"
  };

  bat.enable = true;

  fzf.enable = true;
}
```

#### Usando `home.packages`

Agora, vamos supor que você queira instalar o `wget`.

O home-manager não possui essa **opção**, então não vamos usar o `programs`, em vez disso, vamos procurar por esse pacote no **nixpkgs** que é o repositório de pacotes do Nix.

Para procurar por um pacote, você pode usar o comando `nix search nixpkgs PACOTE` ou ir na [página de pesquisa do nix](https://search.nixos.org/packages) e digitar o nome do pacote que deseja instalar.

Se buscarmos `wget` na página de pesquisa, encontramos o que queremos como primeiro resultado, isso significa que o pacote está no nixpkgs, então basta o colocarmos na em `home.packages`:

```nix
home = {
  # ...
  packages = with pkgs; [
    wget
  ];
};
```

Basta executarmos o comando:

```sh
home-manager switch --flake .
```

E, depois que o Nix terminar de fazer o *build*, o `wget` já vai estar instalado!

### Generations

### Listar

Cada vez que você faz o *build*, você vai ver o número da *generation* criada.
Para listar as *generations* você pode fazer

```sh
home-manager generations
```

Ele vai te mostrar as *generations* existentes em ordem decrescente com o caminho para cada uma.

### Rollback

Para fazer um *rollback*, ou seja, retornar para uma *generation* anterior, basta copiar o caminho dela e executar o arquivo `activate` que existe dentro da pasta dela.
Por exemplo, ao listar minhas *generations* eu vejo que tenho:

```sh
$ home-manager generations
2022-06-17 21:06 : id 56 -> /nix/store/g5qzmkcwbxx786x8lb9xff776gffngyg-home-manager-generation
2022-06-17 21:05 : id 55 -> /nix/store/hb4p96vhkdvwirdsrjh5zi7cljd5d6mk-home-manager-generation
2022-06-15 13:02 : id 54 -> /nix/store/g5qzmkcwbxx786x8lb9xff776gffngyg-home-manager-generation
2022-06-08 12:46 : id 53 -> /nix/store/hb4p96vhkdvwirdsrjh5zi7cljd5d6mk-home-manager-generation
```

Se eu quero voltar para a 54, eu copio o caminho dela `/nix/store/g5qzmkcwbxx786x8lb9xff776gffngyg-home-manager-generation` e executo o arquivo `activate` dela:

```sh
$ /nix/store/g5qzmkcwbxx786x8lb9xff776gffngyg-home-manager-generation/activate
```

### Apagar

Como não existe almoço grátis, as *generations* ocupam espaço em disco, então você vai querer ir deletando as mais antigas.

Você pode usar o comando `home-manager remove-generations` para remover uma *generation*.

Para remover uma série delas, você pode usar `{N..M}`.
Por exemplo, se eu quero remover todas as minhas *generations* da 1 até a 54 eu posso fazer:

```sh
home-manager remove-generations {1..54}
```

## Dicas finais

### Makefile

Para não ter que escrever sempre `home-manager switch --flake .`, você pode criar um arquivo `Makefile` com o conteúdo:

```make
switch:
	home-manager switch --flake .
```

Assim você só precisa executar `make` e ele vai executar o comando do `home-manager`!

### Meu repositório

Eu ainda não tenho muita coisa configurada, mas, se quiser dar uma olhada nas minhas configurações, é só ir [ao meu repositório de dotfiles](https://github.com/Massolari/dotfiles)

Espero que o *Nix* seja útil para você! Vou ficando por aqui!