Initial commit

This commit is contained in:
Shawn Borton 2026-06-21 01:36:08 -05:00
commit a4db9c8516
36 changed files with 15659 additions and 0 deletions

View file

@ -0,0 +1,139 @@
{ config, pkgs, modulesPath, lib, system, ... }:
{
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
(modulesPath + "/profiles/minimal.nix")
# If for some reason "minimal" fails...
#(modulesPath + "/profiles/base.nix")
../virtualization/podman-utils.nix
../virtualization/nebula-sync-oci.nix
];
config = {
boot.kernelPackages = pkgs.linuxPackages_latest;
# Use the boot drive for grub
boot.loader.grub.enable = lib.mkDefault true;
boot.loader.grub.devices = [ "nodev" ];
boot.growPartition = lib.mkDefault true;
# Workaround for busted ZFS kernels
boot.supportedFilesystems = {
zfs = lib.mkForce false;
};
# Allow remote updates with flakes and non-root users
nix.settings.trusted-users = [ "root" "@wheel" ];
nix.settings.experimental-features = [ "nix-command" "flakes" ];
# Provide a default hostname
networking.hostName = lib.mkDefault "crowsnest";
# Activate firewall
networking.firewall.enable = true;
# Open firewall ports for Portainer
networking.firewall.allowedTCPPorts = [80 443 2112 8000];
# Time Zone
time.timeZone = "America/Chicago";
# Enable QEMU Guest for Proxmox
services.qemuGuest.enable = lib.mkDefault true;
#OpenSSH
security.sudo.wheelNeedsPassword = false; # Don't ask for passwords
services.openssh = {
enable = true;
settings.PasswordAuthentication = false;
settings.KbdInteractiveAuthentication = false;
ports = [ 2112 ];
};
programs.ssh.startAgent = true;
#SOPS
sops.defaultSopsFile = ../secrets/secrets.yaml;
sops.defaultSopsFormat = "yaml";
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
# sops.secrets."nebula-sync-primary" = {
# owner = "nebula";
# };
# sops.secrets."nebula-sync-replicas" = {
# owner = "nebula";
# };
#sops.age.keyFile = "/home/shawnb/Development/falcon_nest/sops/age/keys.txt";
#sops.secrets."nebula-sync" = {
# owner = config.users.users.nebula.name;
#};
# Some sane packages we need on every system
environment.systemPackages = with pkgs; [
vim # for emergencies
git # for pulling nix flakes
zsh
bat
eza
podman-tui
];
environment.shells = with pkgs; [ zsh ];
users.defaultUserShell = pkgs.zsh;
programs.zsh = {
shellAliases = {
#.. = "cd ..";
h = "cd ~";
ls = "eza --icons -l -T -L=1";
cat = "bat";
};
enable = lib.mkDefault true;
autosuggestions.enable = lib.mkDefault true;
};
#oh-my-zsh
programs.zsh.ohMyZsh = {
enable = lib.mkDefault true;
theme = lib.mkDefault "jtriley";
};
# Default filesystem
fileSystems."/" = lib.mkDefault {
device = "/dev/disk/by-label/nixos";
autoResize = true;
fsType = "ext4";
};
fileSystems."/mnt/gitlab" = {
device = "//10.0.0.49/Gitlab";
fsType = "cifs";
# options = [ "username=eddie" "password=yoursambapassword" "x-systemd.automount" "noauto" ];
options = [ "x-systemd.automount" "noauto" ];
}
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
system.stateVersion = lib.mkDefault "25.05";
# Let the VM take care of its own upgrades.
system.autoUpgrade.enable = true;
# Clean Nix up after upgrades.
programs.nh = {
enable = true;
clean.enable = true;
clean.extraArgs = "--keep 5";
};
# Add an admin user
users.users.shawnb = lib.mkDefault {
isNormalUser = true;
hashedPassword = "";
description = "Shawn Borton";
extraGroups = [ "networkmanager" "wheel" ];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILgyiCfvu21kVTfRyKjzpH+93k7Jsf+EqLfN6NtolHnf tech@shilohsvillage.com"
];
};
};
}

View file

@ -0,0 +1,104 @@
{ config, pkgs, modulesPath, lib, system, ... }:
{
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
(modulesPath + "/profiles/minimal.nix")
../virtualization/podman-utils.nix
../virtualization/portainer.nix
];
config = {
#Provide a default hostname
networking.hostName = lib.mkDefault "crowsnest";
# Activate firewall
networking.firewall.enable = true;
# Open firewall ports for Portainer
networking.firewall.allowedTCPPorts = [ 2112 8000 9443 9000 ];
# Time Zone
time.timeZone = "America/Chicago";
# Enable QEMU Guest for Proxmox
services.qemuGuest.enable = lib.mkDefault true;
# Use the boot drive for grub
boot.loader.grub.enable = lib.mkDefault true;
boot.loader.grub.devices = [ "nodev" ];
boot.growPartition = lib.mkDefault true;
# Allow remote updates with flakes and non-root users
nix.settings.trusted-users = [ "root" "@wheel" ];
nix.settings.experimental-features = [ "nix-command" "flakes" ];
# Some sane packages we need on every system
environment.systemPackages = with pkgs; [
vim # for emergencies
git # for pulling nix flakes
zsh
bat
eza
];
environment.shells = with pkgs; [ zsh ];
users.defaultUserShell = pkgs.zsh;
programs.zsh = {
shellAliases = {
#.. = "cd ..";
h = "cd ~";
ls = "eza --icons -l -T -L=1";
cat = "bat";
};
enable = lib.mkDefault true;
autosuggestions.enable = lib.mkDefault true;
};
#oh-my-zsh
programs.zsh.ohMyZsh = {
enable = lib.mkDefault true;
theme = lib.mkDefault "jtriley";
};
# Default filesystem
fileSystems."/" = lib.mkDefault {
device = "/dev/disk/by-label/nixos";
autoResize = true;
fsType = "ext4";
};
system.stateVersion = lib.mkDefault "25.05";
# Let the VM take care of its own upgrades.
system.autoUpgrade.enable = true;
# Clean Nix up after upgrades.
programs.nh = {
enable = true;
clean.enable = true;
clean.extraArgs = "--keep 5";
};
security.sudo.wheelNeedsPassword = false; # Don't ask for passwords
services.openssh = {
enable = true;
settings.PasswordAuthentication = false;
settings.KbdInteractiveAuthentication = false;
ports = [ 2112 ];
};
programs.ssh.startAgent = true;
# Add an admin user
users.users.shawnb = lib.mkDefault {
isNormalUser = true;
hashedPassword = "";
description = "Shawn Borton";
extraGroups = [ "networkmanager" "wheel" ];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILgyiCfvu21kVTfRyKjzpH+93k7Jsf+EqLfN6NtolHnf tech@shilohsvillage.com"
];
};
};
}

View file

@ -0,0 +1,53 @@
# Example to create a bios compatible gpt partition
{ lib, ... }: {
disko.devices = {
disk.disk1 = {
device = lib.mkDefault "/dev/sda";
type = "disk";
content = {
type = "gpt";
partitions = {
boot = {
name = "boot";
size = "1M";
type = "EF02";
};
esp = {
name = "ESP";
size = "500M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
root = {
name = "root";
size = "100%";
content = {
type = "lvm_pv";
vg = "pool";
};
};
};
};
};
lvm_vg = {
pool = {
type = "lvm_vg";
lvs = {
root = {
size = "100%FREE";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
mountOptions = [ "defaults" ];
};
};
};
};
};
};
}

348
flake.lock generated Normal file
View file

@ -0,0 +1,348 @@
{
"nodes": {
"disko": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1781152676,
"narHash": "sha256-RxWs5ND31KzTG7wvMM+PMfUjyNpmIEr999lqNARaM5o=",
"owner": "nix-community",
"repo": "disko",
"rev": "ff8702b4de27f72b4c78573dfb89ec74e36abdf1",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"hermes-agent",
"nixpkgs"
]
},
"locked": {
"lastModified": 1772408722,
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"hermes-agent": {
"inputs": {
"flake-parts": "flake-parts",
"nixpkgs": [
"nixpkgs"
],
"npm-lockfile-fix": "npm-lockfile-fix",
"pyproject-build-systems": "pyproject-build-systems",
"pyproject-nix": "pyproject-nix_2",
"uv2nix": "uv2nix_2"
},
"locked": {
"lastModified": 1782023027,
"narHash": "sha256-DZf5zB6Uu43O5z5zC7tFsBwedDBu1UCVolWRkslrLpo=",
"owner": "NousResearch",
"repo": "hermes-agent",
"rev": "fcdefb4181db22da0796c1ac0969542ebac0263b",
"type": "github"
},
"original": {
"owner": "NousResearch",
"repo": "hermes-agent",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1779506708,
"narHash": "sha256-QOD/CNm196nCJRheux/URi4/HE66fthdOMqCJoPP1Y0=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "3ee51fbdac8c8bdfe1e7e1fcaba6520a563f394f",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "release-25.11",
"repo": "home-manager",
"type": "github"
}
},
"nixlib": {
"locked": {
"lastModified": 1736643958,
"narHash": "sha256-tmpqTSWVRJVhpvfSN9KXBvKEXplrwKnSZNAoNPf/S/s=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "1418bc28a52126761c02dd3d89b2d8ca0f521181",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixos-generators": {
"inputs": {
"nixlib": "nixlib",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1769813415,
"narHash": "sha256-nnVmNNKBi1YiBNPhKclNYDORoHkuKipoz7EtVnXO50A=",
"owner": "nix-community",
"repo": "nixos-generators",
"rev": "8946737ff703382fda7623b9fab071d037e897d5",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixos-generators",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1781509190,
"narHash": "sha256-uJZs9Di8I6ciTp6jiojj0HzlNpBkud8ax5aT/O5aJkw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d6df3513510aa548c83868fd22bfddd0a8c0a0d4",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-25.11",
"type": "indirect"
}
},
"npm-lockfile-fix": {
"inputs": {
"nixpkgs": [
"hermes-agent",
"nixpkgs"
]
},
"locked": {
"lastModified": 1775903712,
"narHash": "sha256-2GV79U6iVH4gKAPWYrxUReB0S41ty/Y3dBLquU8AlaA=",
"owner": "jeslie0",
"repo": "npm-lockfile-fix",
"rev": "c6093acb0c0548e0f9b8b3d82918823721930fe8",
"type": "github"
},
"original": {
"owner": "jeslie0",
"repo": "npm-lockfile-fix",
"type": "github"
}
},
"pyproject-build-systems": {
"inputs": {
"nixpkgs": [
"hermes-agent",
"nixpkgs"
],
"pyproject-nix": "pyproject-nix",
"uv2nix": "uv2nix"
},
"locked": {
"lastModified": 1772555609,
"narHash": "sha256-3BA3HnUvJSbHJAlJj6XSy0Jmu7RyP2gyB/0fL7XuEDo=",
"owner": "pyproject-nix",
"repo": "build-system-pkgs",
"rev": "c37f66a953535c394244888598947679af231863",
"type": "github"
},
"original": {
"owner": "pyproject-nix",
"repo": "build-system-pkgs",
"type": "github"
}
},
"pyproject-nix": {
"inputs": {
"nixpkgs": [
"hermes-agent",
"pyproject-build-systems",
"nixpkgs"
]
},
"locked": {
"lastModified": 1769936401,
"narHash": "sha256-kwCOegKLZJM9v/e/7cqwg1p/YjjTAukKPqmxKnAZRgA=",
"owner": "nix-community",
"repo": "pyproject.nix",
"rev": "b0d513eeeebed6d45b4f2e874f9afba2021f7812",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "pyproject.nix",
"type": "github"
}
},
"pyproject-nix_2": {
"inputs": {
"nixpkgs": [
"hermes-agent",
"nixpkgs"
]
},
"locked": {
"lastModified": 1772865871,
"narHash": "sha256-/ZTSg97aouL0SlPHaokA4r3iuH9QzHVuWPACD2CUCFY=",
"owner": "pyproject-nix",
"repo": "pyproject.nix",
"rev": "e537db02e72d553cea470976b9733581bcf5b3ed",
"type": "github"
},
"original": {
"owner": "pyproject-nix",
"repo": "pyproject.nix",
"type": "github"
}
},
"pyproject-nix_3": {
"inputs": {
"nixpkgs": [
"hermes-agent",
"uv2nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1771518446,
"narHash": "sha256-nFJSfD89vWTu92KyuJWDoTQJuoDuddkJV3TlOl1cOic=",
"owner": "pyproject-nix",
"repo": "pyproject.nix",
"rev": "eb204c6b3335698dec6c7fc1da0ebc3c6df05937",
"type": "github"
},
"original": {
"owner": "pyproject-nix",
"repo": "pyproject.nix",
"type": "github"
}
},
"quadlet-nix": {
"locked": {
"lastModified": 1782004439,
"narHash": "sha256-OANOcPKJ3JThp2x/ccz4DDrGDY2hIgM5SBLI51swgfI=",
"owner": "SEIAROTg",
"repo": "quadlet-nix",
"rev": "f1652b490b812c4e0b2a36565cdbedf87f35e438",
"type": "github"
},
"original": {
"owner": "SEIAROTg",
"repo": "quadlet-nix",
"type": "github"
}
},
"root": {
"inputs": {
"disko": "disko",
"hermes-agent": "hermes-agent",
"home-manager": "home-manager",
"nixos-generators": "nixos-generators",
"nixpkgs": "nixpkgs",
"quadlet-nix": "quadlet-nix",
"sops-nix": "sops-nix"
}
},
"sops-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1781943681,
"narHash": "sha256-NFHmA7H47adqiyp+0iEOyZOQhmigDqA/NBAlf4imB6U=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "420f8d2e9882911f65cfac15cc706f639ba96cca",
"type": "github"
},
"original": {
"owner": "Mic92",
"repo": "sops-nix",
"type": "github"
}
},
"uv2nix": {
"inputs": {
"nixpkgs": [
"hermes-agent",
"pyproject-build-systems",
"nixpkgs"
],
"pyproject-nix": [
"hermes-agent",
"pyproject-build-systems",
"pyproject-nix"
]
},
"locked": {
"lastModified": 1770770348,
"narHash": "sha256-A2GzkmzdYvdgmMEu5yxW+xhossP+txrYb7RuzRaqhlg=",
"owner": "pyproject-nix",
"repo": "uv2nix",
"rev": "5d1b2cb4fe3158043fbafbbe2e46238abbc954b0",
"type": "github"
},
"original": {
"owner": "pyproject-nix",
"repo": "uv2nix",
"type": "github"
}
},
"uv2nix_2": {
"inputs": {
"nixpkgs": [
"hermes-agent",
"nixpkgs"
],
"pyproject-nix": "pyproject-nix_3"
},
"locked": {
"lastModified": 1773039484,
"narHash": "sha256-+boo33KYkJDw9KItpeEXXv8+65f7hHv/earxpcyzQ0I=",
"owner": "pyproject-nix",
"repo": "uv2nix",
"rev": "b68be7cfeacbed9a3fa38a2b5adc0cfb81d9bb1f",
"type": "github"
},
"original": {
"owner": "pyproject-nix",
"repo": "uv2nix",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

65
flake.nix Normal file
View file

@ -0,0 +1,65 @@
{
description = "Base flake for ShawnB's NixOS-based VMs and LXCs.";
inputs = {
nixpkgs.url = "nixpkgs/nixos-25.11";
nixos-generators.url = "github:nix-community/nixos-generators";
nixos-generators.inputs.nixpkgs.follows = "nixpkgs";
disko.url = "github:nix-community/disko";
disko.inputs.nixpkgs.follows = "nixpkgs";
hermes-agent.url = "github:NousResearch/hermes-agent";
hermes-agent.inputs.nixpkgs.follows = "nixpkgs";
home-manager.url = "github:nix-community/home-manager/release-25.11";
home-manager.inputs.nixpkgs.follows = "nixpkgs";
quadlet-nix.url = "github:SEIAROTg/quadlet-nix";
sops-nix.url = "github:Mic92/sops-nix";
sops-nix.inputs.nixpkgs.follows = "nixpkgs";
};
outputs =
{
nixpkgs,
nixos-generators,
hermes-agent,
home-manager,
quadlet-nix,
sops-nix,
disko,
...
}@inputs:
{
nixosConfigurations = {
crowsNest = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit inputs; };
modules = [
./crows_nest/configuration.nix
sops-nix.nixosModules.sops
# to enable podman & podman systemd generator
quadlet-nix.nixosModules.quadlet
];
};
hermesAgent = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit inputs; };
modules = [
disko.nixosModules.disko
./hermes_agent/disko-config.nix
./hermes_agent/configuration.nix
];
};
};
packages.x86_64-linux = {
crows_nest_vm = nixos-generators.nixosGenerate {
system = "x86_64-linux";
modules = [ ./crows_nest/configuration.nix ];
format = "virtualbox";
};
hermes_agent_vm = nixos-generators.nixosGenerate {
system = "x86_64-linux";
modules = [ ./hermes_agent/configuration.nix ];
format = "virtualbox";
};
};
};
}

View file

@ -0,0 +1,137 @@
{ config, pkgs, modulesPath, lib, system, ... }:
{
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
(modulesPath + "/profiles/minimal.nix")
# If for some reason "minimal" fails...
#(modulesPath + "/profiles/base.nix")
];
config = {
boot.kernelPackages = pkgs.linuxPackages_latest;
# Use the boot drive for grub
boot.loader.grub.enable = lib.mkDefault true;
boot.loader.grub.devices = [ "nodev" ];
boot.growPartition = lib.mkDefault true;
# Workaround for busted ZFS kernels
#boot.supportedFilesystems = {
# zfs = lib.mkForce false;
#};
# Allow remote updates with flakes and non-root users
nix.settings.trusted-users = [ "root" "@wheel" ];
nix.settings.experimental-features = [ "nix-command" "flakes" ];
# Provide a default hostname
networking.hostName = lib.mkDefault "bubo";
# Activate firewall
networking.firewall.enable = true;
# Open firewall ports
networking.firewall.allowedTCPPorts = [ 80 443 2112 ];
# Time Zone
time.timeZone = "America/Chicago";
# configuration.nix
# Hermes Agent
# services.hermes-agent = {
# enable = true;
# settings.model.default = "opencode/claude-haiku-4-5";
# environmentFiles = [ config.sops.secrets."hermes-env".path ];
# addToSystemPackages = true;
# };
# Enable QEMU Guest for Proxmox
services.qemuGuest.enable = lib.mkDefault true;
#OpenSSH
security.sudo.wheelNeedsPassword = false; # Don't ask for passwords
services.openssh = {
enable = true;
settings.PasswordAuthentication = false;
settings.KbdInteractiveAuthentication = false;
ports = [ 2112 ];
};
programs.ssh.startAgent = true;
#SOPS
# sops.defaultSopsFile = ../secrets/secrets.yaml;
# sops.defaultSopsFormat = "yaml";
# sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
# sops.secrets."nebula-sync-primary" = {
# owner = "nebula";
# };
# sops.secrets."nebula-sync-replicas" = {
# owner = "nebula";
# };
#sops.age.keyFile = "/home/shawnb/Development/falcon_nest/sops/age/keys.txt";
#sops.secrets."nebula-sync" = {
# owner = config.users.users.nebula.name;
#};
# Some sane packages we need on every system
environment.systemPackages = with pkgs; [
vim # for emergencies
git # for pulling nix flakes
zsh
bat
eza
];
environment.shells = with pkgs; [ zsh ];
users.defaultUserShell = pkgs.zsh;
programs.zsh = {
shellAliases = {
#.. = "cd ..";
h = "cd ~";
ls = "eza --icons -l -T -L=1";
cat = "bat";
};
enable = lib.mkDefault true;
autosuggestions.enable = lib.mkDefault true;
};
#oh-my-zsh
programs.zsh.ohMyZsh = {
enable = lib.mkDefault true;
theme = lib.mkDefault "jtriley";
};
# Default filesystem
fileSystems."/" = lib.mkDefault {
device = "/dev/disk/by-label/nixos";
autoResize = true;
fsType = "ext4";
};
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
system.stateVersion = lib.mkDefault "26.05";
# Clean Nix up after upgrades.
programs.nh = {
enable = true;
clean.enable = true;
clean.extraArgs = "--keep 5";
};
# Add an admin user
users.users.shawnb = lib.mkDefault {
isNormalUser = true;
hashedPassword = "";
description = "Shawn Borton";
extraGroups = [ "networkmanager" "wheel" ];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILgyiCfvu21kVTfRyKjzpH+93k7Jsf+EqLfN6NtolHnf tech@shilohsvillage.com"
];
};
};
}

View file

@ -0,0 +1,50 @@
{
config,
lib,
pkgs,
...
}:
# USAGE in your configuration.nix.
# Update devices to match your hardware.
# {
# imports = [ ./disko-config.nix ];
# disko.devices.disk.main.device = "/dev/sda";
# }
{
disko.devices = {
disk = {
device = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive-scsi0";
main = {
type = "disk";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02"; # for grub MBR
};
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}

View file

@ -0,0 +1,323 @@
#+TITLE: Crows Nest Configuration
* Crows Nest Configuration
Configuration for Crows nest, an automation platform featuring Portainer, Ansible, and Gitlab
#+begin_src nix :noweb yes :tangle /home/shawnb/Development/falcon_nest/crows_nest/configuration.nix
{ config, pkgs, modulesPath, lib, system, ... }:
{
imports = [
<<import-paths>>
];
config = {
<<boot-settings>>
<<nix-settings>>
<<networking-config>>
<<timey-wimey-stuff>>
<<services-config>>
# Some sane packages we need on every system
environment.systemPackages = with pkgs; [
vim # for emergencies
git # for pulling nix flakes
<<system-packages>>
];
<<shell-configuration>>
<<file-system>>
<<system-information>>
<<user-config>>
};
}
#+end_src
** Imports
#+NAME: imports
#+begin_src nix :noweb-ref import-paths
(modulesPath + "/profiles/qemu-guest.nix")
(modulesPath + "/profiles/minimal.nix")
# If for some reason "minimal" fails...
#(modulesPath + "/profiles/base.nix")
../virtualization/podman-utils.nix
../virtualization/nebula-sync-oci.nix
#+end_src
** ISO Information
#+NAME: iso-config
#+begin_src nix
# Let's get it booted in here
(modulesPath + "/installer/cd-dvd/iso-image.nix")
(modulesPath + "/installer/cd-dvd/installation-cd-minimal.nix")
# Provide an initial copy of the NixOS channel so that the user
# doesn't need to run "nix-channel --update" first.
(modulesPath + "/installer/cd-dvd/channel.nix")
# EFI + USB bootable
isoImage.makeEfiBootable = true;
isoImage.makeUsbBootable = true;
#+end_src
** Boot Settings
#+NAME: boot-settings
#+begin_src nix :noweb-ref boot-settings
boot.kernelPackages = pkgs.linuxPackages_latest;
# Use the boot drive for grub
boot.loader.grub.enable = lib.mkDefault true;
boot.loader.grub.devices = [ "nodev" ];
boot.growPartition = lib.mkDefault true;
# Workaround for busted ZFS kernels
boot.supportedFilesystems = {
zfs = lib.mkForce false;
};
#+end_src
** Nix Settings
#+NAME: nix-settings
#+begin_src nix :noweb-ref nix-settings
# Allow remote updates with flakes and non-root users
nix.settings.trusted-users = [ "root" "@wheel" ];
nix.settings.experimental-features = [ "nix-command" "flakes" ];
#+end_src
** Networking and Time Settings
#+NAME: networking-config
#+begin_src nix :var ports=network-ports :noweb-ref networking-config
# Provide a default hostname
networking.hostName = lib.mkDefault "crowsnest";
# Activate firewall
networking.firewall.enable = true;
# Open firewall ports for Portainer
networking.firewall.allowedTCPPorts = [80 443 2112 8000];
#+end_src
#+NAME: timey-wimey-stuff
#+begin_src nix :noweb-ref timey-wimey-stuff
# Time Zone
time.timeZone = "America/Chicago";
#+end_src
** System Packages
#+NAME: system-packages
#+begin_src nix :noweb-ref system-packages
zsh
bat
eza
podman-tui
#+end_src
** Shell Configuration
#+NAME: shell-configuration
#+begin_src nix :noweb-ref shell-configuration
environment.shells = with pkgs; [ zsh ];
users.defaultUserShell = pkgs.zsh;
programs.zsh = {
shellAliases = {
#.. = "cd ..";
h = "cd ~";
ls = "eza --icons -l -T -L=1";
cat = "bat";
};
enable = lib.mkDefault true;
autosuggestions.enable = lib.mkDefault true;
};
#oh-my-zsh
programs.zsh.ohMyZsh = {
enable = lib.mkDefault true;
theme = lib.mkDefault "jtriley";
};
#+end_src
** File System
#+NAME: file-system
#+begin_src nix :noweb-ref file-system
# Default filesystem
fileSystems."/" = lib.mkDefault {
device = "/dev/disk/by-label/nixos";
autoResize = true;
fsType = "ext4";
};
fileSystems."/mnt/gitlab" = {
device = "//10.0.0.49/Gitlab";
fsType = "cifs";
# options = [ "username=eddie" "password=yoursambapassword" "x-systemd.automount" "noauto" ];
options = [ "x-systemd.automount" "noauto" ];
}
#+end_src
** System Information
#+NAME: system-information
#+begin_src nix :noweb-ref system-information
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
system.stateVersion = lib.mkDefault "25.05";
# Let the VM take care of its own upgrades.
system.autoUpgrade.enable = true;
# Clean Nix up after upgrades.
programs.nh = {
enable = true;
clean.enable = true;
clean.extraArgs = "--keep 5";
};
#+end_src
** Users
#+NAME: user-config
#+begin_src nix :noweb-ref user-config
# Add an admin user
users.users.shawnb = lib.mkDefault {
isNormalUser = true;
hashedPassword = "";
description = "Shawn Borton";
extraGroups = [ "networkmanager" "wheel" ];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILgyiCfvu21kVTfRyKjzpH+93k7Jsf+EqLfN6NtolHnf tech@shilohsvillage.com"
];
};
#+end_src
** Services
#+NAME: services-config
#+begin_src nix :noweb-ref services-config
# Enable QEMU Guest for Proxmox
services.qemuGuest.enable = lib.mkDefault true;
#OpenSSH
security.sudo.wheelNeedsPassword = false; # Don't ask for passwords
services.openssh = {
enable = true;
settings.PasswordAuthentication = false;
settings.KbdInteractiveAuthentication = false;
ports = [ 2112 ];
};
programs.ssh.startAgent = true;
#SOPS
sops.defaultSopsFile = ../secrets/secrets.yaml;
sops.defaultSopsFormat = "yaml";
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
# sops.secrets."nebula-sync-primary" = {
# owner = "nebula";
# };
# sops.secrets."nebula-sync-replicas" = {
# owner = "nebula";
# };
#sops.age.keyFile = "/home/shawnb/Development/falcon_nest/sops/age/keys.txt";
#sops.secrets."nebula-sync" = {
# owner = config.users.users.nebula.name;
#};
#+end_src
** OLD CONFIG :noexport:
#+NAME: old_config
#+begin_src nix
{ config, pkgs, modulesPath, lib, system, ... }:
{
#imports = [
# (modulesPath + "/profiles/qemu-guest.nix")
# (modulesPath + "/profiles/minimal.nix")
# ../virtualization/podman-utils.nix
# ../virtualization/portainer.nix
#];
config = {
#Provide a default hostname
#networking.hostName = lib.mkDefault "crowsnest";
# Activate firewall
#networking.firewall.enable = true;
# Open firewall ports for Portainer
#networking.firewall.allowedTCPPorts = [ 2112 8000 9443 9000 ];
# Time Zone
#time.timeZone = "America/Chicago";
# Enable QEMU Guest for Proxmox
# services.qemuGuest.enable = lib.mkDefault true;
# Use the boot drive for grub
# boot.loader.grub.enable = lib.mkDefault true;
# boot.loader.grub.devices = [ "nodev" ];
# boot.growPartition = lib.mkDefault true;
# Allow remote updates with flakes and non-root users
# nix.settings.trusted-users = [ "root" "@wheel" ];
# nix.settings.experimental-features = [ "nix-command" "flakes" ];
# Some sane packages we need on every system
# environment.systemPackages = with pkgs; [
# vim # for emergencies
# git # for pulling nix flakes
# zsh
# bat
# eza
# ];
# environment.shells = with pkgs; [ zsh ];
# users.defaultUserShell = pkgs.zsh;
# programs.zsh = {
# shellAliases = {
# #.. = "cd ..";
# h = "cd ~";
# ls = "eza --icons -l -T -L=1";
# cat = "bat";
# };
# enable = lib.mkDefault true;
# autosuggestions.enable = lib.mkDefault true;
# };
# #oh-my-zsh
# programs.zsh.ohMyZsh = {
# enable = lib.mkDefault true;
# theme = lib.mkDefault "jtriley";
# };
# Default filesystem
# fileSystems."/" = lib.mkDefault {
# device = "/dev/disk/by-label/nixos";
# autoResize = true;
# fsType = "ext4";
# };
# system.stateVersion = lib.mkDefault "25.05";
# # Let the VM take care of its own upgrades.
# system.autoUpgrade.enable = true;
# # Clean Nix up after upgrades.
# programs.nh = {
# enable = true;
# clean.enable = true;
# clean.extraArgs = "--keep 5";
# };
#security.sudo.wheelNeedsPassword = false; # Don't ask for passwords
#services.openssh = {
# enable = true;
# settings.PasswordAuthentication = false;
# settings.KbdInteractiveAuthentication = false;
# ports = [ 2112 ];
#};
#programs.ssh.startAgent = true;
# Add an admin user
#users.users.shawnb = lib.mkDefault {
# isNormalUser = true;
# hashedPassword = "";
# description = "Shawn Borton";
# extraGroups = [ "networkmanager" "wheel" ];
# openssh.authorizedKeys.keys = [
# "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILgyiCfvu21kVTfRyKjzpH+93k7Jsf+EqLfN6NtolHnf tech@shilohsvillage.com"
# ];
#};
};
}
#+end_src

View file

@ -0,0 +1,16 @@
#+title: Gitlab
* Gitlab Configuration
#+begin_src nix :noweb yes :tangle /home/shawnb/Development/falcon_nest/crows_nest/gitlab.nix
{ config, pkgs, modulesPath, lib, system, ... }:
{
services = {
gitlab = {
enable = true;
packages.gitlab = "gitlab-ce";
statePath = "/mnt/gitlab";
};
};
}
#+end_src

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

80
literal/terraform.org Normal file
View file

@ -0,0 +1,80 @@
* Terraform Plan for Crows Nest ISO
** Prompt
Please create a Terraform plan that will allow me to provision a new virtual machine on Proxmox using the telmate Proxmox provider. This virtual machine should have 4 CPU cores, 8GB of RAM, and 64 GB of disk space. Please give it an ID of 105, set full_clone to be true, and set the node's name to roxie. Also, turn on automatic reboot of the virtual machine. Leave the following information blank so that I can fill it in later: the URL of the Proxmox instance, the user name, and the token from Proxmox. Call the VM "crows-nest"
** Result (from Claude Sonnet 4)
#+begin_src terraform :tangle /home/shawnb/Development/terraform/nixos_template_provider.tf
terraform {
required_providers {
proxmox = {
source = "telmate/proxmox"
version = "3.0.2-rc01"
}
}
}
provider "proxmox" {
pm_api_url = "https://pve.home.onionpatch.cloud:8006/api2/json"
pm_user = "terraform-prov@pve"
pm_password = "8pKkz^@C%gOYbegcGQ"
}
#+end_src
#+begin_src terraform :tangle /home/shawnb/Development/terraform/nixos_template.tf
# General
resource "proxmox_vm_qemu" "nixos-template" {
name = "nixos-template"
desc = "NixOS Template"
agent = 1
target_node = "roxie"
vmid = 0
vm_state = "stopped"
onboot = false
automatic_reboot = false
# Bios and OS
bios = "seabios"
qemu_os = "l26"
tags = "nixos,server,linux"
hotplug = 0
# CPU and Memory
cpu {
cores = 1
sockets = 1
type = "host"
}
memory = 2048
balloon = 2048
# Disks
scsihw = "virtio-scsi-single"
disks {
ide {
ide2 {
cdrom {
iso = "local:iso/nixos-minimal-26.05.1947.a0374025a863-x86_64-linux.iso"
}
}
}
scsi {
scsi0 {
disk {
size = "40G"
iothread = true
replicate = false
storage = "local-lvm"
}
}
}
}
# Network
network {
id = 0
model = "virtio"
bridge = "vmbr0"
}
}
#+end_src

View file

@ -0,0 +1,79 @@
#+title: Nebula Sync
* Nebula Sync Configuration
#+begin_src nix :noweb yes :tangle ~/Development/falcon_nest/virtualization/nebula-sync-oci.nix
{ config, lib, pkgs, ... }:
{
<<nebula-user>>
<<sops>>
<<oci-container>>
}
#+end_src
** Version number
I'm putting the version number at the start of this file just below the configuration file skeleton. The goal is to receive an update that a new version is ready, then update this version number to the new version and tangle the code block. Then, commit both this Org file and the generated file to Git for the pipeline to run.
You may ask, why not just use the "latest" tag? It makes sense. If I'm updating the container image manually, I wouldn't need to change anything, just run `nixos-rebuild-switch` when I get word of a new release. The virtue of Nix and NixOS, however, is that I can make my setup reproducible with respects to not only the NixOS version, but the Docker image version. Plus, it provides a historical record of the container on Crow's Nest and Git, not to mention, if I shut down and wipe clean the container, then decide I need it again, I can go right back to the last known working version.
#+NAME: version-number
#+begin_src nix :noweb-ref version-number
0.11.2
#+end_src
** Set up the nebula user
Each container on this system gets its own user.
#+NAME: nebula-user
#+begin_src nix :noweb-ref nebula-user
users.users.nebula = {
isSystemUser = true;
group = "podman";
home = "/var/lib/nebula";
linger = false;
createHome = true;
autoSubUidGidRange = true;
};
#+end_src
** Set up secrets
The setup for nebula-sync requires the user to put in two environment variables,
one for the primary Pi-Hole instance and one for what it calls replicas or the
secondary instances. Each of these variables is a string consisting of the IP
address followed by a pipe symbol then the admin or app password for
the instance.
Obviously, having passwords in the configuration file wouldn't do so I decided to use sops-nix to manage secrets. After a bit of finessing, it works well, you just have to use environmentFiles for any oci-containers since the end result of the secrets management is a single file with the necessary information inside and you'll need to reference that file.
#+NAME: sops
#+begin_src nix :noweb-ref sops
sops = {
secrets = {
nebula-sync-env = {
sopsFile = ../secrets/nebula-sync.env;
format = "dotenv";
owner = config.users.users.nebula.name;
};
};
};
#+end_src
** Configure the container
Then we bring it all together. I'm skipping TLS verification for now, though I plan on going back and setting up the Pi-Holes with a proper domain name and SSL certificate. Plan on being the keyphrase here.
Cron is set to run at 23 minutes past the hour. We won't do a full sync, but we'll ensure gravity is updated after each run.
#+NAME: oci-container
#+begin_src nix :noweb yes :noweb-ref oci-container
virtualisation.oci-containers.containers.nebula-sync = {
autoRemoveOnStop = false;
autoStart = false;
image = "ghcr.io/lovelaze/nebula-sync:<<version-number>>";
environment = {
FULL_SYNC = "false";
RUN_GRAVITY = "true";
CRON = "23 * * * *";
CLIENT_SKIP_TLS_VERIFICATION = "true";
};
environmentFiles = [ config.sops.secrets.nebula-sync-env.path ];
podman = { user = "nebula"; };
log-driver = "journald";
};
#+end_src

View file

@ -0,0 +1,156 @@
* Setting up Podman and Portainer
** Set up Podman :factcheck:
#+begin_src nix :noweb yes :tangle ~/Development/falcon_nest/virtualization/podman-utils.nix
{ pkgs, lib, config, ... }: {
<<podman-utils>>
}
#+end_src
NixOS seems to jive better with Podman, probably due to its ability to run without a daemon, which usually needs root access.
I'll be looking to run several containers the NixOS way. What does that mean? Well, no yaml, just Nix. Nix to declare the containers and Nix to declare the systemd scripts that help launch and scrap containers. However, there are some configuration options that we need to set for the VM that will host them.
First, I'll enable podman and specify it as the backend...:
#+begin_src nix
virtualisation.podman.enable = true;
virtualisation.oci-containers.backend = "podman";
#+end_src
...and enable some sane defaults, like ensuring compatibility with Docker (so 3rd-party images play nice) and enabling automatic pruning of old images:
#+begin_src nix
virtualisation.podman.autoPrune.enable = true;
virtualisation.podman.dockerCompat = true;
#+end_src
Then I'll set up DNS for all Podman networks:
#+begin_src nix
networking.firewall.interfaces = let
matchAll =
if !config.networking.nftables.enable then "podman+" else "podman*";
in { "${matchAll}".allowedUDPPorts = [ 53 ]; };
#+end_src
Here's the completed code:
#+begin_src nix :noweb-ref podman-utils
virtualisation = {
oci-containers.backend = "podman";
podman = {
enable = true;
autoPrune = {
enable = true;
flags = [ "--all" "--filter 'until=24h'" "--force" ];
};
dockerCompat = true;
};
};
networking.firewall.interfaces = let
matchAll =
if !config.networking.nftables.enable then "podman+" else "podman*";
in { "${matchAll}".allowedUDPPorts = [ 53 ]; };
# If you want to trigger this service manually, run sudo systemctl restart update-containers.service.
systemd.services.update-containers = {
serviceConfig = {
Type = "oneshot";
ExecStart = lib.getExe (pkgs.writeShellScriptBin "update-containers" ''
images=$(${pkgs.podman}/bin/podman ps -a --format="{{.Image}}" | sort -u)
for image in $images; do
${pkgs.podman}/bin/podman pull "$image"
done
'');
};
};
#+end_src
** Set up Portainer
#+begin_src nix :noweb yes :tangle ~/Development/falcon_nest/virtualization/portainer.nix
{ pkgs, lib, config, ... }: {
<<portainer-config>>
}
#+end_src
This virtual machine will feature a single Portainer instance to act as "mission control" for my Debian-based fileserver and a Raspberry Pi running some public-facing utilities that run a variety of Docker containers.
I want to be careful to run this instance properly and securely. I'll need to create a user for the Portainer container.
#+begin_src nix :noweb-ref portainer-config
users.users.portainer = {
isSystemUser = true;
group = "podman";
home = "/var/lib/portainer";
createHome = true;
};
#+end_src
Now for the actual container configuration, and this is courtesy of a great tool called [[https://github.com/aksiksi/compose2nix][compose2nix]]. compose2nix takes your standard Docker Compose YAML file and generates a configuration for the container.
#+begin_src nix :noweb-ref portainer-config
virtualisation.oci-containers.containers."portainer" = {
podman.user = "portainer";
image = "portainer/portainer-ce:lts";
volumes = [
"/var/run/docker.sock:/var/run/docker.sock:rw"
"portainer_portainer_data:/data:rw"
];
ports = [ "8000:8000/tcp" "9443:9443/tcp" "9000:9000/tcp" ];
log-driver = "journald";
extraOptions =
[ "--network-alias=portainer" "--network=portainer_default" ];
};
#+end_src
compose2nix also takes care of setting up the systemd service that's responsible for restarting the container when necessary, as well as setting up the volumes and networks the container will use.
#+begin_src nix :noweb-ref portainer-config
# Networks
systemd.services."podman-network-portainer_default" = {
path = [ pkgs.podman ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStop = "podman network rm -f portainer_default";
};
script = ''
podman network inspect portainer_default || podman network create portainer_default
'';
partOf = [ "podman-compose-portainer-root.target" ];
wantedBy = [ "podman-compose-portainer-root.target" ];
};
# Volumes
systemd.services."podman-volume-portainer_portainer_data" = {
path = [ pkgs.podman ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
podman volume inspect portainer_portainer_data || podman volume create portainer_portainer_data
'';
partOf = [ "podman-compose-portainer-root.target" ];
wantedBy = [ "podman-compose-portainer-root.target" ];
};
# Root service
# When started, this will automatically create all resources and start
# the containers. When stopped, this will teardown all resources.
systemd.targets."podman-compose-portainer-root" = {
unitConfig = { Description = "Root target generated by compose2nix."; };
wantedBy = [ "multi-user.target" ];
};
#+end_src
All that's left is to import these into this VM's configuration:
#+begin_src nix
imports = [
../virtualization/podman-utils.nix
../virtualization/portainer.nix
];
#+end_src

View file

@ -0,0 +1,104 @@
{ config, pkgs, modulesPath, lib, system, ... }:
{
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
(modulesPath + "/profiles/minimal.nix")
../virtualization/podman-utils.nix
../virtualization/portainer.nix
];
config = {
#Provide a default hostname
networking.hostName = lib.mkDefault "crowsnest";
# Activate firewall
networking.firewall.enable = true;
# Open firewall ports for Portainer
networking.firewall.allowedTCPPorts = [ 2112 8000 9443 9000 ];
# Time Zone
time.timeZone = "America/Chicago";
# Enable QEMU Guest for Proxmox
services.qemuGuest.enable = lib.mkDefault true;
# Use the boot drive for grub
boot.loader.grub.enable = lib.mkDefault true;
boot.loader.grub.devices = [ "nodev" ];
boot.growPartition = lib.mkDefault true;
# Allow remote updates with flakes and non-root users
nix.settings.trusted-users = [ "root" "@wheel" ];
nix.settings.experimental-features = [ "nix-command" "flakes" ];
# Some sane packages we need on every system
environment.systemPackages = with pkgs; [
vim # for emergencies
git # for pulling nix flakes
zsh
bat
eza
];
environment.shells = with pkgs; [ zsh ];
users.defaultUserShell = pkgs.zsh;
programs.zsh = {
shellAliases = {
#.. = "cd ..";
h = "cd ~";
ls = "eza --icons -l -T -L=1";
cat = "bat";
};
enable = lib.mkDefault true;
autosuggestions.enable = lib.mkDefault true;
};
#oh-my-zsh
programs.zsh.ohMyZsh = {
enable = lib.mkDefault true;
theme = lib.mkDefault "jtriley";
};
# Default filesystem
fileSystems."/" = lib.mkDefault {
device = "/dev/disk/by-label/nixos";
autoResize = true;
fsType = "ext4";
};
system.stateVersion = lib.mkDefault "25.05";
# Let the VM take care of its own upgrades.
system.autoUpgrade.enable = true;
# Clean Nix up after upgrades.
programs.nh = {
enable = true;
clean.enable = true;
clean.extraArgs = "--keep 5";
};
security.sudo.wheelNeedsPassword = false; # Don't ask for passwords
services.openssh = {
enable = true;
settings.PasswordAuthentication = false;
settings.KbdInteractiveAuthentication = false;
ports = [ 2112 ];
};
programs.ssh.startAgent = true;
# Add an admin user
users.users.shawnb = lib.mkDefault {
isNormalUser = true;
hashedPassword = "";
description = "Shawn Borton";
extraGroups = [ "networkmanager" "wheel" ];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILgyiCfvu21kVTfRyKjzpH+93k7Jsf+EqLfN6NtolHnf tech@shilohsvillage.com"
];
};
};
}

10
secrets/nebula-sync.env Normal file
View file

@ -0,0 +1,10 @@
PRIMARY=ENC[AES256_GCM,data:96Sq3dCEKfQPy65Dw92GCLCza09OP9eowji8tcAE7iREQaGsYW4qmPNwWLoP+NFtp3VX1nda8SqZRnC2fQ==,iv:uyL5AEN27b/+f+wvb0xZF+EMH5NA+K5mbKO13DLYeEU=,tag:nk9ljB41Lkc1SycbP81GGQ==,type:str]
REPLICAS=ENC[AES256_GCM,data:J/iYdZb/EZUJf3jU2+5ADGrsqxUYchYV6mBb1VpzfJ0t+Suzf7/B2PIfXEBlFOi3RoG69Htq9zajMS70RYZE,iv:/HywKT8e7NTbaROSOvtQ+SEMCCDxvzmjy14Z4DADwgM=,tag:f34kf4nIGWXg0+5Wj88tXQ==,type:str]
sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwUUx5bzI3aFdHSFljaWl1\nRlo2Qk9HM2l4YXN1ejkyUUtsSU1tb1NTQTJRClNpTGx3Nmxmdm90M2ptcFhLa0JD\nckxzNnJLanljMDBleFBtVEpCVEFWRlUKLS0tIHB1V1c3eDUzczRuMnVxWXBycTJm\nckhoWVJDTlJ6ampCeFVJR2VYOFpIU00K9h2HuDfPbFFw3nWOIEyzwWveR6jobhQC\nyaAEfj/MSK5WCJ4/4W5rP/CIP+UYpRW/6zY4UxuLjFMNooINDjw6kw==\n-----END AGE ENCRYPTED FILE-----\n
sops_age__list_0__map_recipient=age1z7h49dlulj2lut20dk9h0wqmkf5l3m30fzcf0yq7mqhhhlnnjf3q5jwd2a
sops_age__list_1__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYdlA1aGxON240V0NUS016\ncnQzRzBPbWU0ZXQ5WTFRN0g2SDNXdzdaRlFFClBSSjNpN3Q4dVAxdDlRcHpKMUdR\nUHNkQXdUQWRVRkd6UGxTdXAwbXpzV0UKLS0tIFhIbVpJQTE1d2sraTVhelBXeVdY\nVnkxV0NNNUlUNHFhZ1NBaERXK0M4bk0KDj0EJa6I3DhOpdVld0MOPRUrFKX/UxnM\nMox7wqXgX5X8Zzu/rPhPrJ5Vpo5mvSdNnuaKs84SZBjicV2hpzrEGw==\n-----END AGE ENCRYPTED FILE-----\n
sops_age__list_1__map_recipient=age17dyle4u3ltd0fj0g8e443tyyevydr6gv0s2vrfvdm94n0glx0ffq3tz0n6
sops_lastmodified=2026-05-20T19:11:02Z
sops_mac=ENC[AES256_GCM,data:ncVj4XrhtT268xaVXd37VyQcSyhv1PE3KETz56y6dTPRngnPh7plcr0MGpYDH1xpfesbYWkocmwjOD0OhkaPiJb8WxHzgeSG96SxKuh/3oCSXN6FQl0Gt8HVtRXAjfUZ7Ctw8GtZXDyFbj7/9PoJd6PtSmmsm+ogRswHrMPKpeY=,iv:FAoKLyE/W570ViqZCvwyIfQhpRF5SDTt73DvhIZmsXQ=,tag:ViG3DFQ6YGBARMIif42A6g==,type:str]
sops_unencrypted_suffix=_unencrypted
sops_version=3.12.2

28
secrets/secrets.yaml Normal file
View file

@ -0,0 +1,28 @@
#ENC[AES256_GCM,data:Zdj5rouFv4JDxU+1MeWOMf+IK2Pxw4q6q6h6BbKmB1HQDK3QoG0=,iv:fhmLZkxQPz6lJ6LLG9v4NMDzTvxqazsu61346vyfaW4=,tag:KpkXU9WEfAB6B9Oqnx/H9g==,type:comment]
example-key: ENC[AES256_GCM,data:L21aO2kz7H9Zclh3Sw==,iv:aeXEz6POmus+f2Gr9YtYUY+3tIXoGgH60AnnQ8F2hUg=,tag:Up6djzxD87QjYOKB4Lxj5g==,type:str]
#ENC[AES256_GCM,data:PAeJNybdiOuX3+cnw7Lox12yhibHcSP4214pLXlKSkfKtqbdzV9I67zMu2WM/v87OSbZlLclad0=,iv:iUA3Gppxphpz6bKcNSEs7SqzIs6lvXuHU7rNNTPitOs=,tag:CNrbee/QIinmxjJvwz63eQ==,type:comment]
#ENC[AES256_GCM,data:s5Yf7RjRRF5RQjS3NxQHiO6x4Ae6sykwcTCb+CLUik5FO8LX/++7SKMH6+EwyLt593Qu8zlrhabGDn2QTLzf/2PTD6KvA1jr,iv:1ejPXYU8Qxd1fejgLbbv0lbRQMYpyBd957c3cBuUbCI=,tag:6pr9h9Dn4THuQe1WxOFu1g==,type:comment]
sops:
age:
- recipient: age1z7h49dlulj2lut20dk9h0wqmkf5l3m30fzcf0yq7mqhhhlnnjf3q5jwd2a
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBleXVvb2JaOFFEUlBrZHVI
WWxoZytzTUJDb1JuSEl2aGpoaVFUT0tuRmpZCjNRWXdORFBhbU9ucS85UllkVTlE
RFZQNEhEbXo1dTZ4aWU3eEkyN0JWVzQKLS0tIGtzNjc0Ny95ODFJeVN5OGg4MzdF
cndKUmxoMEZYcklGMEhRaWRia2dsMXMKtdvO/AayzdQXRUxRTIHx0Tn83ERnVkoZ
SvEB+1atuRkEOBSna3WnX1aFXQIh1wFW4gkRE4ciONJmYlcWykvWJw==
-----END AGE ENCRYPTED FILE-----
- recipient: age17dyle4u3ltd0fj0g8e443tyyevydr6gv0s2vrfvdm94n0glx0ffq3tz0n6
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxaC9RUDZtMjdoZHhiKzJT
U1hMSXBXYjVFTFpWU0t6OHR4cHc1bHZGMlNrClVkQWtCZWpxK051MDl3Zk55bW5O
L3RHQjFrb2s0L3BNbnFWV1VYWjdMTncKLS0tIFVNRVBjOHdNc0RpVGxkWnJWVjYx
UzdCT0xnUGRwK1BRdjB0eEw2S0ZJakkKVajLoknMuzvE4wnKg+FsbLi/QwHRjuJL
M9imW/4ReNAwjmxXkSBkatwQOMiEB0/mTCFN9/gYvMXF4EYYq4Msqg==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-05-20T16:06:53Z"
mac: ENC[AES256_GCM,data:u1uI1FJugMRoNE0DfpIP6j1XQeSjhMl1yQCMg18PhxYG+FYyBzvStbzCj9koelpEzpTIZff/V6d4C1JLPreQ+dyQNJ6qdEfHihQ6TlEQD9Mn9TIYp4GN3gvIbQtCNylA5Ucnp9lvMPPBTwcnyNcXtNf1wI5X+fWmUJzH4XpQFKM=,iv:h3Blc9AwPND2VDIJDVLBRILhxWYb0YUzxcLZbn0rekk=,tag:Vhv2qtSok1t1IqDEZ7aixA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.12.2

4341
session-ses_1ce4.md Normal file

File diff suppressed because it is too large Load diff

3
shell.nix Normal file
View file

@ -0,0 +1,3 @@
{ pkgs ? import <nixpkgs> { } }:
pkgs.mkShell { packages = [ pkgs.nil ]; }

1
sops/age/keys.txt Normal file
View file

@ -0,0 +1 @@
AGE-SECRET-KEY-1NKMLAGCA0JDEXQQZEC2V8DURYEYF2F2VETZPG0T9JM6EA5J4D0DSPQZ2QD

24
terraform/.terraform.lock.hcl generated Normal file
View file

@ -0,0 +1,24 @@
# This file is maintained automatically by "tofu init".
# Manual edits may be lost in future updates.
provider "registry.opentofu.org/telmate/proxmox" {
version = "3.0.2-rc01"
constraints = "3.0.2-rc01"
hashes = [
"h1:571ROPuTMC0w5lr9hbUXi7NVLsG3SpmZxXXZx8cAT+Q=",
"zh:34d264243a4513f4e30c01fb37cc6a3e592d7823dfd182c5edfb170ac7b7de3a",
"zh:544428311ad20fbb3ad2cd854e893bbf036023cb57c3acc5093d141976dac670",
"zh:5c2396b328edee8de7ac144c15a6b7e668e81063699bc8c110d7c39fb8da70e9",
"zh:5ca8e33476ad06a0259071120a59477e8f107f30c1178ea7b9f6cafe1a461ade",
"zh:5ea56eb8275edc754a01a0180750e9c939cd997d3a50659617770211f4337da9",
"zh:9dd3482df6bbe00a4a6152be3567b6c08d35c3644a327a1f5ac30fd95ccd449f",
"zh:a76075fafadcc94a825151aff169bae4e0c05e3c7717e16dcdcf16ffa61a0780",
"zh:b1d95f97b22f671db762f7adf428b409e6736c078bcf267d8391985b8847d6e3",
"zh:cc94255cd1b18e6a341c15089015c457c8c639c25c426b07f278d5ea9850b3b5",
"zh:ce991103cb69b0b3e275127e3ab92c88bb3b6b0f4e5a2cb082aeaef70a7f7d61",
"zh:d24838bce87b38e12544a1329f5ad30e2be045968e639a3f4ddd5c84aa648e04",
"zh:e106ebd4eea8d62d62e62f261a262febc615e17466b54ac18f7e65c7e79e0008",
"zh:e254ca76c95e6e92da973b7bddc36bfa0a1e31d7c7e758ef4b01315db969388b",
"zh:f1d1d5f4c39267cacebe0ab7e9e06caf9692707f3b5369685541b65bc8b840ce",
]
}

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017 <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,51 @@
[![Build Status](https://travis-ci.com/Telmate/terraform-provider-proxmox.svg?branch=master)](https://travis-ci.com/Telmate/terraform-provider-proxmox)
# Terraform provider plugin for Proxmox
This repository provides a Terraform provider for
the [Proxmox virtualization platform](https://pve.proxmox.com/pve-docs/) and exposes Terraform resources to provision
QEMU VMs and LXC Containers.
## Getting Started
In order to get started, use [the documentation included in this repository](docs/index.md). The documentation contains
a list of the options for the provider. Moreover, there are some guides available how to combine options and start
specific VMs.
## Quick Start
Follow this [install guide](docs/guides/installation.md) to install the plugin.
## Known Limitations
* `proxmox_vm_qemu`.`disk`.`size` attribute does not match what is displayed in the Proxmox UI.
* Updates to `proxmox_vm_qemu` resources almost always result as a failed task within the Proxmox UI. This appears to be
harmless and the desired configuration changes do get applied.
* When using the `proxmox_lxc` resource, the provider will crash unless `rootfs` is defined.
* When using the Network Boot mode (PXE), a valid NIC must be defined for the VM, and the boot order must specify network first.
## Contributing
When contributing, please also add documentation to help other users.
### Debugging the provider
Debugging is available for this provider through the Terraform Plugin SDK versions 2.0.0. Therefore, the plugin can be
started with the debugging flag `--debug`.
For example (using [delve](https://github.com/go-delve/delve) as Debugger):
```bash
dlv exec --headless ./terraform-provider-my-provider -- --debug
```
For more information about debugging a provider please
see: [Debugger-Based Debugging](https://www.terraform.io/docs/extend/debugging.html#debugger-based-debugging)
## Useful links
* [Proxmox](https://www.proxmox.com/en/)
* [Proxmox documentation](https://pve.proxmox.com/pve-docs/)
* [Terraform](https://www.terraform.io/)
* [Terraform documentation](https://www.terraform.io/docs/index.html)
* [Recommended ISO builder](https://github.com/Telmate/terraform-ubuntu-proxmox-iso)

57
terraform/crows_nest.tf Normal file
View file

@ -0,0 +1,57 @@
# General
resource "proxmox_vm_qemu" "crows-nest" {
name = "crows-nest"
desc = "Automation platform with Portainer, Gitlab, and Ansible"
agent = 1
target_node = "roxie"
vmid = 0
vm_state = "stopped"
onboot = true
automatic_reboot = true
# Bios and OS
bios = "seabios"
qemu_os = "l26"
tags = "nixos,server,linux"
hotplug = 0
# CPU and Memory
cpu {
cores = 4
sockets = 1
type = "host"
}
memory = 8192
balloon = 8192
# Disks
scsihw = "virtio-scsi-single"
disks {
ide {
ide2 {
cdrom {
iso = "local:iso/nixos-25.05.20250816-x86_64-linux.iso"
}
}
}
scsi {
scsi0 {
disk {
size = "96G"
iothread = true
replicate = false
storage = "local-lvm"
}
}
}
}
# Network
network {
id = 0
model = "virtio"
bridge = "vmbr0"
}
}

57
terraform/hermes_agent.tf Normal file
View file

@ -0,0 +1,57 @@
# General
resource "proxmox_vm_qemu" "hermes-agent" {
name = "hermes-agent"
desc = "An agentic AI assistant for homelab maintenance and research assistance"
agent = 1
target_node = "roxie"
vmid = 0
vm_state = "stopped"
onboot = true
automatic_reboot = true
# Bios and OS
bios = "seabios"
qemu_os = "l26"
tags = "nixos,server,linux"
hotplug = 0
# CPU and Memory
cpu {
cores = 1
sockets = 1
type = "host"
}
memory = 2048
balloon = 2048
# Disks
scsihw = "virtio-scsi-single"
disks {
ide {
ide2 {
cdrom {
iso = "local:iso/nixos-25.05.20250816-x86_64-linux.iso"
}
}
}
scsi {
scsi0 {
disk {
size = "40G"
iothread = true
replicate = false
storage = "local-lvm"
}
}
}
}
# Network
network {
id = 0
model = "virtio"
bridge = "vmbr0"
}
}

View file

@ -0,0 +1,14 @@
terraform {
required_providers {
proxmox = {
source = "telmate/proxmox"
version = "3.0.2-rc01"
}
}
}
provider "proxmox" {
pm_api_url = "https://pve.home.onionpatch.cloud:8006/api2/json"
pm_user = "terraform-prov@pve"
pm_password = "8pKkz^@C%gOYbegcGQ"
}

14
terraform/provider.tf Normal file
View file

@ -0,0 +1,14 @@
terraform {
required_providers {
proxmox = {
source = "telmate/proxmox"
version = "3.0.2-rc01"
}
}
}
provider "proxmox" {
pm_api_url = "https://pve.home.onionpatch.cloud:8006/api2/json"
pm_user = "terraform-prov@pve"
pm_password = "8pKkz^@C%gOYbegcGQ"
}

View file

@ -0,0 +1 @@
{"version":4,"terraform_version":"1.10.3","serial":5,"lineage":"0954cb2d-ea1f-6ef0-fe21-5f795adcbfb1","outputs":{},"resources":[{"mode":"managed","type":"proxmox_vm_qemu","name":"crows-nest","provider":"provider[\"registry.opentofu.org/telmate/proxmox\"]","instances":[{"schema_version":0,"attributes":{"additional_wait":5,"agent":1,"agent_timeout":90,"args":"","automatic_reboot":true,"balloon":8192,"bios":"seabios","boot":"order=scsi0;ide2;net0","bootdisk":"","ci_wait":null,"cicustom":null,"cipassword":"","ciupgrade":false,"ciuser":"","clone":null,"clone_id":null,"clone_wait":10,"cores":0,"cpu":[{"affinity":"","cores":4,"flags":[],"limit":0,"numa":false,"sockets":1,"type":"host","units":0,"vcores":0}],"cpu_type":"","current_node":"roxie","default_ipv4_address":"","default_ipv6_address":null,"define_connection_info":true,"desc":"Automation platform with Portainer, Gitlab, and Ansible","disk":[],"disks":[{"ide":[{"ide0":[],"ide1":[],"ide2":[{"cdrom":[{"iso":"local:iso/nixos-25.05.20250816-x86_64-linux.iso","passthrough":false}],"cloudinit":[],"disk":[],"ignore":false,"passthrough":[]}],"ide3":[]}],"sata":[],"scsi":[{"scsi0":[{"cdrom":[],"cloudinit":[],"disk":[{"asyncio":"","backup":true,"cache":"","discard":false,"emulatessd":false,"format":"raw","id":0,"iops_r_burst":0,"iops_r_burst_length":0,"iops_r_concurrent":0,"iops_wr_burst":0,"iops_wr_burst_length":0,"iops_wr_concurrent":0,"iothread":true,"linked_disk_id":-1,"mbps_r_burst":0,"mbps_r_concurrent":0,"mbps_wr_burst":0,"mbps_wr_concurrent":0,"readonly":false,"replicate":false,"serial":"","size":"96G","storage":"local-lvm","wwn":""}],"ignore":false,"passthrough":[]}],"scsi1":[],"scsi10":[],"scsi11":[],"scsi12":[],"scsi13":[],"scsi14":[],"scsi15":[],"scsi16":[],"scsi17":[],"scsi18":[],"scsi19":[],"scsi2":[],"scsi20":[],"scsi21":[],"scsi22":[],"scsi23":[],"scsi24":[],"scsi25":[],"scsi26":[],"scsi27":[],"scsi28":[],"scsi29":[],"scsi3":[],"scsi30":[],"scsi4":[],"scsi5":[],"scsi6":[],"scsi7":[],"scsi8":[],"scsi9":[]}],"virtio":[]}],"efidisk":[],"force_create":false,"force_recreate_on_change_of":null,"full_clone":true,"hagroup":"","hastate":"","hostpci":[],"hotplug":"0","id":"roxie/qemu/105","ipconfig0":null,"ipconfig1":null,"ipconfig10":null,"ipconfig11":null,"ipconfig12":null,"ipconfig13":null,"ipconfig14":null,"ipconfig15":null,"ipconfig2":null,"ipconfig3":null,"ipconfig4":null,"ipconfig5":null,"ipconfig6":null,"ipconfig7":null,"ipconfig8":null,"ipconfig9":null,"kvm":true,"linked_vmid":0,"machine":"","memory":8192,"name":"crows-nest","nameserver":null,"network":[{"bridge":"vmbr0","firewall":false,"id":0,"link_down":false,"macaddr":"bc:24:11:bd:e1:7a","model":"virtio","mtu":0,"queues":0,"rate":0,"tag":0}],"numa":false,"onboot":true,"os_network_config":null,"os_type":null,"pci":[],"pcis":[],"pool":"","protection":false,"pxe":null,"qemu_os":"l26","reboot_required":false,"scsihw":"virtio-scsi-single","searchdomain":null,"serial":[],"skip_ipv4":false,"skip_ipv6":false,"smbios":[{"family":"","manufacturer":"","product":"","serial":"","sku":"","uuid":"37b73bc3-23be-4684-81aa-b59da37726a0","version":""}],"sockets":0,"ssh_forward_ip":null,"ssh_host":"","ssh_port":"","ssh_private_key":null,"ssh_user":null,"sshkeys":null,"startup":"","tablet":true,"tags":"linux;nixos;server","target_node":"roxie","target_nodes":null,"timeouts":null,"tpm_state":[],"unused_disk":[],"usb":[],"usbs":[],"vcpus":0,"vga":[],"vm_state":"stopped","vmid":105},"sensitive_attributes":[[{"type":"get_attr","value":"cipassword"}],[{"type":"get_attr","value":"ssh_private_key"}]],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxMjAwMDAwMDAwMDAwLCJkZWZhdWx0IjoxMjAwMDAwMDAwMDAwLCJkZWxldGUiOjEyMDAwMDAwMDAwMDAsInJlYWQiOjEyMDAwMDAwMDAwMDAsInVwZGF0ZSI6MTIwMDAwMDAwMDAwMH19"}]}],"check_results":null}

View file

@ -0,0 +1 @@
{"version":4,"terraform_version":"1.10.3","serial":4,"lineage":"0954cb2d-ea1f-6ef0-fe21-5f795adcbfb1","outputs":{},"resources":[],"check_results":null}

View file

@ -0,0 +1,40 @@
{ config, lib, pkgs, ... }:
{
users.users.nebula = {
isSystemUser = true;
group = "podman";
home = "/var/lib/nebula";
linger = false;
createHome = true;
autoSubUidGidRange = true;
};
sops = {
secrets = {
nebula-sync-env = {
sopsFile = ../secrets/nebula-sync.env;
format = "dotenv";
owner = config.users.users.nebula.name;
};
};
};
virtualisation.oci-containers.containers.nebula-sync = {
autoRemoveOnStop = false;
autoStart = false;
image = "ghcr.io/lovelaze/nebula-sync:0.11.2";
environment = {
FULL_SYNC = "false";
RUN_GRAVITY = "true";
CRON = "23 * * * *";
CLIENT_SKIP_TLS_VERIFICATION = "true";
};
environmentFiles = [ config.sops.secrets.nebula-sync-env.path ];
podman = { user = "nebula"; };
log-driver = "journald";
};
}

View file

@ -0,0 +1,38 @@
{ config, lib, pkgs, ... }:
{
users.users.nebula = {
isSystemUser = true;
group = "podman";
home = "/var/lib/nebula";
linger = false;
createHome = true;
autoSubUidGidRange = true;
};
sops = {
secrets = {
nebula-sync-env = {
sopsFile = ../secrets/nebula-sync.env;
format = "dotenv";
owner = config.users.users.nebula.name;
};
};
};
virtualisation.oci-containers.containers.nebula-sync = {
autoRemoveOnStop = false;
autoStart = false;
image = "ghcr.io/lovelaze/nebula-sync:0.11.2";
environment = {
FULL_SYNC = "false";
RUN_GRAVITY = "true";
CRON = "23 * * * *";
CLIENT_SKIP_TLS_VERIFICATION = "true";
};
environmentFiles = [ config.sops.secrets.nebula-sync-env.path ];
podman = { user = "nebula"; };
log-driver = "journald";
};
}

View file

@ -0,0 +1,45 @@
{ pkgs, lib, config, ... }: {
virtualisation = {
containers.enable = true;
oci-containers.backend = "podman";
podman = {
enable = true;
autoPrune = {
enable = true;
flags = [ "--all" "--filter 'until=24h'" "--force" ];
};
dockerCompat = true;
};
};
networking.firewall.interfaces = let
matchAll =
if !config.networking.nftables.enable then "podman+" else "podman*";
in { "${matchAll}".allowedUDPPorts = [ 53 ]; };
# If you want to trigger this service manually, run sudo systemctl restart update-containers.service.
systemd.services.updatecontainers = {
serviceConfig = {
Type = "oneshot";
User = "portainer";
ExecStart = lib.getExe (pkgs.writeShellScriptBin "update-containers" ''
images=$(${pkgs.podman}/bin/podman ps -a --format="{{.Image}}" | sort -u)
for image in $images; do
${pkgs.podman}/bin/podman pull "$image"
done
'');
};
};
systemd.user.timers = {
updatecontainers = {
unitConfig.ConditionUser = "portainer";
timerConfig = {
Unit = "updatecontainers.service";
OnCalendar = "Sun 02:03";
};
wantedBy = [ "timers.target" ];
};
};
}

View file

@ -0,0 +1,77 @@
# Auto-generated using compose2nix v0.3.2-pre.
# nix run github:aksiksi/compose2nix -- -project=portainer -output=/home/shawnb/.dotfiles/virtualization/portainer-composer.nix
{ pkgs, lib, config, ... }:
{
# Create portainer user and add to docker group
users.users.portainer = {
isSystemUser = true;
group = "podman";
home = "/var/lib/portainer";
createHome = true;
};
# Containers
virtualisation.oci-containers.containers."portainer" = {
podman.user = "portainer";
image = "portainer/portainer-ce:lts";
volumes = [
"/var/run/docker.sock:/var/run/docker.sock:rw"
"portainer_portainer_data:/data:rw"
];
ports = [ "8000:8000/tcp" "9443:9443/tcp" "9000:9000/tcp" ];
log-driver = "journald";
extraOptions =
[ "--network-alias=portainer" "--network=portainer_default" ];
};
systemd.services."podman-portainer" = {
serviceConfig = { Restart = lib.mkOverride 90 "always"; };
after = [
"podman-network-portainer_default.service"
"podman-volume-portainer_portainer_data.service"
];
requires = [
"podman-network-portainer_default.service"
"podman-volume-portainer_portainer_data.service"
];
partOf = [ "podman-compose-portainer-root.target" ];
wantedBy = [ "podman-compose-portainer-root.target" ];
};
# Networks
systemd.services."podman-network-portainer_default" = {
path = [ pkgs.podman ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStop = "podman network rm -f portainer_default";
};
script = ''
podman network inspect portainer_default || podman network create portainer_default
'';
partOf = [ "podman-compose-portainer-root.target" ];
wantedBy = [ "podman-compose-portainer-root.target" ];
};
# Volumes
systemd.services."podman-volume-portainer_portainer_data" = {
path = [ pkgs.podman ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
podman volume inspect portainer_portainer_data || podman volume create portainer_portainer_data
'';
partOf = [ "podman-compose-portainer-root.target" ];
wantedBy = [ "podman-compose-portainer-root.target" ];
};
# Root service
# When started, this will automatically create all resources and start
# the containers. When stopped, this will teardown all resources.
systemd.targets."podman-compose-portainer-root" = {
unitConfig = { Description = "Root target generated by compose2nix."; };
wantedBy = [ "multi-user.target" ];
};
}

View file

@ -0,0 +1,47 @@
{
config,
inputs,
lib,
...
}:
{
# to enable podman & podman systemd generator
virtualisation.quadlet.enable = true;
users.users.portainer = {
autoSubUidGidRange = true;
isSystemUser = true;
group = "podman";
home = "/var/lib/portainer";
linger = true;
createHome = true;
};
home-manager.users.portainer = { pkgs, config, ... }: {
imports = [ inputs.quadlet-nix.homeManagerModules.quadlet ];
virtualisation.quadlet.containers = {
portainer = {
autoStart = true;
serviceConfig = {
RestartSec = "10";
Restart = "always";
User = "portainer";
};
containerConfig = {
cgroupsMode = "disable";
image = "docker.io/portainer/portainer-ce:lts";
logDriver = "journald";
publishPorts = [
"0.0.0.0:8080:8080"
"0.0.0.0:9000:9000"
"0.0.0.0:9443:9443"
];
userns = "keep-id";
volumes = [
"/var/run/docker.sock:/var/run/docker.sock:rw"
"portainer_portainer_data:/data:rw"
];
};
};
};
home.stateVersion = "25.05";
};
}

View file

@ -0,0 +1,81 @@
{ pkgs, lib, config, ... }: {
users.users.portainer = {
autoSubUidGidRange = true;
isSystemUser = true;
group = "podman";
home = "/var/lib/portainer";
# linger = true;
createHome = true;
};
virtualisation.oci-containers.containers.portainer = {
autoStart = true;
image = "portainer/portainer-ce:lts";
#privileged = true;
volumes = [
#"/var/run/docker.sock:/var/run/docker.sock:rw"
"/run/podman/podman.sock:/var/run/docker.sock"
"portainer_portainer_data:/data:rw"
];
ports = [ "8000:8000" "9443:9443" "9000:9000" ];
log-driver = "journald";
# extraOptions =
# [ "--network-alias=portainer" "--network=portainer_default" ];
environment = {
PUID = "997";
PGID = "997";
TZ = "America/Chicago";
};
};
# systemd.services."podman-portainer" = {
# serviceConfig = { Restart = lib.mkOverride 90 "always"; };
# after = [
# "podman-network-portainer_default.service"
# "podman-volume-portainer_portainer_data.service"
# ];
# requires = [
# "podman-network-portainer_default.service"
# "podman-volume-portainer_portainer_data.service"
# ];
# partOf = [ "podman-compose-portainer-root.target" ];
# wantedBy = [ "podman-compose-portainer-root.target" ];
# };
# # Networks
# systemd.services."podman-network-portainer_default" = {
# path = [ pkgs.podman ];
# serviceConfig = {
# Type = "oneshot";
# RemainAfterExit = true;
# ExecStop = "podman network rm -f portainer_default";
# };
# script = ''
# podman network inspect portainer_default || podman network create portainer_default
# '';
# partOf = [ "podman-compose-portainer-root.target" ];
# wantedBy = [ "podman-compose-portainer-root.target" ];
# };
# # Volumes
# systemd.services."podman-volume-portainer_portainer_data" = {
# path = [ pkgs.podman ];
# serviceConfig = {
# Type = "oneshot";
# RemainAfterExit = true;
# };
# script = ''
# podman volume inspect portainer_portainer_data || podman volume create portainer_portainer_data
# '';
# partOf = [ "podman-compose-portainer-root.target" ];
# wantedBy = [ "podman-compose-portainer-root.target" ];
# };
# # Root service
# # When started, this will automatically create all resources and start
# # the containers. When stopped, this will teardown all resources.
# systemd.targets."podman-compose-portainer-root" = {
# unitConfig = { Description = "Root target generated by compose2nix."; };
# wantedBy = [ "multi-user.target" ];
# };
}