From b97263495bf0c465c4feb08a2d9d369e530556f6 Mon Sep 17 00:00:00 2001 From: m3tam3re Date: Wed, 12 Mar 2025 14:28:01 +0100 Subject: [PATCH] playbook base initial skeleton --- flake.lock | 44 +++++++++++++ flake.nix | 39 +++++++++++ justfiles/main.just | 69 ++++++++++++++++++++ justfiles/tiers/core.just | 101 +++++++++++++++++++++++++++++ justfiles/tiers/premium.just | 0 justfiles/tiers/starter.just | 0 modules/core.nix | 95 +++++++++++++++++++++++++++ modules/hardware-configuration.nix | 26 ++++++++ modules/services.nix | 37 +++++++++++ services/baserow/default.nix | 9 +++ services/caddy/default.nix | 3 + services/core/default.nix | 28 ++++++++ services/n8n/default.nix | 9 +++ services/portainer/default.nix | 11 ++++ 14 files changed, 471 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 justfiles/main.just create mode 100644 justfiles/tiers/core.just create mode 100644 justfiles/tiers/premium.just create mode 100644 justfiles/tiers/starter.just create mode 100644 modules/core.nix create mode 100644 modules/hardware-configuration.nix create mode 100644 modules/services.nix create mode 100644 services/baserow/default.nix create mode 100644 services/caddy/default.nix create mode 100644 services/core/default.nix create mode 100644 services/n8n/default.nix create mode 100644 services/portainer/default.nix diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..64734c6 --- /dev/null +++ b/flake.lock @@ -0,0 +1,44 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1741600792, + "narHash": "sha256-yfDy6chHcM7pXpMF4wycuuV+ILSTG486Z/vLx/Bdi6Y=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "ebe2788eafd539477f83775ef93c3c7e244421d3", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-unstable": { + "locked": { + "lastModified": 1741708242, + "narHash": "sha256-cNRqdQD4sZpN7JLqxVOze4+WsWTmv2DGH0wNCOVwrWc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b62d2a95c72fb068aecd374a7262b37ed92df82b", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b62d2a95c72fb068aecd374a7262b37ed92df82b", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs", + "nixpkgs-unstable": "nixpkgs-unstable" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..7be3b00 --- /dev/null +++ b/flake.nix @@ -0,0 +1,39 @@ +# self-host-playbook-base/flake.nix +{ + description = "Base configuration for self-host-playbook"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11"; + nixpkgs-unstable.url = "github:NixOS/nixpkgs/b62d2a95c72fb068aecd374a7262b37ed92df82b"; + }; + + outputs = { + self, + nixpkgs, + nixpkgs-unstable, + }: { + nixosModules = { + default = {tier ? "starter"}: { + config, + lib, + pkgs, + ... + }: { + nixpkgs.overlays = [ + (final: _prev: { + unstable = import nixpkgs-unstable { + system = final.system; + }; + }) + ]; + imports = [ + ./modules/core.nix + (import ./modules/services.nix { + inherit lib config pkgs; + tier = tier; + }) + ]; + }; + }; + }; +} diff --git a/justfiles/main.just b/justfiles/main.just new file mode 100644 index 0000000..ac0720b --- /dev/null +++ b/justfiles/main.just @@ -0,0 +1,69 @@ +import? "/etc/self-host-playbook/tiers/core.just" +import? "/etc/self-host-playbook/tiers/starter.just" +import? "/etc/self-host-playbook/tiers/premium.just" + +@default: + #!/usr/bin/env bash + + # Function to show the header + show_header() { + gum style \ + --foreground 212 --border double --border-foreground 212 \ + --align center --width 50 --margin "1 2" --padding "1 2" \ + "πŸš€ DevOps Control Center πŸš€" + } + + # Function to create menu items with icons and descriptions + create_menu() { + echo "πŸ“Š Status - Show running docker containers" + echo "πŸ“ Logs - View container logs" + echo "πŸ’Ύ Disk Usage - Show docker disk usage" + echo "πŸ”„ Restart - Restart a specific container" + echo "🐳 Update Docker - Update Docker containers" + echo "❌ Exit" + } + + while true; do + clear + show_header + + # Show menu and get selection + choice=$(create_menu | gum choose --cursor.foreground 212 --selected.foreground 212 --header "Select an action:" --cursor "➜ ") + + # Exit if no selection + if [ -z "$choice" ]; then + exit 0 + fi + + # Process selection + case $choice in + "πŸ“Š Status"*) + just status + ;; + "πŸ“ Logs"*) + just logs + ;; + "πŸ›‘ Stop All"*) + just docker-stop-all + ;; + "πŸ—‘οΈ Prune"*) + just docker-prune + ;; + "πŸ’Ύ Disk Usage"*) + just docker-disk + ;; + "πŸ”„ Restart"*) + just docker-restart + ;; + "🐳 Update Docker"*) + just update-containers + ;; + "❌ Exit") + echo "Goodbye! πŸ‘‹" | gum style --foreground 212 + exit 0 + ;; + esac + + # Pause after command execution + gum confirm "Press Enter to continue..." --default=true --affirmative "Continue" --negative "Exit" || exit 0 + done diff --git a/justfiles/tiers/core.just b/justfiles/tiers/core.just new file mode 100644 index 0000000..1aa042c --- /dev/null +++ b/justfiles/tiers/core.just @@ -0,0 +1,101 @@ +status: + #!/usr/bin/env bash + gum style --foreground 212 --bold --border normal --align center --width 50 --margin "1 2" "πŸ“Š Running Containers" + docker ps --format "table {{"{{.Names}}\t{{.Status}}"}}" | gum table + +# Interactive logs viewer with gum +logs: + #!/usr/bin/env bash + gum style --foreground 212 --bold --border normal --align center --width 50 --margin "1 2" "πŸ“ Docker Logs Viewer" + + # Get running container names + containers=($(docker ps --format "{{"{{.Names}}"}}")) + + if [ ${#containers[@]} -eq 0 ]; then + gum style --foreground 1 "⚠️ No running containers found" + exit 1 + fi + + # Select container using gum choose + container=$(printf "%s\n" "${containers[@]}" | gum choose --header "Select a container:" --cursor.foreground 212) + + if [ -z "$container" ]; then + exit 0 + fi + + # Select number of lines using gum choose + lines=$(gum choose --header "Select number of log lines:" --cursor.foreground 212 \ + "5 lines" "10 lines" "25 lines" "50 lines" "100 lines" "200 lines") + + if [ -z "$lines" ]; then + exit 0 + fi + + # Extract number from selection + lines=${lines%% *} + + # Show spinner while fetching logs + gum spin --spinner dot --title "Fetching logs..." -- sleep 1 + + # Show logs + docker logs "$container" 2>&1 | tail -n "$lines" | gum pager + +docker-disk: + #!/usr/bin/env bash + gum style --foreground 212 --bold --border normal --align center --width 50 --margin "1 2" "πŸ’Ύ Docker Disk Usage" + docker system df | gum table + +docker-restart: + #!/usr/bin/env bash + containers=($(docker ps --format "{{"{{.Names}}"}}")) + if [ ${#containers[@]} -eq 0 ]; then + gum style --foreground 1 "⚠️ No running containers found" + exit 1 + fi + container=$(printf "%s\n" "${containers[@]}" | gum choose --header "Select a container to restart:" --cursor.foreground 212) + if [ -n "$container" ]; then + gum spin --spinner dot --title "Restarting $container..." -- docker restart "$container" + gum style --foreground 212 "βœ… Container $container restarted successfully!" + fi + +update-containers: + #!/usr/bin/env bash + set -e # Exit on error + CONTAINERS=($(docker ps --format "{{"{{.Names}}"}}")) + + echo "Will update these containers:" + printf '%s\n' "${CONTAINERS[@]}" | gum table && \ + gum confirm "Continue?" || exit 0 + + # First collect all image information + declare -A CONTAINER_IMAGES + echo "Collecting image information..." + for container in "${CONTAINERS[@]}"; do + FULL_IMAGE=$(docker inspect "$container" --format '{{"{{.Config.Image}}"}}') + CONTAINER_IMAGES[$container]=$(echo "$FULL_IMAGE" | sed 's/@sha256.*$//') + echo "$container -> ${CONTAINER_IMAGES[$container]}" + done + + echo "Stopping containers..." && \ + for container in "${CONTAINERS[@]}"; do + echo "Stopping $container..." + sudo systemctl stop "docker-$container.service" + done + + echo "Pulling new images..." && \ + for container in "${CONTAINERS[@]}"; do + IMAGE="${CONTAINER_IMAGES[$container]}" + echo -e "\nπŸ“₯ Pulling $IMAGE for $container..." | gum style --foreground 99 + if ! docker pull "$IMAGE" --quiet=false; then + echo "❌ Failed to pull $IMAGE" | gum style --foreground 196 + exit 1 + fi + echo "------------------------" + done + + echo "Starting containers..." && \ + for container in "${CONTAINERS[@]}"; do + echo "Starting $container..." + sudo systemctl start "docker-$container.service" + done && \ + gum style --foreground 212 "βœ… Containers updated successfully!" diff --git a/justfiles/tiers/premium.just b/justfiles/tiers/premium.just new file mode 100644 index 0000000..e69de29 diff --git a/justfiles/tiers/starter.just b/justfiles/tiers/starter.just new file mode 100644 index 0000000..e69de29 diff --git a/modules/core.nix b/modules/core.nix new file mode 100644 index 0000000..c48925d --- /dev/null +++ b/modules/core.nix @@ -0,0 +1,95 @@ +{pkgs, ...}: { + imports = [ + ./hardware-configuration.nix + ]; + + # Enable flakes and nix commands + nix = { + settings = { + experimental-features = ["nix-command" "flakes"]; + # Enable automatic garbage collection + auto-optimise-store = true; + }; + # Automatic cleanup of old generations + gc = { + automatic = true; + dates = "weekly"; + options = "--delete-older-than 30d"; + }; + }; + + # Boot configuration + boot.loader.grub = { + enable = true; + efiSupport = true; + efiInstallAsRemovable = true; + }; + + # Your base configuration here + system.autoUpgrade = { + enable = true; + allowReboot = true; + dates = "04:00"; + flake = "path:/etc/nixos/current"; + randomizedDelaySec = "45min"; + flags = [ + "--update-input nixpkgs" + "--update-input base-config" + ]; + }; + + # Other base configurations... + services.openssh = { + enable = true; + settings = { + PermitRootLogin = "no"; + PasswordAuthentication = false; + MaxAuthTries = 3; + LoginGraceTime = "30s"; + }; + ports = [2222]; + }; + + # System packages + environment.systemPackages = with pkgs; [ + # Docker tools + docker + docker-compose + # System utilities + neovim + git + unstable.gum + just + jq + (pkgs.writeShellScriptBin "shp" '' + exec sudo ${pkgs.just}/bin/just -f /etc/self-host-playbook/justfile "$@" + '') + ]; + + # Enable Docker with recommended settings + virtualisation = { + docker = { + enable = true; + # Enable docker daemon to start on boot + enableOnBoot = true; + # Use overlay2 storage driver + storageDriver = "overlay2"; + # Enable live restore + liveRestore = true; + }; + oci-containers = { + backend = "docker"; + }; + }; + + environment.etc = { + # Main justfile + "self-host-playbook/justfile".source = ../justfiles/main.just; + + # Tier justfiles + "self-host-playbook/tiers/core.just".source = ../justfiles/tiers/core.just; + "self-host-playbook/tiers/starter.just".source = ../justfiles/tiers/starter.just; + }; + # System state version (do not change) + system.stateVersion = "24.11"; +} diff --git a/modules/hardware-configuration.nix b/modules/hardware-configuration.nix new file mode 100644 index 0000000..4daad50 --- /dev/null +++ b/modules/hardware-configuration.nix @@ -0,0 +1,26 @@ +# Do not modify this file! It was generated by β€˜nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ + lib, + modulesPath, + ... +}: { + imports = [ + (modulesPath + "/profiles/qemu-guest.nix") + ]; + + boot.initrd.availableKernelModules = ["ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod"]; + boot.initrd.kernelModules = []; + boot.kernelModules = []; + boot.extraModulePackages = []; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.ens18.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} diff --git a/modules/services.nix b/modules/services.nix new file mode 100644 index 0000000..e1667d9 --- /dev/null +++ b/modules/services.nix @@ -0,0 +1,37 @@ +# modules/services.nix +{ + config, + lib, + tier ? "starter", + ... +}: +with lib; let + tiers = { + starter = { + services = ["portainer" "caddy" "n8n"]; + description = "Basic management tools"; + }; + premium = { + services = ["portainer" "caddy" "n8n" "baserow"]; + description = "Automation and database tools"; + }; + }; +in { + imports = + map + (serviceName: import ../services/${serviceName}) + tiers.${tier}.services; + + options.services.selfHostPlaybook = { + enable = mkEnableOption "self host playbook"; + tier = mkOption { + type = types.enum ["starter" "premium"]; + default = "starter"; + description = "Service tier to enable"; + }; + }; + + config = mkIf config.services.selfHostPlaybook.enable { + # Add any additional configuration here if needed + }; +} diff --git a/services/baserow/default.nix b/services/baserow/default.nix new file mode 100644 index 0000000..369af4e --- /dev/null +++ b/services/baserow/default.nix @@ -0,0 +1,9 @@ +{ + virtualisation.oci-containers.containers.baserow = { + image = "docker.io/baserow/baserow:latest"; + environmentFiles = ["/etc/environment-files/baserow.env"]; + ports = ["127.0.0.1:3000:80"]; + volumes = ["baserow_data:/baserow/data"]; + extraOptions = ["--network=web"]; + }; +} diff --git a/services/caddy/default.nix b/services/caddy/default.nix new file mode 100644 index 0000000..84fb950 --- /dev/null +++ b/services/caddy/default.nix @@ -0,0 +1,3 @@ +{ + services.caddy.enable = true; +} diff --git a/services/core/default.nix b/services/core/default.nix new file mode 100644 index 0000000..d8216db --- /dev/null +++ b/services/core/default.nix @@ -0,0 +1,28 @@ +{ + imports = [ + ./baserow + ./n8n + ./portainer + ]; + + virtualisation.oci-containers.backend = "docker"; + + systemd.services.docker-network-web = { + description = "Create Docker Network Web"; + requires = ["docker.service"]; + after = ["docker.service"]; + wantedBy = ["multi-user.target"]; + + # Run on startup if network doesn't exist + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + + script = '' + if ! /run/current-system/sw/bin/docker network ls | grep -q 'web'; then + /run/current-system/sw/bin/docker network create web + fi + ''; + }; +} diff --git a/services/n8n/default.nix b/services/n8n/default.nix new file mode 100644 index 0000000..67264a5 --- /dev/null +++ b/services/n8n/default.nix @@ -0,0 +1,9 @@ +{ + virtualisation.oci-containers.containers.n8n = { + image = "docker.io/n8nio/n8n:latest"; + environmentFiles = ["/etc/environment-files/n8n.env"]; + ports = ["127.0.0.1:5678:5678"]; + volumes = ["n8n_data:/home/node/.n8n"]; + extraOptions = ["--network=web"]; + }; +} diff --git a/services/portainer/default.nix b/services/portainer/default.nix new file mode 100644 index 0000000..68020ca --- /dev/null +++ b/services/portainer/default.nix @@ -0,0 +1,11 @@ +{ + virtualisation.oci-containers.containers.portainer = { + image = "docker.io/portainer/portainer-ce:latest"; + ports = ["127.0.0.1:9000:9000"]; + volumes = [ + "/etc/localtime:/etc/localtime:ro" + "/var/run/docker.sock:/var/run/docker.sock:ro" + "portainer_data:/data" + ]; + }; +}