Skip to content

Instantly share code, notes, and snippets.

@corpix
Last active August 11, 2019 12:31
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save corpix/72d5a2d3a557f56ca8f76f5f4af7ba2e to your computer and use it in GitHub Desktop.
Save corpix/72d5a2d3a557f56ca8f76f5f4af7ba2e to your computer and use it in GitHub Desktop.
Scripts to deploy NixOS machines without nixops/nix-deploy/etc...

Requirements:

  • root directory is a git repository(for git rev-parse --show-toplevel)
  • github.com/nixos/nixpkgs at pkgs/nixpkgs

Tree:

.
├── .git
├── deploy
├── env
├── realise
├── machines
│   └── localhost
│       └── default.nix
└── pkgs
    └── nixpkgs
        ├── ...
        └── .git

$ cat deploy

#! /usr/bin/env bash
set -e

root="$(git rev-parse --show-toplevel)"
source "$root"/env

if [ -z "$1" ] || [ -z "$2" ]
then
    echo
    echo "Usage: $0 <configuration> <machine>"
    echo
    echo "    <configuration> Relative path to machine directory"
    echo "                    or nix configuration which exports system"
    echo
    echo "    <machine>       Machine expression which will be passed to SSH"
    echo
    exit 1
fi

configuration="$1"
shift

machine="$1"
shift

derivation="$($root/realise "$configuration")"

[ ! -z "$derivation" ] || {
    echo "No derivation built, aborting"
    exit 1
} 1>&2

nix-copy-closure --gzip --to "$machine" "$derivation"
ssh "$machine" '
    set -e
    sudo nix-env --profile /nix/var/nix/profiles/system --set '"$derivation"'
    sudo /nix/var/nix/profiles/system/bin/switch-to-configuration switch
'

$ cat env

#! /usr/bin/env bash
root="${root:-$(git rev-parse --show-toplevel)}"

export NIX_PATH="${root}:nixpkgs=${root}/pkgs/nixpkgs"
export NIX_BUILD_CORES=4

# Docker needs locale
# Otherwise some python apps may break 
export LANG=en_US.UTF-8
export LANGUAGE=en_US:en
export LC_ALL=en_US.UTF-8

$ cat realise

#! /usr/bin/env bash
set -e

root="$(git rev-parse --show-toplevel)"
source "$root"/env

[ ! -z "$1" ] || {
    echo
    echo "Usage: $0 <configuration> [configuration] ..."
    echo
    echo "    <configuration> Relative path to machine directory"
    echo "                    or nix configuration which exports system"
    echo
    exit 1
}

while [ ! -z "$1" ]
do
    configuration="$1"
    shift

    echo "$configuration" 1>&2

    instance=$(nix-instantiate "$configuration")

    [ ! -z "$instance" ] || {
        echo "Failed to instantiate $configuration"
        exit 1
    } 1>&2

    nix-store --realise $instance
done

$ cat machines/localhost/default.nix

(import <nixpkgs/nixos> {
  system = "x86_64-linux";
  configuration = { ... }: {
    imports = [
      ./hardware.nix
    ];
    # ... machine configuration ...
  };
}).system

$ ./deploy machines/localhost localhost


Bonus: shell in docker container to deploy from machines without nix installed.

Pay attention that it use nixos/nix:2.0 container

$ cat shell

#! /usr/bin/env bash
set -e

root="$(git rev-parse --show-toplevel)"
source "$root"/env

[ -z "$SSH_AUTH_SOCK" ] || {
    docker_run_args+=" -v $SSH_AUTH_SOCK:/ssh-agent -e SSH_AUTH_SOCK=/ssh-agent "
}

[ ! -d "$HOME/.ssh" ] || {
    ensure_ssh_script='mkdir -p ~/.ssh && chmod 700 ~/.ssh && cp /root/ssh/* /root/.ssh && chmod 600 /root/.ssh/*;'
}

[ ! -e "$HOME/.ssh/config" ] || {
    docker_run_args+=" -v $HOME/.ssh/config:/root/ssh/config:ro "
}

[ ! -e "$HOME/.ssh/known_hosts" ] || {
    docker_run_args+=" -v $HOME/.ssh/known_hosts:/root/ssh/known_hosts:ro "
}

[ "$#" -eq 0 ] || {
    command="$(printf "%q " "$@")"
}

set -x
sudo docker run --rm -it                              \
     -v nix:/nix:rw                                   \
     -v "$root":/deploy                               \
     -e NIX_PATH=/deploy:nixpkgs=/deploy/pkgs/nixpkgs \
     -e NIX_BUILD_CORES=$NIX_BUILD_CORES              \
     -w /deploy                                       \
     $docker_run_args nixos/nix:2.0                   \
     nix-shell --command "$ensure_ssh_script ${command:-exec bash}"

$ cat shell.nix

with import <nixpkgs> {};
stdenv.mkDerivation {
  name = "deploy-shell";
  buildInputs = [
    git
    bashInteractive
    openssh
    nano
    mc
    jq
    glibcLocales
    rsync
  ];
  shellHook = ''
    source ./env
  '';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment