Add NixOS configuration for ringtail workstation (#207)
## Summary - NixOS flake for ringtail (gaming/compute workstation, RTX 4080) in `nixos/ringtail/` - Declarative disk partitioning via disko (GPT, 512M EFI + ext4 root on NVMe) - NVIDIA proprietary drivers, sway/Wayland desktop, greetd, PipeWire, Steam - Tailscale integration for tailnet connectivity - Ansible playbook + `mise run provision-ringtail` for ongoing management - Pulumi auth key (`tag:homelab`, `tag:blumeops`) for tailnet bootstrap ## Deployment Order 1. **Merge PR** 2. `pulumi up` in tailscale stack → creates auth key 3. Retrieve auth key: `pulumi stack output ringtail_authkey --show-secrets` 4. On ringtail NixOS installer: - `nix run github:nix-community/disko -- --mode disko /tmp/disk-config.nix` (or from cloned repo) - `nixos-install --flake github:eblume/blumeops?dir=nixos/ringtail#ringtail` 5. Reboot, `tailscale up --auth-key=<key>` 6. Verify: `tailscale status`, SSH from gilbert ## Test plan - [ ] Review NixOS configuration for completeness - [ ] Verify disko partition layout matches ringtail hardware - [ ] Run `pulumi preview` for tailscale stack - [ ] Install NixOS on ringtail - [ ] Confirm tailscale connectivity - [ ] Confirm sway desktop works - [ ] Test `mise run provision-ringtail` for ongoing management 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/207
This commit is contained in:
parent
5f9b024b4a
commit
b9d813cde1
9 changed files with 281 additions and 1 deletions
|
|
@ -5,6 +5,9 @@ all:
|
||||||
hosts:
|
hosts:
|
||||||
indri:
|
indri:
|
||||||
ansible_host: indri
|
ansible_host: indri
|
||||||
|
ringtail:
|
||||||
|
ansible_host: ringtail
|
||||||
|
ansible_user: eblume
|
||||||
workstations:
|
workstations:
|
||||||
hosts:
|
hosts:
|
||||||
gilbert:
|
gilbert:
|
||||||
|
|
|
||||||
25
ansible/playbooks/ringtail.yml
Normal file
25
ansible/playbooks/ringtail.yml
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
- name: Configure ringtail (NixOS)
|
||||||
|
hosts: ringtail
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Ensure blumeops repo is present
|
||||||
|
ansible.builtin.git:
|
||||||
|
repo: "https://forge.ops.eblu.me/eblume/blumeops.git"
|
||||||
|
dest: /etc/blumeops
|
||||||
|
version: main
|
||||||
|
register: _repo
|
||||||
|
|
||||||
|
- name: Rebuild NixOS
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: nixos-rebuild switch --flake /etc/blumeops/nixos/ringtail#ringtail
|
||||||
|
register: _rebuild
|
||||||
|
changed_when: "'activating the configuration' in _rebuild.stdout"
|
||||||
|
when: _repo.changed
|
||||||
|
|
||||||
|
- name: Verify tailscale is connected
|
||||||
|
ansible.builtin.command: tailscale status --self --json
|
||||||
|
register: _ts_status
|
||||||
|
changed_when: false
|
||||||
|
failed_when: "'Running' not in _ts_status.stdout"
|
||||||
1
docs/changelog.d/feature-ringtail-nixos.feature.md
Normal file
1
docs/changelog.d/feature-ringtail-nixos.feature.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Add NixOS configuration for ringtail (gaming/compute workstation with RTX 4080). Includes declarative disk partitioning via disko, NVIDIA drivers, sway/Wayland desktop, Steam, Tailscale, and Ansible-driven provisioning.
|
||||||
9
mise-tasks/provision-ringtail
Executable file
9
mise-tasks/provision-ringtail
Executable file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#MISE description="Run ansible playbook to provision ringtail (NixOS)"
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
export MISE_TASK_OUTPUT=interleave
|
||||||
|
|
||||||
|
cd ansible
|
||||||
|
ansible-playbook playbooks/ringtail.yml "$@"
|
||||||
104
nixos/ringtail/configuration.nix
Normal file
104
nixos/ringtail/configuration.nix
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
# Bootloader
|
||||||
|
boot.loader.systemd-boot.enable = true;
|
||||||
|
boot.loader.efi.canTouchEfiVariables = true;
|
||||||
|
|
||||||
|
# Networking
|
||||||
|
networking.hostName = "ringtail";
|
||||||
|
networking.networkmanager.enable = true;
|
||||||
|
|
||||||
|
# Time zone
|
||||||
|
time.timeZone = "America/Los_Angeles";
|
||||||
|
|
||||||
|
# Locale
|
||||||
|
i18n.defaultLocale = "en_US.UTF-8";
|
||||||
|
|
||||||
|
# NVIDIA proprietary drivers
|
||||||
|
hardware.graphics.enable = true;
|
||||||
|
services.xserver.videoDrivers = [ "nvidia" ];
|
||||||
|
hardware.nvidia = {
|
||||||
|
modesetting.enable = true;
|
||||||
|
open = false; # Use proprietary driver for RTX 4080
|
||||||
|
nvidiaSettings = true;
|
||||||
|
package = config.boot.kernelPackages.nvidiaPackages.stable;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Wayland / Sway
|
||||||
|
programs.sway = {
|
||||||
|
enable = true;
|
||||||
|
wrapperFeatures.gtk = true;
|
||||||
|
extraPackages = with pkgs; [
|
||||||
|
swaylock
|
||||||
|
swayidle
|
||||||
|
wezterm # terminal
|
||||||
|
wmenu # app launcher
|
||||||
|
mako # notifications
|
||||||
|
grim # screenshots
|
||||||
|
slurp # region selection
|
||||||
|
];
|
||||||
|
};
|
||||||
|
security.polkit.enable = true;
|
||||||
|
|
||||||
|
# Enable greetd as display manager for sway
|
||||||
|
services.greetd = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
default_session = {
|
||||||
|
command = "${pkgs.greetd.tuigreet}/bin/tuigreet --time --cmd sway";
|
||||||
|
user = "greeter";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# PipeWire for audio
|
||||||
|
services.pipewire = {
|
||||||
|
enable = true;
|
||||||
|
alsa.enable = true;
|
||||||
|
pulse.enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Steam
|
||||||
|
programs.steam = {
|
||||||
|
enable = true;
|
||||||
|
dedicatedServer.openFirewall = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Tailscale
|
||||||
|
services.tailscale.enable = true;
|
||||||
|
|
||||||
|
# SSH
|
||||||
|
services.openssh = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
PasswordAuthentication = false;
|
||||||
|
PermitRootLogin = "no";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# User account
|
||||||
|
users.users.eblume = {
|
||||||
|
isNormalUser = true;
|
||||||
|
initialPassword = "changeme";
|
||||||
|
extraGroups = [ "wheel" "networkmanager" "video" ];
|
||||||
|
openssh.authorizedKeys.keys = [
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILmh1SSCdDAyu3vkSQH7kAXEPDi8APyjo9JXDTjtha2j"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# System packages
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
git
|
||||||
|
vim
|
||||||
|
htop
|
||||||
|
curl
|
||||||
|
wget
|
||||||
|
];
|
||||||
|
|
||||||
|
# Enable nix flakes
|
||||||
|
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||||
|
|
||||||
|
# NixOS release
|
||||||
|
system.stateVersion = "25.11";
|
||||||
|
}
|
||||||
84
nixos/ringtail/disk-config.nix
Normal file
84
nixos/ringtail/disk-config.nix
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
{
|
||||||
|
disko.devices = {
|
||||||
|
disk = {
|
||||||
|
nvme = {
|
||||||
|
type = "disk";
|
||||||
|
device = "/dev/nvme0n1";
|
||||||
|
content = {
|
||||||
|
type = "gpt";
|
||||||
|
partitions = {
|
||||||
|
ESP = {
|
||||||
|
size = "512M";
|
||||||
|
type = "EF00";
|
||||||
|
content = {
|
||||||
|
type = "filesystem";
|
||||||
|
format = "vfat";
|
||||||
|
mountpoint = "/boot";
|
||||||
|
mountOptions = [ "umask=0077" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
root = {
|
||||||
|
size = "100%";
|
||||||
|
content = {
|
||||||
|
type = "filesystem";
|
||||||
|
format = "ext4";
|
||||||
|
mountpoint = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
games = {
|
||||||
|
type = "disk";
|
||||||
|
device = "/dev/sda";
|
||||||
|
content = {
|
||||||
|
type = "gpt";
|
||||||
|
partitions = {
|
||||||
|
games = {
|
||||||
|
size = "100%";
|
||||||
|
content = {
|
||||||
|
type = "filesystem";
|
||||||
|
format = "ext4";
|
||||||
|
mountpoint = "/mnt/games";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
storage1 = {
|
||||||
|
type = "disk";
|
||||||
|
device = "/dev/sdb";
|
||||||
|
content = {
|
||||||
|
type = "gpt";
|
||||||
|
partitions = {
|
||||||
|
storage1 = {
|
||||||
|
size = "100%";
|
||||||
|
content = {
|
||||||
|
type = "filesystem";
|
||||||
|
format = "ext4";
|
||||||
|
mountpoint = "/mnt/storage1";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
storage2 = {
|
||||||
|
type = "disk";
|
||||||
|
device = "/dev/sdc";
|
||||||
|
content = {
|
||||||
|
type = "gpt";
|
||||||
|
partitions = {
|
||||||
|
storage2 = {
|
||||||
|
size = "100%";
|
||||||
|
content = {
|
||||||
|
type = "filesystem";
|
||||||
|
format = "ext4";
|
||||||
|
mountpoint = "/mnt/storage2";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
23
nixos/ringtail/flake.nix
Normal file
23
nixos/ringtail/flake.nix
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
description = "NixOS configuration for ringtail (gaming/compute workstation)";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
|
||||||
|
disko = {
|
||||||
|
url = "github:nix-community/disko";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { nixpkgs, disko, ... }: {
|
||||||
|
nixosConfigurations.ringtail = nixpkgs.lib.nixosSystem {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
modules = [
|
||||||
|
disko.nixosModules.disko
|
||||||
|
./disk-config.nix
|
||||||
|
./hardware-configuration.nix
|
||||||
|
./configuration.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
18
nixos/ringtail/hardware-configuration.nix
Normal file
18
nixos/ringtail/hardware-configuration.nix
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Do not modify this file! It was generated by 'nixos-generate-config'
|
||||||
|
# and may be overwritten by future invocations. Please make changes
|
||||||
|
# to configuration.nix instead.
|
||||||
|
{ config, lib, pkgs, modulesPath, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports =
|
||||||
|
[ (modulesPath + "/installer/scan/not-detected.nix")
|
||||||
|
];
|
||||||
|
|
||||||
|
boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" ];
|
||||||
|
boot.initrd.kernelModules = [ ];
|
||||||
|
boot.kernelModules = [ "kvm-amd" ];
|
||||||
|
boot.extraModulePackages = [ ];
|
||||||
|
|
||||||
|
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||||
|
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ This program manages:
|
||||||
- Device tags for infrastructure classification
|
- Device tags for infrastructure classification
|
||||||
|
|
||||||
Devices are tagged based on their role:
|
Devices are tagged based on their role:
|
||||||
- tag:homelab - Server infrastructure (indri)
|
- tag:homelab - Server infrastructure (indri, ringtail)
|
||||||
- tag:workstation - Development machines that can manage homelab (gilbert)
|
- tag:workstation - Development machines that can manage homelab (gilbert)
|
||||||
- tag:nas - Network-attached storage (sifaka)
|
- tag:nas - Network-attached storage (sifaka)
|
||||||
- tag:blumeops - Resources managed by this IaC
|
- tag:blumeops - Resources managed by this IaC
|
||||||
|
|
@ -82,10 +82,23 @@ flyio_key = tailscale.TailnetKey(
|
||||||
expiry=7776000, # 90 days
|
expiry=7776000, # 90 days
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Auth key for ringtail (gaming/compute workstation, NixOS)
|
||||||
|
# Used during bootstrap: `tailscale up --auth-key=<key>`
|
||||||
|
# Once ringtail is on the tailnet, add DeviceTags resource for ongoing management.
|
||||||
|
ringtail_key = tailscale.TailnetKey(
|
||||||
|
"ringtail-key",
|
||||||
|
reusable=False,
|
||||||
|
ephemeral=False,
|
||||||
|
preauthorized=True,
|
||||||
|
tags=["tag:homelab", "tag:blumeops"],
|
||||||
|
expiry=86400, # 24 hours - single use for bootstrap
|
||||||
|
)
|
||||||
|
|
||||||
# ============== Exports ==============
|
# ============== Exports ==============
|
||||||
pulumi.export("acl_id", acl.id)
|
pulumi.export("acl_id", acl.id)
|
||||||
pulumi.export("policy_hash", policy_hash)
|
pulumi.export("policy_hash", policy_hash)
|
||||||
pulumi.export("flyio_authkey", flyio_key.key)
|
pulumi.export("flyio_authkey", flyio_key.key)
|
||||||
|
pulumi.export("ringtail_authkey", ringtail_key.key)
|
||||||
|
|
||||||
pulumi.export("indri_device_id", indri.node_id)
|
pulumi.export("indri_device_id", indri.node_id)
|
||||||
pulumi.export("indri_tags", indri_tags.tags)
|
pulumi.export("indri_tags", indri_tags.tags)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue