+flake template

This commit is contained in:
m3tam3re 2025-02-18 08:50:17 +01:00
commit 8f23618b47
11 changed files with 614 additions and 0 deletions

29
flake.nix Normal file
View File

@ -0,0 +1,29 @@
{
description = "Self Host Playbook!";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = {nixpkgs, ...}: let
systems = [
"aarch64-linux"
"i686-linux"
"x86_64-linux"
"aarch64-darwin"
"x86_64-darwin"
];
forAllSystems = nixpkgs.lib.genAttrs systems;
in {
templates = {
starter = {
description = ''
Description here!
'';
path = ./starter;
};
};
formatter =
forAllSystems (system: nixpkgs.legacyPackages.${system}.alejandra);
};
}

0
starter/README.md Normal file
View File

0
starter/config.json Normal file
View File

184
starter/configuration.nix Normal file
View File

@ -0,0 +1,184 @@
{
lib,
pkgs,
...
}:
# Read configuration from JSON
let
jsonConfig = builtins.fromJSON (builtins.readFile ./config.json);
in {
imports = [
./disko-config.nix
./hardware-configuration.nix
];
# Enable flakes and nix commands
nix = {
settings = {
experimental-features = ["nix-command" "flakes"];
# Enable automatic garbage collection
auto-optimise-store = true;
trusted-users = [jsonConfig.username];
};
# Automatic cleanup of old generations
gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 30d";
};
};
# Boot configuration
boot.loader.grub = {
enable = true;
devices = ["/dev/sda"];
efiSupport = true;
efiInstallAsRemovable = true;
};
# Networking
networking = {
firewall = {
enable = true;
# Only allow necessary ports
allowedTCPPorts = [80 443 2222]; # HTTP, HTTPS, and SSH
};
};
environment.etc = {
environment-files = {
source = pkgs.copyPathToStore ./env;
};
};
# System packages
environment.systemPackages = with pkgs; [
# System utilities
neovim
git
# Docker tools
docker
docker-compose
];
# 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;
};
# Services configuration
services = {
# SSH server configuration
openssh = {
enable = true;
settings = {
PermitRootLogin = "no";
PasswordAuthentication = false;
# Additional security settings
MaxAuthTries = 3;
LoginGraceTime = "30s";
};
ports = [2222];
};
# Caddy configuration with security headers
caddy = {
enable = true;
virtualHosts = {
"${jsonConfig.domains.portainer}" = {
extraConfig = ''
reverse_proxy localhost:9000
header {
# Security headers
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
}
'';
};
"${jsonConfig.domains.n8n}" = {
extraConfig = ''
reverse_proxy localhost:5678
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
}
'';
};
"${jsonConfig.domains.baserow}" = {
extraConfig = ''
reverse_proxy localhost:3000
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
}
'';
};
};
};
};
# User configuration
users.users.${jsonConfig.username} = {
isNormalUser = true;
extraGroups = ["wheel" "docker"];
hashedPassword = jsonConfig.hashedPassword;
openssh.authorizedKeys.keys = [jsonConfig.sshKey];
# Set default shell to bash
shell = pkgs.bash;
};
# Container configurations
virtualisation.oci-containers = {
backend = "docker";
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"
];
extraOptions = [
"--network=web"
];
};
"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"];
};
"baserow" = {
image = "docker.io/baserow/baserow:1.30.1";
environmentFiles = ["/etc/environment-files/baserow.env"];
ports = ["127.0.0.1:3000:80"];
volumes = ["baserow_data:/baserow/data"];
extraOptions = ["--network=web"];
};
};
};
system.activationScripts.createDockerNetworkWeb = lib.mkAfter ''
if ! /run/current-system/sw/bin/docker network ls | /run/current-system/sw/bin/awk '{print $2}' | /run/current-system/sw/bin/grep -q 'web'; then
/run/current-system/sw/bin/docker network create web
fi
'';
# System state version (do not change)
system.stateVersion = "24.11";
}

41
starter/disko-config.nix Normal file
View File

@ -0,0 +1,41 @@
let
jsonConfig = builtins.fromJSON (builtins.readFile ./config.json);
in {
disko.devices = {
disk = {
main = {
type = "disk";
device = jsonConfig.rootDevice;
content = {
type = "gpt";
partitions = {
bios = {
size = "1M";
type = "EF02"; # for GRUB BIOS boot
priority = 1;
};
esp = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = ["defaults" "umask=0077"];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
mountOptions = ["noatime" "nodiratime" "discard"];
};
};
};
};
};
};
};
}

1
starter/env/baserow.env vendored Normal file
View File

