1. 21
  1.  

  2. 3

    You can install using a prebuilt image too. Some rebuild time, but pretty easy…

    I found the instructions on the wiki lacking, so wrote my own.

    # Find a build from https://hydra.nixos.org/job/nixos/trunk-combined/nixos.sd_image_raspberrypi4.aarch64-linux -
    # in this case, we're using https://hydra.nixos.org/build/118111398.
    
    # Curl the image and flash your SD card in one fell swoop.
    # From the install machine, replace mmcblk0 with wherever your SD card is.
    # Verify the printed SHA256 against https://hydra.nixos.org/build/118111398.
    # (Click "details" for the bz2 archive to see the expected hash):
    curl -L https://hydra.nixos.org/build/118111398/download/1/nixos-sd-image-20.09pre223885.d6fcf36e478-aarch64-linux.img.bz2 | tee >(sha256sum >&2) >(bzcat | dd if=/dev/stdin of=/dev/mmcblk0 bs=4M status=progress) >/dev/null
    
    # Boot the NixOS install image, and run, from the console:
    sudo -s
    passwd nixos
    systemctl start sshd
    
    # Upgrade the install image in-place to a customized image.
    # Connecting over ssh is probably easier for these since you'll need to copy a configuration over.
    # Just ssh nixos@nixos-host and enter the password you chose.
    # Then elevate to root, you'll need it.
    sudo -s
    
    # This needs to be manually mounted for NixOS to correctly change the boot generation when you rebuild for the first time.
    # The config will make this permanent.
    mkdir -p /boot
    mount /dev/disk/by-label/FIRMWARE /boot
    
    # Update the channel to the latest:
    nix-channel --update
    
    # Create swap space. The config will make this permanent.
    fallocate -l 1g /swap
    chmod 0600 /swap
    mkswap /swap
    swapon /swap
    
    # Copy over configuration.nix to /etc/nixos/configuration.nix.
    # Change "4" to "3" in the bootloader config if you're on a rpi3.
    # See the comment in the configuration file about what to do if /boot runs out of space.
    nixos-rebuild boot
    
    # Set a root password.
    passwd root
    
    # Boot into the customized image:
    reboot
    
    # Now you are no longer in an install image and can use the system!
    # Reclaim SD space by GCing components from the install image.
    rm -rf /home/nixos
    nix-collect-garbage -d
    

    Use this configuration.nix:

    { pkgs, lib, ... }:
    
    /*
     * This config is derived from: https://nixos.wiki/wiki/NixOS_on_ARM/Raspberry_Pi
     * Note: if you get a "no space left on device" when building this, run:
     *     rm -rf /boot/kernel.img* /boot/initrd* /boot/old
     * and run your nixos-rebuild again. The boot partition is rather small.
     */
    {
      imports = [ ];
    
      ###
      ### This section is critical for the raspberry pi to boot.
      ###
      boot.loader.grub.enable = false;
      boot.loader.raspberryPi = {
        enable = true;
        version = 4; # change to 3 for rpi3
        firmwareConfig = ''
          gpu_mem=192
          dtparam=audio=on
          disable_overscan=1
          hdmi_drive=2
          disable_audio_dither=1
    
          # Can be shortened if desired.
          boot_delay=10
        '';
      };
    
      # This is necessary for the rpi3 as well.
      boot.kernelPackages = pkgs.linuxPackages_rpi4;
    
      # Makes graphics work.
      hardware.deviceTree = {
        base = pkgs.device-tree_rpi;
        overlays = [ "${pkgs.device-tree_rpi.overlays}/vc4-fkms-v3d.dtbo" ];
      };
    
      # Enable nonfree firmware (necessary for the rpi)
      hardware.enableRedistributableFirmware = true;
    
      # Log important messages to the system console.
      boot.consoleLogLevel = lib.mkDefault 7;
    
      # These are the filesystems defined on the SD image.
      fileSystems = {
        "/boot" = {
          device = "/dev/disk/by-label/FIRMWARE";
          fsType = "vfat";
          options = [ "nofail" ];
        };
        "/" = {
          device = "/dev/disk/by-label/NIXOS_SD";
          fsType = "ext4";
        };
      };
    
      # Prevent use of swapspace as much as possible
      boot.kernel.sysctl = { "vm.swappiness" = 0; };
    
      ###
      ### This is where all your config goes.
      ###
    
      # Customization for console font, language, time, etc...
      console = {
        font = "sun12x22";
        keyMap = "us";
      };
      i18n = {
        defaultLocale = "en_US.UTF-8";
      };
      time.timeZone = "America/Los_Angeles";
    
      # Graphics. Comment if you don't care.
      hardware.opengl = {
        enable = true;
        setLdLibraryPath = true;
        package = pkgs.mesa_drivers;
      };
      services.xserver = {
        enable = true;
        displayManager.lightdm.enable = true;
    
        # enlightenment is probably fine for the rpi4, but we'll use the old standby of XFCE for the rpi3
        desktopManager.xfce.enable = true;
    
        videoDrivers = [ "modesetting" ];
      };
    
      # Sound. Comment if you don't care.
      hardware.pulseaudio.enable = true;
      hardware.pulseaudio.support32Bit = true;
      sound.enable = true;
    
      # Hostname, host ID, etc.
      networking = {
        hostName = "rpi";
        firewall = {
          enable = true;
          allowedTCPPorts = [ 22 ];
        };
        wireless.enable = true;
      };
    
      security = {
        hideProcessInformation = true;
      };
    
      environment.systemPackages = with pkgs; [
        wget vimHugeX curl git htop zsh tmux psmisc pciutils manpages
        zip unzip
        usbutils alsaUtils
      ];
    
      # SSH config. Change passwordAuthentication if you want to log in with a password.
      services.openssh = {
        enable = true;
        passwordAuthentication = false;
        permitRootLogin = "prohibit-password";
        forwardX11 = true;
      };
    
      users = {
        users.myuser = {
          uid = 1000;
          isNormalUser = true;
          extraGroups = ["wheel" "audio" "tty"];
          shell = pkgs.zsh;
          openssh.authorizedKeys.keys = [ ];
        };
      };
    
      nixpkgs.config = {
        allowUnfree = true;
      };
    
      swapDevices = [ { device = "/swap"; size = 1024; } ];
    }
    
    1. 5
      # Boot the NixOS install image, and run, from the console:
      sudo -s
      passwd nixos
      systemctl start sshd
      

      The whole point of the author was that they didn’t want to have to connect a screen to their raspberrypi but have sshd enabled on first boot.

      1. 1

        I’m aware. Last time I tried this process (as the OP said), you needed to set up QEMU to compile things – that is, set up QEMU to run GCC inside QEMU – or use an aarch64 EC2 instance. Which just seems weird considering that cross compilers exist, and left me more than a little disappointed in the current ability of nixpkgs to natively cross compile binaries. Or am I missing something important?

        To me, just running the rebuild on the pi is good enough, and doesn’t require hacks like running an entire aarch64 cross compile inside an ARM emulator. When you have to pick the lesser evil, and all…

        [E] What’s especially confusing is that native cross compiling seems to exist, but all the guides I see end up emulating GCC with QEMU. I can’t tell if this is just bad documentation or if that’s really what you have to do.

        1. 1

          Im aware.

          Would’ve been nice to mention that then. Because right now it looks like your answer is a solution to the original problem, which it is not.

          I can’t tell if this is just bad documentation or if that’s really what you have to do.

          Just bad documentation. Have a look at https://github.com/nix-community/nixos-generators. The post has also been updated to reflect that.

          1. 1

            Would’ve been nice to mention that then

            I don’t really feel like editing my post when you say it like that regardless of how right you are, so go be pedantic elsewhere. I don’t know if you’re used to strong-arming commenters on other sites, but that’s not how lobsters works.

            Thanks for the link and clarification, though. Only being able to compile for your current architecture seemed counterintuitive given how nixpkgs is supposed to function.

            1. 2

              Sorry, I didn’t want to sound pedantic or condescending. I just thought it would’ve been nice to give a heads up for readers looking for an answer to the problem in the article, because they might get a wrong impression and get stuck trying to get your approach to work without connecting a screen and keyboard.

              1. 1

                No worries. I’d clarify, but it seems like it doesn’t allow edits at this point. :(

    2. 3

      You can use https://github.com/nix-community/nixos-generators to create a bootable aarch64 image cross compiled from x86 out of the box.

      1. 2

        Just replied via e-mail to you, but for anyone else that’s reading: I have added a section which includes nixos-generators as it’s an amazing tool. I didn’t update the Vagrant section mostly because it’s just a log of stuff that I have done to get it working, but I have added a note.

        Let me know if you have any other suggestions!

        1. 1

          Generators were one of the interesting things I learned from the Super Bootable 64 post a few days ago. Super convenient tool. Thanks for the reminder.

        2. 1

          I think that using Packer instead of Terraform would be better choice there as this tool is built for such purposes.

          1. 3

            Your wish is my command!

            https://github.com/Robertof/nixos-docker-sd-image-builder/tree/master/packer

            Thanks again for the tip, it definitely made more sense. Unfortunately the behavior of Packer to create an AMI is engraved into it, and it can’t be disabled, so I had to resort to an hacky solution to not make it do it.

            In the end, it’s still better and cleaner than the Terraform solution.

            Cheers!

            1. 2

              Hey, I am the story author. Thanks for the tip!

              I am definitely not a power user of Terraform and Packer, so I guess it has shown. However, I did look at Packer originally and I thought that its purpose was more for building base images (or AMIs) to use to spawn other instances/VMs, rather than to just run an instance to produce an artifact.

              For example, the amazon AMI builder explicitly mentions that its purpose is to generate AMI images. I’d greatly appreciate if you could give me some insight, I’ll dig deeper in the documentation meanwhile.

              Thanks!

              EDIT: just saw https://www.packer.io/docs/post-processors/artifice/, going to play with it now!