- Add github.com/google/uuid to dependencies list - Fix version bumping: both main.go and flake.nix need updates - Add section on updating vendorHash when dependencies change - Use nix run .#default instead of nix build for verification Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
homelab-deploy
A message-based deployment system for NixOS configurations using NATS for messaging. Deploy NixOS configurations across a fleet of hosts with support for tiered access control, role-based targeting, and AI assistant integration.
Overview
The homelab-deploy binary provides three operational modes:
- Listener mode - Runs on each NixOS host as a systemd service, subscribing to NATS subjects and executing
nixos-rebuildwhen deployment requests arrive - MCP mode - Runs as an MCP (Model Context Protocol) server, exposing deployment tools for AI assistants
- CLI mode - Manual deployment commands for administrators
Installation
Using Nix Flakes
# Run directly
nix run github:torjus/homelab-deploy -- --help
# Add to your flake inputs
{
inputs.homelab-deploy.url = "github:torjus/homelab-deploy";
}
Building from source
nix develop
go build ./cmd/homelab-deploy
CLI Usage
Listener Mode
Run on each NixOS host to listen for deployment requests:
homelab-deploy listener \
--hostname myhost \
--tier prod \
--nats-url nats://nats.example.com:4222 \
--nkey-file /run/secrets/listener.nkey \
--flake-url git+https://git.example.com/user/nixos-configs.git \
--role dns \
--timeout 600
Listener Flags
| Flag | Required | Description |
|---|---|---|
--hostname |
Yes | Hostname for this listener |
--tier |
Yes | Deployment tier (test or prod) |
--nats-url |
Yes | NATS server URL |
--nkey-file |
Yes | Path to NKey seed file |
--flake-url |
Yes | Git flake URL for nixos-rebuild |
--role |
No | Role for role-based targeting |
--timeout |
No | Deployment timeout in seconds (default: 600) |
--deploy-subject |
No | NATS subjects to subscribe to (repeatable) |
--discover-subject |
No | Discovery subject (default: deploy.discover) |
Subject Templates
Deploy subjects support template variables that are expanded at startup:
<hostname>- The listener's hostname<tier>- The listener's tier<role>- The listener's role (subjects with<role>are skipped if role is not set)
Default subjects:
deploy.<tier>.<hostname>
deploy.<tier>.all
deploy.<tier>.role.<role>
Deploy Command
Deploy to hosts via NATS:
# Deploy to a specific host
homelab-deploy deploy deploy.prod.myhost \
--nats-url nats://nats.example.com:4222 \
--nkey-file /run/secrets/deployer.nkey \
--branch main \
--action switch
# Deploy to all test hosts
homelab-deploy deploy deploy.test.all \
--nats-url nats://nats.example.com:4222 \
--nkey-file /run/secrets/deployer.nkey
# Deploy to all prod DNS servers
homelab-deploy deploy deploy.prod.role.dns \
--nats-url nats://nats.example.com:4222 \
--nkey-file /run/secrets/deployer.nkey
Deploy Flags
| Flag | Required | Description |
|---|---|---|
--nats-url |
Yes | NATS server URL |
--nkey-file |
Yes | Path to NKey seed file |
--branch |
No | Git branch or commit (default: master) |
--action |
No | nixos-rebuild action (default: switch) |
--timeout |
No | Response timeout in seconds (default: 900) |
Subject Aliases
Configure aliases via environment variables to simplify common deployments:
export HOMELAB_DEPLOY_ALIAS_TEST="deploy.test.all"
export HOMELAB_DEPLOY_ALIAS_PROD="deploy.prod.all"
export HOMELAB_DEPLOY_ALIAS_PROD_DNS="deploy.prod.role.dns"
# Now use short aliases
homelab-deploy deploy test --nats-url ... --nkey-file ...
homelab-deploy deploy prod-dns --nats-url ... --nkey-file ...
Alias lookup: HOMELAB_DEPLOY_ALIAS_<NAME> where name is uppercased and hyphens become underscores.
MCP Server Mode
Run as an MCP server for AI assistant integration:
# Test-tier only access
homelab-deploy mcp \
--nats-url nats://nats.example.com:4222 \
--nkey-file /run/secrets/mcp.nkey
# With admin access to all tiers
homelab-deploy mcp \
--nats-url nats://nats.example.com:4222 \
--nkey-file /run/secrets/mcp.nkey \
--enable-admin \
--admin-nkey-file /run/secrets/admin.nkey
MCP Tools
| Tool | Description |
|---|---|
deploy |
Deploy to test-tier hosts only |
deploy_admin |
Deploy to any tier (requires --enable-admin) |
list_hosts |
Discover available deployment targets |
Tool Parameters
deploy / deploy_admin:
hostname- Target specific hostall- Deploy to all hosts (in tier)role- Deploy to hosts with this rolebranch- Git branch/commit (default: master)action- switch, boot, test, dry-activate (default: switch)tier- Required for deploy_admin only
list_hosts:
tier- Filter by tier (optional)
NixOS Module
Add the module to your NixOS configuration:
{
inputs.homelab-deploy.url = "github:torjus/homelab-deploy";
outputs = { self, nixpkgs, homelab-deploy, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [
homelab-deploy.nixosModules.default
{
services.homelab-deploy.listener = {
enable = true;
tier = "prod";
role = "dns";
natsUrl = "nats://nats.example.com:4222";
nkeyFile = "/run/secrets/homelab-deploy-nkey";
flakeUrl = "git+https://git.example.com/user/nixos-configs.git";
};
}
];
};
};
}
Module Options
| Option | Type | Default | Description |
|---|---|---|---|
enable |
bool | false |
Enable the listener service |
package |
package | pkgs.homelab-deploy |
Package to use |
hostname |
string | config.networking.hostName |
Hostname for subject templates |
tier |
enum | required | "test" or "prod" |
role |
string | null |
Role for role-based targeting |
natsUrl |
string | required | NATS server URL |
nkeyFile |
path | required | Path to NKey seed file |
flakeUrl |
string | required | Git flake URL |
timeout |
int | 600 |
Deployment timeout in seconds |
deploySubjects |
list of string | see below | Subjects to subscribe to |
discoverSubject |
string | "deploy.discover" |
Discovery subject |
environment |
attrs | {} |
Additional environment variables |
Default deploySubjects:
[
"deploy.<tier>.<hostname>"
"deploy.<tier>.all"
"deploy.<tier>.role.<role>"
]
Message Protocol
Deploy Request
{
"action": "switch",
"revision": "main",
"reply_to": "deploy.responses.abc123"
}
Deploy Response
{
"hostname": "myhost",
"status": "completed",
"error": null,
"message": "Successfully switched to generation 42"
}
Status values: accepted, rejected, started, completed, failed
Error codes: invalid_revision, invalid_action, already_running, build_failed, timeout
NATS Authentication
All connections use NKey authentication. Generate keys with:
nk -gen user -pubout
Configure appropriate publish/subscribe permissions in your NATS server for each credential type.
License
MIT