@ -0,0 +1 @@
BASEROW_PUBLIC_URL=BASEROW_DOMAIN

3
starter/env/n8n.env vendored Normal file
View File

@ -0,0 +1,3 @@
N8N_HOST=N8N_DOMAIN
NODE_ENV=production
N8N_ENCRYPTION_KEY=changeme

48
starter/flake.lock generated Normal file
View File

@ -0,0 +1,48 @@
{
"nodes": {
"disko": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1739760714,
"narHash": "sha256-SaGuzIQUTC2UPwX0aTm9W4aSEUcq3h6yZbi30piej2U=",
"owner": "nix-community",
"repo": "disko",
"rev": "be1e4321c9fb3a4bc2c061dafcdb424937a74dad",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1739580444,
"narHash": "sha256-+/bSz4EAVbqz8/HsIGLroF8aNaO8bLRL7WfACN+24g4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8bb37161a0488b89830168b81c48aed11569cb93",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"disko": "disko",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

25
starter/flake.nix Normal file
View File

@ -0,0 +1,25 @@
{
description = "Self-hosted server setup with Portainer, n8n, and Baserow";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = {
self,
nixpkgs,
...
} @ inputs: {
nixosConfigurations.server = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
inputs.disko.nixosModules.disko
./configuration.nix
];
};
};
}

View File

@ -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.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.ens18.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}

257
starter/install.sh Executable file
View File

