Imperative Configuration
Idempotent configuration scripts for systems not managed by NixOS.
Overview
This directory contains configuration scripts for hosts that cannot or do not use NixOS. Unlike the declarative NixOS configurations in /systems, these scripts use an imperative approach but are designed to be idempotent - safe to run multiple times.
Philosophy
While NixOS provides declarative configuration management, some systems need to use traditional Linux distributions for various reasons:
- Hardware compatibility requirements
- Organizational constraints
- Testing different distributions
- Transition periods before migrating to NixOS
These scripts bridge the gap by providing repeatable, documented configuration management for non-NixOS systems.
Key Principles
- Idempotent - Scripts can be run multiple times safely
- Self-contained - Each host directory is independent
- Documented - Each host has a README explaining its setup
- Versioned - Configuration is tracked in git like NixOS configs
Directory Structure
Each host has its own directory containing:
imperative/
├── README.md # This file
├── nagoya/ # Debian server
│ ├── README.md # Host-specific documentation
│ └── apply.sh # Main configuration script
└── wakasu/ # Fedora desktop
├── README.md # Host-specific documentation
└── apply.sh # Main configuration script
Current Hosts
nagoya - Debian Server
- OS: Debian (aarch64)
- Type: Server
- Components: Docker, Kind, Wireguard, Syncthing
- VPN IP: 10.100.0.80/24
See nagoya/README.md for details.
wakasu - Fedora Desktop
- OS: Fedora
- Type: Desktop
- User: vincent
- Components: Helix, ACPI, Wireguard, Syncthing
- VPN IP: 10.100.0.90/24
See wakasu/README.md for details.
Usage
Running Configuration Scripts
From the repository root:
# Apply configuration for a specific host
sudo ./imperative/<hostname>/apply.sh
# With environment variables (e.g., for wireguard)
sudo WG_PRIVATE_KEY="..." ./imperative/<hostname>/apply.sh
Adding a New Host
-
Create a new directory for the host:
mkdir imperative/<hostname> -
Create an
apply.shscript following the existing patterns:- Use
set -euo pipefailfor safety - Implement colored logging functions
- Make functions idempotent
- Use
setup.*naming convention for functions
- Use
-
Create a
README.mddocumenting:- System information
- Components installed
- Usage instructions
- Environment variables needed
-
Make the script executable:
chmod +x imperative/<hostname>/apply.sh -
Update this README to include the new host
Testing Scripts
Test scripts in a VM or container before running on production systems:
# Example with Docker
docker run -it --rm -v $(pwd):/repo debian:latest
cd /repo
./imperative/<hostname>/apply.sh
Script Conventions
Error Handling
All scripts use strict error handling:
set -euo pipefail
This ensures:
set -e- Exit on any errorset -u- Exit on undefined variableset -o pipefail- Catch errors in pipes
Logging
Use colored logging functions for clarity:
log_info "Normal operation message"
log_warn "Warning message"
log_error "Error message"
Function Naming
Use setup.* prefix for setup functions:
setup.docker() { ... }
setup.wireguard() { ... }
setup.syncthing() { ... }
Environment Variables
- Document all required environment variables in the host’s README
- Provide sensible defaults where possible
- Fail gracefully or warn when optional variables are missing
Idempotency
Make operations idempotent by:
- Checking if software is already installed
- Using
||for commands that might fail on re-run - Avoiding destructive operations
- Using configuration management tools when available
Integration with NixOS Configs
While these scripts are imperative, they share concepts with the NixOS configurations:
- Wireguard configuration matches the network topology in
globals.nix - Syncthing IDs should be coordinated with NixOS hosts
- VPN IP addressing follows the same scheme (10.100.0.0/24)
Transitioning to NixOS
When ready to migrate a host to NixOS:
- Document the current system state using the imperative script as reference
- Create a new NixOS configuration in
/systems/<hostname> - Test the NixOS configuration in a VM
- Perform the migration
- Archive or remove the imperative configuration
Maintenance
Updating Scripts
When updating scripts:
- Test changes in a VM or non-production system
- Update the host’s README if behavior changes
- Run shellcheck to catch common issues:
shellcheck imperative/<hostname>/apply.sh
Version Control
All changes to imperative configurations should be:
- Committed to git
- Documented in commit messages
- Tagged if significant milestones are reached
Comparison: Imperative vs NixOS
| Aspect | Imperative (this directory) | NixOS (/systems) |
|---|---|---|
| Approach | Scripts to reach state | Declare desired state |
| Idempotency | Manual (via careful coding) | Automatic |
| Rollback | Manual/difficult | Built-in generations |
| Reproducibility | Best effort | Guaranteed |
| Documentation | README + comments | Code is documentation |
| OS Support | Any Linux distro | NixOS only |
| Learning Curve | Familiar bash scripts | Steeper (Nix language) |
| State Management | Imperative commands | Declarative configuration |
Resources
Similar Approaches
- Ansible playbooks (declarative automation)
- Chef/Puppet recipes (configuration management)
- Shell scripts (traditional approach)
Why Not Use These Tools?
For simple single-host configurations, a well-written bash script:
- Has no dependencies beyond bash and standard tools
- Is easy to understand and modify
- Runs quickly without agent overhead
- Keeps everything in this repository
For managing many similar hosts, consider tools like Ansible.