Your setup machine needs the following:
Nix on your Setup Machine (unless you're using NixOS)
An id_ed25519 keypair on your Setup Machine. (Link coming soon.)
Git (Optional). Clan uses Git internally, but you can optionally install it to make your own use of it. See the Git installation instructions.
The steps in this document will erase all data on your Google Cloud server's hard drive.
If you already have a server on Google Cloud running, you can skip this step.
We recommend using gcloud command-line tool.
Launch a server:
gcloud compute instances create linux-server-01 \
--machine-type=e2-medium \
--image-family=ubuntu-2204-lts \
--image-project=ubuntu-os-cloud \
--boot-disk-size=20GB \
--metadata="ssh-keys=$(whoami):$(cat ~/.ssh/id_ed25519.pub)" \
--no-shielded-secure-boot \
--no-shielded-vtpm \
--no-shielded-integrity-monitoring (Notice the whoami: We're creating a user on the remote machine with the same name as your local username.)
After this command runs, you can find the IP address from the command's output, under EXTERNAL_IP:
NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS
linux-server-01 us-central1-a e2-medium 10.128.0.4 34.170.5.83 RUNNING Verify that you can log in:
ssh <USERNAME>@<IP-ADDRESS> replacing <USERNAME> with your local username and <IP-ADDRESS> with the IP address displayed after the server was provisioned. (Note, the server sometimes takes a moment to boot up, so you might need to try this command a couple of times before it lets you in.)
Next, enable root access. First, copy the authorized_keys file:
gcloud compute ssh linux-server-01 --command="sudo mkdir -p /root/.ssh && sudo cp ~/.ssh/authorized_keys /root/.ssh/authorized_keys" Second, enable root login:
gcloud compute ssh linux-server-01 --command="sudo sed -i 's/PermitRootLogin no/PermitRootLogin prohibit-password/g' /etc/ssh/sshd_config" Third, restart the ssh daemon:
gcloud compute ssh linux-server-01 --command="sudo systemctl restart ssh" Now test out root access:
ssh root@<IP-ADDRESS> replacing <IP-ADDRESS> with the ip address provided earlier.
Then exit:
exit Start by creating a new clan:
nix run "https://git.clan.lol/clan/clan-core/archive/main.tar.gz#clan-cli" --refresh -- init and enter a name for it, e.g. MY-CLAN-1, followed by a domain, e.g. myclan1.lol. (This does not have to be an actual registered domain.)
The first time you run this, Clan will automatically create an age key at ~/.config/sops/age/keys.txt. This key encrypts your secrets - back it up somewhere safe, and then type "y".
If you've run this before, you'll also be asked to select admin keys; you'll most likely want to type "1" and press enter.
Change to the new folder:
cd MY-CLAN-1 You will see a message about direnv needing approval to run. Type:
direnv allow Next, create a machine configuration, which adds a description of a machine to your inventory. For this example, call it test-machine, by typing:
clan machines create test-machine Open clan.nix, and find the inventory.machines line; add the following immediately after it; replace the IP address with your Hetzner server's IP address:
inventory.machines = { # FIND THIS LINE, ADD THE FOLLOWING
test-machine = {
deploy.targetHost = "root@<IP-ADDRESS>"; # REPLACE WITH YOUR MACHINE'S IP ADDRESS; keep "root@"
tags = [ ];
}; Test it out:
clan machines list Next, add your public key to the allowed keys. You can find it by running:
cat ~/.ssh/id_ed25519.pub Open clan.nix, and replace PASTE_YOUR_KEY_HERE with the contents of the id_ed25519.pub file:
"admin-machine-1" = "PASTE_YOUR_KEY_HERE"; Verify that your configuration is valid:
clan show Now gather the hardware configuration from the target machine:
clan machines init-hardware-config test-machine You will be asked to enter "y" to proceed.
Next, configure a disk for the target machine. You'll run this command in two steps; first, type it like so:
clan templates apply disk ext4-single-disk test-machine --set mainDisk "" This will generate an error; note the disk ID it prints out (typically /dev/disk/by-id/scsi-0Google_PersistentDisk_persistent-disk-0), and add it inside the quotes, e.g.:
clan templates apply disk ext4-single-disk test-machine --set mainDisk "/dev/disk/by-id/scsi-0Google_PersistentDisk_persistent-disk-0" Next we need to deal with a situation specific to Google Cloud. Google Cloud does not expose the partition tables to the guest operating systsem. As such, we need to make some adjustments to the default configuration.nix and disko.nix files.
Switch to the directory holding these files:
cd machines/test-machine Open configuration.nix and replace its contents with:
{ lib, modulesPath, ... }:
{
imports = [
(modulesPath + "/virtualisation/google-compute-image.nix")
];
networking.hostName = lib.mkForce "test-machine";
security.googleOsLogin.enable = lib.mkForce false;
} The GCP module (google-compute-image.nix) provides essential drivers and services for running NixOS on Google Cloud, but it also enables Google OS Login by default, which is a GCP-specific SSH key management system that conflicts with clan's sshd service. We disable it with security.googleOsLogin.enable = lib.mkForce false so that clan's authorized_keys configuration works properly. The lib.mkForce is needed
because we're overriding values that the GCP module already sets.
Next, open 'disko.nix" and add the highlighted lines:
# ---
# schema = "ext4-single-disk"
# [placeholders]
# mainDisk = "/dev/disk/by-id/scsi-0Google_PersistentDisk_persistent-disk-0"
# ---
# This file was automatically generated!
# CHANGING this configuration requires wiping and reinstalling the machine
{ lib, ... }: # ADD THIS LINE
{
boot.loader.grub = {
efiInstallAsRemovable = true;
efiSupport = true;
};
boot.loader.timeout = lib.mkForce 0; # ADD THIS LINE
fileSystems."/".device = lib.mkForce "/dev/disk/by-label/nixos"; # ADD THIS LINE
disko.devices = {
disk = {
main = {
name = "main-49086db16eb74c23bed59fc2045fd513";
device = "/dev/disk/by-id/scsi-0Google_PersistentDisk_persistent-disk-0";
type = "disk";
content = {
type = "gpt";
partitions = {
"boot" = {
size = "1M";
type = "EF02"; # for grub MBR
priority = 1;
};
ESP = {
type = "EF00";
size = "500M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
extraArgs = [ "-n" "ESP" ]; # ADD THIS LINE
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
extraArgs = [ "-L" "nixos" ]; # ADD THIS LINE
};
};
};
};
};
};
};
} Then return to the root of the clan:
cd ../.. Install NixOS on the target machine by typing:
clan machines install test-machine You will be asked whether you want to install — type y. You will also be prompted for a password; you can accept the defaults and press Enter.
You will then be asked for a password to assign to the root login for the machine. You can either create one, or let Clan assign a random one.
If you get an error regarding sandboxing not being available, type the following to disable sandboxing, and then run the above command again:
clan vars generate test-machine --no-sandbox Now you can try connecting to the remote machine:
clan ssh test-machine You'll quite likely get an error at first regarding the host identification. It should include a line to type to remove the old ID; paste the line you're shown, which will look similar to this:
ssh-keygen -f '/home/user/.ssh/known_hosts' -R '<IP-ADDRESS>' Then try again:
clan ssh test-machine You should connect and see the prompt:
[root@test-machine:~]# Now let's look at how you can use Clan to install and remove packages on a target machine.
For this demonstration we'll add three command-line packages: bat, btop, and tldr. In clan.nix, under inventory.instances, add the following lines:
inventory.instances = {
packages = {
roles.default.machines."test-machine".settings = {
packages = [ "bat" "btop" "tldr" ];
};
};
# ... existing wifi service ...
}; This declares that the three packages will be present on the machine. To install them, type:
clan machines update test-machine Now ssh into the machine, and they should be present:
which bat
which btop
which tldr Each will show a path to the binary file:
/run/current-system/sw/bin/bat
/run/current-system/sw/bin/btop
/run/current-system/sw/bin/tldr Next, let's remove one of the three packages. The packages portion of clan.nix declares what additional packages should exist; by removing one, Nix will remove that package. Remove the "tldr" from the list:
packages = [ "bat" "btop" ]; and run the update again:
clan machines update test-machine Now when you check which tldr, it should show that it's not in the path:
which tldr
which: no tldr in (/run/wrappers/bin:/root/.nix-profile/bin:/nix/profile/bin:/root/.local/state/nix/profile/bin:/etc/profiles/per-user/root/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin)
When you need to add a new user, you can do so right from within the clan.nix file, and then update the system.
Let's add a user called Alice. Open clan.nix, and under inventory.instances, add the following:
inventory.instances = { # Add the following under this line
user-alice = {
module.name = "users";
roles.default.machines."test-machine" = {};
roles.default.tags.all = {};
roles.default.settings = {
user = "alice";
};
}; Save the file. Now type the following to add a password for alice (include the no-sandbox if you needed no sandbox earlier):
clan vars generate test-machine --no-sandbox You will be prompted for a password. Or you can press Enter to automatically generate one.
If you automatically generated one, to retrieve it type:
clan vars get test-machine user-password-alice/user-password On cloud machines, this password will be used for sudo access if you grant it. Typically password login is disabled on a cloud machine.
Next, let's add a key file so Alice can log in remotely. For this we'll use your own key file as before. Type:
cat ~/.ssh/id_ed25519.pub Then open machines/test-machine/configuration.nix. Add the following, before the closing brace:
{
imports = [
];
# New machine!
users.users.alice.openssh.authorizedKeys.keys = [
"PASTE_YOUR_KEY_HERE"
];
} and replace PASTE_YOUR_KEY_HERE with the contents of the file.
Now update the machine by typing:
clan machines update test-machine Once complete, you can log in as alice:
ssh alice@<IP-ADDRESS> replacing <IP-ADDRESS> with the Hetzner server's IP address.
After you trust Alice, you can grant her sudo access. To do so, update the clan.nix file by adding her to the wheel group:
user-alice = {
module.name = "users";
roles.default.machines."test-machine" = {};
roles.default.tags.all = {};
roles.default.settings = {
user = "alice";
groups = [ "wheel" ]; # Add this to allow sudo
};
}; Again type:
clan machines update test-machine If you were already logged in as alice before running the update, you will need to log out and back in for the change to take.
Then after logged in as alice, try using sudo:
sudo echo "hello" You will be prompted for the password and should see "hello" printed.
To revoke alice's sudo access, simply remove the line you added:
groups = [ "wheel" ];
And once again run:
clan machines update test-machine Log out, and log alice back in. Now try the same sudo command; you'll be prompted for password, but then shown:
alice is not in the sudoers file.