@ -0,0 +1,257 @@
#!/usr/bin/env bash
set -euo pipefail
# Ensure we're in a flakes-enabled environment with required tools
if ! command -v nix &> /dev/null; then
echo "❌ Nix is not installed. Please install Nix first:"
echo "curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install"
exit 1
fi
# Check if we need to enter a new shell
if [[ ! -v INSIDE_NIX_SHELL ]]; then
export NIX_CONFIG="experimental-features = nix-command flakes"
export INSIDE_NIX_SHELL=1
exec nix shell nixpkgs#git nixpkgs#mkpasswd --command bash "$0"
fi
# Function to setup from template
setup_from_template() {
local TEMPLATE=starter
local DIR_NAME="self-host-playbook"
if [ -d "$DIR_NAME" ]; then
echo "📂 Directory '$DIR_NAME' already exists"
read -p "Do you want to proceed in the existing directory? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
cd "$DIR_NAME"
else
echo "❌ Please choose a different directory or remove the existing one"
exit 1
fi
else
echo "🔄 Creating new self-host-playbook configuration from template..."
nix flake new --template "git+https://code.m3tam3re.com/m3tam3re/self-host-playbook#${TEMPLATE}" "$DIR_NAME"
cd "$DIR_NAME"
fi
}
# Function to generate SSH key
generate_ssh_key() {
local KEY_NAME="self-host-playbook"
local KEY_PATH="$HOME/.ssh/${KEY_NAME}"
if [ ! -f "$KEY_PATH" ]; then
mkdir -p "$HOME/.ssh"
echo "🔑 Generating new SSH key pair..." >&2
ssh-keygen -t ed25519 -f "$KEY_PATH" -N "" >&2
echo "✅ New SSH key pair generated" >&2
else
echo "🔑 Using existing SSH key pair" >&2
fi
if [ ! -f "${KEY_PATH}.pub" ]; then
echo "❌ Error: Public key file not found" >&2
exit 1
fi
printf "%s" "$KEY_PATH"
}
echo "🚀 Welcome to the Self-Host Playbook!"
echo "================================================"
echo "This script will help you manage your NixOS server with:"
echo "- Portainer (Docker management)"
echo "- n8n (Workflow automation)"
echo "- Baserow (No-code database)"
echo "- Caddy (Automatic HTTPS reverse proxy)"
echo "================================================"
echo
# Function to hash password using mkpasswd
hash_password() {
local password=$1
mkpasswd -m sha-512 "$password"
}
# Function to display device detection guide
show_device_guide() {
echo
echo "📝 How to find your root device name:"
echo "------------------------------------"
echo "1. SSH into your server with the credentials provided by your cloud provider or use the web console"
echo "2. Run the following command:"
echo " lsblk -d -o NAME,SIZE"
echo
echo "Look for the main disk, usually the largest one. It will be shown as:"
echo "- AWS (older): xvda"
echo "- AWS (newer): nvme0n1"
echo "- GCP/Azure/Linode: sda"
echo "- DigitalOcean: vda"
echo
echo "The device name will be /dev/[name shown in lsblk]"
echo
echo "💡 Example:"
echo " NAME SIZE"
echo " sda 76.3G"
echo " sr0 1024M"
echo "------------------------------------"
echo
}
# Function to get device name based on provider
get_device_name() {
local provider=$1
case $provider in
"aws-new")
echo "/dev/nvme0n1"
;;
"aws-old")
echo "/dev/xvda"
;;
"gcp")
echo "/dev/sda"
;;
"azure")
echo "/dev/sda"
;;
"digitalocean")
echo "/dev/vda"
;;
"linode")
echo "/dev/sda"
;;
"hetzner")
echo "/dev/sda"
;;
*)
echo "unknown"
;;
esac
}
# Collect user input
echo "📝 Please provide the following information:"
echo "-------------------------------------------"
read -p "1. Enter target server IP address: " SERVER_IP
read -p "2. Enter desired username for server access: " USERNAME
read -s -p "3. Enter desired password: " PASSWORD
echo
echo "4. Enter domain names for services (must point to $SERVER_IP):"
read -p " - Domain for Portainer: " PORTAINER_DOMAIN
read -p " - Domain for n8n: " N8N_DOMAIN
read -p " - Domain for Baserow: " BASEROW_DOMAIN
echo
echo "5. Select your cloud provider:"
echo " 1) AWS (Newer instances with NVMe)"
echo " 2) AWS (Older instances)"
echo " 3) Google Cloud Platform"
echo " 4) Microsoft Azure"
echo " 5) DigitalOcean"
echo " 6) Linode"
echo " 7) Hetzner"
echo " 8) I'm not sure (Show detection guide)"
echo
read -p "Enter your choice (1-8): " PROVIDER_CHOICE
# Set device name based on provider choice
case $PROVIDER_CHOICE in
1)
PROVIDER="aws-new"
;;
2)
PROVIDER="aws-old"
;;
3)
PROVIDER="gcp"
;;
4)
PROVIDER="azure"
;;
5)
PROVIDER="digitalocean"
;;
6)
PROVIDER="linode"
;;
7)
PROVIDER="hetzner"
;;
8)
show_device_guide
read -p "Enter your root device name (e.g., /dev/sda): " DEVICE_NAME
PROVIDER="custom"
;;
*)
echo "❌ Invalid choice"
exit 1
;;
esac
if [ "$PROVIDER" != "custom" ]; then
DEVICE_NAME=$(get_device_name "$PROVIDER")
fi
echo
echo "Using root device: $DEVICE_NAME"
read -p "Is this correct? (y/N) " CONFIRM
if [[ ! $CONFIRM =~ ^[Yy]$ ]]; then
echo "Installation aborted. Please run the script again with the correct device."
exit 1
fi
echo "🛠️ Preparing server configuration..."
# Generate SSH key
SSH_KEY_PATH=$(generate_ssh_key) || exit 1
SSH_PUB_KEY=$(cat "${SSH_KEY_PATH}.pub") || {
echo "❌ Error: Failed to read public key from ${SSH_KEY_PATH}.pub"
exit 1
}
# Hash the password
HASHED_PASSWORD=$(hash_password "$PASSWORD")
echo "📝 Customizing configuration files..."
# Write configuration to JSON file
cat > config.json << EOF
{
"username": "$USERNAME",
"hashedPassword": "$HASHED_PASSWORD",
"sshKey": "$SSH_PUB_KEY",
"domains": {
"portainer": "$PORTAINER_DOMAIN",
"n8n": "$N8N_DOMAIN",
"baserow": "$BASEROW_DOMAIN"
},
"rootDevice": "$DEVICE_NAME"
}
EOF
echo "📦 Setting up environment files..."
# Update environment files with domains
sed -i "s/N8N_DOMAIN/$N8N_DOMAIN/g" ./env/n8n.env
sed -i "s/BASEROW_DOMAIN/$BASEROW_DOMAIN/g" ./env/baserow.env
echo "🚀 Starting NixOS installation..."
echo "This process might take several minutes..."
# Run nixos-anywhere installation
nix run github:nix-community/nixos-anywhere -- --flake .#server root@$SERVER_IP
echo
echo "🎉 Installation completed successfully!"
echo "=====================================>"
echo "You can now access your services at:"
echo "- Portainer: https://$PORTAINER_DOMAIN"
echo "- n8n: https://$N8N_DOMAIN"
echo "- Baserow: https://$BASEROW_DOMAIN"
echo
echo "To connect to your server, use:"
echo "ssh -i $SSH_KEY_PATH -p 2222 $USERNAME@$SERVER_IP"
echo
echo "⚠️ Important: Please save your SSH key path: $SSH_KEY_PATH"
echo "=====================================>"