Implement dual improvements to enable efficient testing of pipeline changes without polluting master branch: 1. Add --force flag to create-host script - Skip hostname/IP uniqueness validation - Overwrite existing host configurations - Update entries in flake.nix and terraform/vms.tf (no duplicates) - Useful for iterating on configurations during testing 2. Add branch support to bootstrap mechanism - Bootstrap service reads NIXOS_FLAKE_BRANCH environment variable - Defaults to master if not set - Uses branch in git URL via ?ref= parameter - Service loads environment from /etc/environment 3. Add cloud-init disk support for branch configuration - VMs can specify flake_branch field in terraform/vms.tf - Automatically generates cloud-init snippet setting NIXOS_FLAKE_BRANCH - Uploads snippet to Proxmox via SSH - Production VMs omit flake_branch and use master 4. Update documentation - Document --force flag usage in create-host README - Add branch testing examples in terraform README - Update TODO.md with testing workflow - Add .generated/ to gitignore Testing workflow: Create feature branch, set flake_branch in VM definition, deploy with terraform, iterate with --force flag, clean up before merging. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
6.9 KiB
NixOS Host Configuration Generator
Automated tool for generating NixOS host configurations, flake.nix entries, and Terraform VM definitions for homelab infrastructure.
Installation
The tool is available in the Nix development shell:
nix develop
Usage
Basic Usage
Create a new host with DHCP networking:
python -m scripts.create_host.create_host create --hostname test01
Create a new host with static IP:
python -m scripts.create_host.create_host create \
--hostname test01 \
--ip 10.69.13.50/24
Create a host with custom resources:
python -m scripts.create_host.create_host create \
--hostname bighost01 \
--ip 10.69.13.51/24 \
--cpu 8 \
--memory 8192 \
--disk 100G
Dry Run Mode
Preview what would be created without making changes:
python -m scripts.create_host.create_host create \
--hostname test01 \
--ip 10.69.13.50/24 \
--dry-run
Force Mode (Regenerate Existing Configuration)
Overwrite an existing host configuration (useful for testing):
python -m scripts.create_host.create_host create \
--hostname test01 \
--ip 10.69.13.50/24 \
--force
This mode:
- Skips hostname and IP uniqueness validation
- Overwrites files in
hosts/<hostname>/ - Updates existing entries in
flake.nixandterraform/vms.tf(doesn't duplicate) - Useful for iterating on configuration templates during testing
Options
-
--hostname(required): Hostname for the new host- Must be lowercase alphanumeric with hyphens
- Must be unique (not already exist in repository)
-
--ip(optional): Static IP address with CIDR notation- Format:
10.69.13.X/24 - Must be in
10.69.13.0/24subnet - Last octet must be 1-254
- Omit this option for DHCP configuration
- Format:
-
--cpu(optional, default: 2): Number of CPU cores- Must be at least 1
-
--memory(optional, default: 2048): Memory in MB- Must be at least 512
-
--disk(optional, default: "20G"): Disk size- Examples: "20G", "50G", "100G"
-
--dry-run(flag): Preview changes without creating files -
--force(flag): Overwrite existing host configuration- Skips uniqueness validation
- Updates existing entries instead of creating duplicates
What It Does
The tool performs the following actions:
-
Validates the configuration:
- Hostname format (RFC 1123 compliance)
- Hostname uniqueness
- IP address format and subnet (if provided)
- IP address uniqueness (if provided)
-
Generates host configuration files:
hosts/<hostname>/default.nix- Import wrapperhosts/<hostname>/configuration.nix- Full host configuration
-
Updates repository files:
flake.nix- Adds new nixosConfigurations entryterraform/vms.tf- Adds new VM definition
-
Displays next steps for:
- Reviewing changes with git diff
- Verifying NixOS configuration
- Verifying Terraform configuration
- Committing changes
- Deploying the VM
Generated Configuration
Host Features
All generated hosts include:
-
Full system imports from
../../system:- Nix binary cache integration
- SSH with root login
- SOPS secrets management
- Internal ACME CA integration
- Daily auto-upgrades with auto-reboot
- Prometheus node-exporter
- Promtail logging to monitoring01
-
VM guest agent from
../../common/vm -
Hardware configuration from
../template/hardware-configuration.nix
Networking
Static IP mode (when --ip is provided):
systemd.network.networks."ens18" = {
matchConfig.Name = "ens18";
address = [ "10.69.13.50/24" ];
routes = [ { Gateway = "10.69.13.1"; } ];
linkConfig.RequiredForOnline = "routable";
};
DHCP mode (when --ip is omitted):
systemd.network.networks."ens18" = {
matchConfig.Name = "ens18";
networkConfig.DHCP = "ipv4";
linkConfig.RequiredForOnline = "routable";
};
DNS Configuration
All hosts are configured with:
- DNS servers:
10.69.13.5,10.69.13.6(ns1, ns2) - Domain:
home.2rjus.net
Examples
Create a test VM with defaults
python -m scripts.create_host.create_host create --hostname test99
This creates a DHCP VM with 2 CPU cores, 2048 MB memory, and 20G disk.
Create a database server with static IP
python -m scripts.create_host.create_host create \
--hostname pgdb2 \
--ip 10.69.13.52/24 \
--cpu 4 \
--memory 4096 \
--disk 50G
Preview changes before creating
python -m scripts.create_host.create_host create \
--hostname test99 \
--ip 10.69.13.99/24 \
--dry-run
Error Handling
The tool validates input and provides clear error messages for:
- Invalid hostname format (must be lowercase alphanumeric with hyphens)
- Duplicate hostname (already exists in repository)
- Invalid IP format (must be X.X.X.X/24)
- Wrong subnet (must be 10.69.13.0/24)
- Invalid last octet (must be 1-254)
- Duplicate IP address (already in use)
- Resource constraints (CPU < 1, memory < 512 MB)
Integration with Deployment Pipeline
This tool implements Phase 2 of the automated deployment pipeline:
- Phase 1: Template building ✓ (build-and-deploy-template.yml)
- Phase 2: Host configuration generation ✓ (this tool)
- Phase 3: Bootstrap automation (planned)
- Phase 4: Secrets management (planned)
- Phase 5: DNS automation (planned)
- Phase 6: Full integration (planned)
Development
Project Structure
scripts/create-host/
├── create_host.py # Main CLI entry point (typer app)
├── __init__.py # Package initialization
├── validators.py # Validation logic
├── generators.py # File generation using Jinja2
├── manipulators.py # Text manipulation for flake.nix and vms.tf
├── models.py # Data models (HostConfig)
├── templates/
│ ├── default.nix.j2 # Template for default.nix
│ └── configuration.nix.j2 # Template for configuration.nix
└── README.md # This file
Testing
Run the test cases from the implementation plan:
# Test 1: DHCP host with defaults
python -m scripts.create_host.create_host create --hostname testdhcp --dry-run
# Test 2: Static IP host
python -m scripts.create_host.create_host create \
--hostname test50 --ip 10.69.13.50/24 --dry-run
# Test 3: Custom resources
python -m scripts.create_host.create_host create \
--hostname test51 --ip 10.69.13.51/24 \
--cpu 8 --memory 8192 --disk 100G --dry-run
# Test 4: Duplicate hostname (should error)
python -m scripts.create_host.create_host create --hostname ns1 --dry-run
# Test 5: Invalid subnet (should error)
python -m scripts.create_host.create_host create \
--hostname testbad --ip 192.168.1.50/24 --dry-run
# Test 6: Invalid hostname (should error)
python -m scripts.create_host.create_host create --hostname Test_Host --dry-run
License
Part of the nixos-servers homelab infrastructure repository.