diff --git a/.gitignore b/.gitignore index bc15009..4d05df6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,30 +4,3 @@ dist/ result machine-specific web-app -lnbits -lnbits-extensions - -# User-specific deployment configuration -# Copy example-krops.nix to krops.nix and customize -krops.nix - -# User-specific build configuration -# Copy example-build-local.nix to build-local.nix and customize -build-local.nix - -# Machine-specific configurations (user creates these) -# Keep example-machine as a template -config/machines/* -!config/machines/example-machine/ - -# Secrets - only ignore unencrypted secrets -# Encrypted .age files are SAFE to commit -secrets/**/!(*.age) -secrets/**/*.txt -secrets/**/*.key -secrets/**/*.pem -secrets/**/*.env - -# Age/Passage identity files (NEVER commit these!) -.passage/ -identities diff --git a/DEPLOYMENT-GUIDE.md b/DEPLOYMENT-GUIDE.md index a290300..e27c199 100644 --- a/DEPLOYMENT-GUIDE.md +++ b/DEPLOYMENT-GUIDE.md @@ -7,90 +7,30 @@ This setup builds the web-app **locally** with machine-specific configuration, t - Machine-specific `.env` files - Machine-specific images in the `public` folder -## Initial Setup - -When you first clone this repository, you need to set up your local configuration: - -### 1. Create your configuration files - -```bash -# Copy the example templates -cp example-krops.nix krops.nix -cp example-build-local.nix build-local.nix -``` - -### 2. Create your first machine configuration - -```bash -# Copy the example machine template -cp -r config/machines/example-machine config/machines/my-machine - -# Edit the configuration -# - Change the domain in configuration.nix -# - Add your hardware-configuration.nix (from nixos-generate-config) -``` - -### 3. Create machine-specific web-app assets (if deploying web-app) - -```bash -mkdir -p machine-specific/my-machine/env -mkdir -p machine-specific/my-machine/images - -# Add your .env file and images -# See machine-specific/example-machine/ for reference -``` - -### 4. Update krops.nix and build-local.nix - -**In `krops.nix`:** -- Replace `example-machine` with your machine name -- Update the SSH target (`root@your-host`) -- Add to the `inherit` list and `all` script - -**In `build-local.nix`:** -- Replace `example-machine` with your machine name -- Add to the `all` build script - -### 5. Build and deploy! - -```bash -# Build web-app locally (if using web-app) -nix-build ./build-local.nix -A my-machine && ./result/bin/build-my-machine - -# Deploy to target machine -nix-build ./krops.nix -A my-machine && ./result -``` - -**Note:** Your `krops.nix`, `build-local.nix`, and machine configs in `config/machines/*` are gitignored. You can safely pull updates without overwriting your local configuration. - ## Structure ``` . -├── config/ # NixOS configuration files -│ ├── shared.nix # Shared config for all machines -│ ├── nginx.nix # Nginx configuration -│ ├── lnbits.nix # LNBits configuration -│ ├── pict-rs.nix # Pict-rs configuration -│ └── machines/ # Machine-specific configs (gitignored) -│ ├── example-machine/ # Template (committed to git) -│ │ ├── configuration.nix # Main config entry point -│ │ ├── boot.nix # Bootloader settings -│ │ └── example-service.nix # Service examples -│ ├── machine1/ # Your machines (gitignored) -│ └── machine2/ # Your machines (gitignored) -├── web-app/ # Shared web-app source (symlink) -├── machine-specific/ # Machine-specific web-app assets (symlink) -├── lnbits/ # LNBits source (symlink) -├── secrets/ # Encrypted secrets -│ ├── example-machine/ -│ │ └── README.md # Secrets usage guide -│ ├── machine1/ # Machine-specific secrets -│ │ └── *.age # Encrypted with age +├── web-app/ # Shared web-app source code +│ ├── package.json +│ ├── index.html +│ └── public/ # Base public folder +├── machine-specific/ +│ ├── machine1/ +│ │ ├── env/.env # Machine1's environment file +│ │ └── images/ # Machine1's images +│ │ ├── logo.png +│ │ └── banner.jpg │ └── machine2/ -├── build/ # Generated locally (gitignored) -├── build-local.nix # Local build scripts -└── krops.nix # Deployment configuration +│ ├── env/.env # Machine2's environment file +│ └── images/ # Machine2's images +│ ├── logo.png +│ └── banner.jpg +├── build/ # Generated locally (gitignored) +│ ├── machine1/dist/ # Built files for machine1 +│ └── machine2/dist/ # Built files for machine2 +├── build-local.nix # Local build scripts +└── krops.nix # Deployment configuration ``` ## How It Works @@ -143,31 +83,10 @@ nix-build ./krops.nix -A all && ./result ### Add a new machine -1. **Copy the example template:** - ```bash - cp -r config/machines/example-machine config/machines/my-new-machine - ``` - -2. **Edit the configuration:** - - Open `config/machines/my-new-machine/configuration.nix` - - Change `domain = "example.com"` to your domain - - Add your `hardware-configuration.nix` (from `nixos-generate-config`) - -3. **Create machine-specific web-app assets** (if using web-app): - ```bash - mkdir -p machine-specific/my-new-machine/env - mkdir -p machine-specific/my-new-machine/images - # Add .env file and images - ``` - -4. **Add to krops.nix and build-local.nix:** - - Add `my-new-machine` configuration to both files - -5. **Build and deploy:** - ```bash - nix-build ./build-local.nix -A my-new-machine && ./result/bin/build-my-new-machine - nix-build ./krops.nix -A my-new-machine && ./result - ``` +1. Create directories: `machine-specific/machine3/env/` and `machine-specific/machine3/images/` +2. Add `.env` file and images for machine3 +3. Create `config/machine3/configuration.nix` +4. Add machine3 to `build-local.nix` and `krops.nix` ### Update environment variables @@ -183,161 +102,3 @@ Edit files in `web-app/`, then rebuild locally After any changes: rebuild locally, then redeploy. -## Adding Machine-Specific Services - -Sometimes you need services that only run on certain machines (e.g., WireGuard on machine1 but not machine2). - -### Using the Example Template - -A complete example machine configuration is provided in `config/example-machine/`: - -``` -config/example-machine/ - ├── configuration.nix # Template with domain parameter - ├── boot.nix # Bootloader configuration examples - └── example-service.nix # WireGuard and other service examples -``` - -**To use the template:** -1. Copy the `example-machine` directory to your new machine name: - ```bash - cp -r config/example-machine config/my-new-machine - ``` -2. Edit `configuration.nix` to set your domain -3. Copy your `hardware-configuration.nix` from `nixos-generate-config` -4. Customize `boot.nix` for your bootloader (UEFI or BIOS) -5. Modify or remove `example-service.nix` as needed -6. Add the machine to `build-local.nix` and `krops.nix` - -### Example: Machine1 has WireGuard - -**Structure:** -``` -config/ - ├── shared.nix # Shared config for all machines - ├── machine1/ - │ ├── configuration.nix # Imports shared.nix + machine-specific modules - │ ├── wireguard.nix # Machine1-specific service - │ ├── hardware-configuration.nix - │ └── boot.nix - └── machine2/ - ├── configuration.nix # Only imports shared.nix - ├── hardware-configuration.nix - └── boot.nix -``` - -### Steps to Add a Machine-Specific Service - -1. **Create a service configuration file** in the machine's directory: - ```bash - # Example: config/machine1/wireguard.nix - { config, lib, pkgs, ... }: - { - networking.wireguard.interfaces = { - wg0 = { - privateKeyFile = "/etc/wireguard/privatekey"; - ips = [ "10.0.0.2/24" ]; - peers = [ ... ]; - }; - }; - } - ``` - -2. **Import it in the machine's configuration.nix**: - ```nix - # config/machine1/configuration.nix - { config, pkgs, ... }: - { - imports = [ - (import /var/src/config-shared { - inherit config pkgs; - domain = "4lpaca.io"; - }) - ./hardware-configuration.nix - ./boot.nix - ./wireguard.nix # ← Add your service here - ]; - } - ``` - -3. **Deploy** - the service will only be deployed to that specific machine: - ```bash - nix-build ./krops.nix -A machine1 && ./result - ``` - -### Common Machine-Specific Services - -- **WireGuard VPN** - Only on machines that need VPN access -- **Backup services** - Different backup targets per machine -- **Development tools** - Extra packages for staging vs production -- **Custom hardware drivers** - GPU drivers, specific hardware support - -The key is that each machine's `configuration.nix` can import different modules while still sharing common configuration through `shared.nix`. - -## Deploying LNBits Extensions - -You can deploy custom LNBits extensions to `/var/lib/lnbits/extensions` on your target machines. - -### Setup - -**1. Create extensions directory:** -```bash -mkdir -p lnbits-extensions -``` - -**2. Add your custom extensions:** -```bash -# Example: Clone a custom extension -git clone https://github.com/your-org/custom-extension lnbits-extensions/custom-extension -``` - -**3. Enable in krops.nix:** -Uncomment the lnbits-extensions line: -```nix -lnbits-extensions.file = toString ./lnbits-extensions; -``` - -**4. Enable in config/lnbits.nix:** - -Choose one of two options: - -**Option 1: Replace extensions directory** (use if you manage ALL extensions via deployment) -```nix -systemd.tmpfiles.rules = [ - "L+ /var/lib/lnbits/extensions - - - - /var/src/lnbits-extensions" -]; -``` -⚠️ **Warning:** This will DELETE any extensions installed via the LNBits UI! - -**Option 2: Merge deployed extensions** (safer - keeps UI-installed extensions) -```nix -systemd.services.lnbits-copy-extensions = { - description = "Copy deployed LNBits extensions"; - before = [ "lnbits.service" ]; - wantedBy = [ "lnbits.service" ]; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${pkgs.rsync}/bin/rsync -av /var/src/lnbits-extensions/ /var/lib/lnbits/extensions/"; - }; -}; -``` - -**5. Deploy:** -```bash -nix-build ./krops.nix -A machine1 && ./result -``` - -### How It Works - -**Option 1 (Symlink):** -- Your `./lnbits-extensions` directory is deployed to `/var/src/lnbits-extensions` -- A symlink replaces `/var/lib/lnbits/extensions` → `/var/src/lnbits-extensions` -- Any existing extensions directory is deleted -- All extensions must be managed via deployment - -**Option 2 (Copy/Merge):** -- Your `./lnbits-extensions` directory is deployed to `/var/src/lnbits-extensions` -- Deployed extensions are copied into `/var/lib/lnbits/extensions/` -- Existing UI-installed extensions are preserved -- You can mix deployed extensions with UI-installed ones - diff --git a/config/lnbits.nix b/config/lnbits.nix index fb83bc3..28d6545 100644 --- a/config/lnbits.nix +++ b/config/lnbits.nix @@ -1,14 +1,6 @@ -{ domain, pkgs, config, lib, ... }: +{ domain, pkgs, ... }: -let - lnbitsFlake = builtins.getFlake "path:/var/src/lnbits-src"; -in { - # Import the LNBits service module from the flake (following official guide pattern) - imports = [ - "${lnbitsFlake}/nix/modules/lnbits-service.nix" - ]; - # LNBits service configuration services.lnbits = { enable = true; @@ -16,12 +8,9 @@ in port = 5000; openFirewall = true; stateDir = "/var/lib/lnbits"; - # Use lnbits package from the flake - package = lnbitsFlake.packages.${pkgs.system}.lnbits; + # Use lnbits from deployed flake source at /var/src/lnbits-src + package = (builtins.getFlake "path:/var/src/lnbits-src").packages.${pkgs.system}.lnbits; env = { - # Custom extensions path (if deployed via krops) - # Extensions from /var/src/lnbits-extensions will be symlinked to /var/lib/lnbits/extensions - # LNBITS_EXTENSIONS_PATH = "/var/lib/lnbits/extensions"; LNBITS_ADMIN_UI = "true"; AUTH_ALLOWED_METHODS = "user-id-only, username-password"; LNBITS_BACKEND_WALLET_CLASS = "FakeWallet"; @@ -92,31 +81,4 @@ in }; }; }; - - # Deploy custom extensions - # WARNING: L+ will REPLACE /var/lib/lnbits/extensions if it already exists! - # This will DELETE any extensions installed via the LNBits UI. - # - # Option 1: Replace extensions directory entirely (use with caution) - systemd.tmpfiles.rules = [ - # Set permissions on source directory so lnbits user can read it - "d /var/src/lnbits-extensions 0755 lnbits lnbits - -" - # Create symlink with proper ownership - "L+ /var/lib/lnbits/extensions - lnbits lnbits - /var/src/lnbits-extensions" - ]; - # - # Option 2: Manually merge deployed extensions with existing ones - # Copy deployed extensions into the extensions directory without replacing it: - # systemd.tmpfiles.rules = [ - # "d /var/src/lnbits-extensions 0755 lnbits lnbits - -" - # ]; - # systemd.services.lnbits-copy-extensions = { - # description = "Copy deployed LNBits extensions"; - # before = [ "lnbits.service" ]; - # wantedBy = [ "lnbits.service" ]; - # serviceConfig = { - # Type = "oneshot"; - # ExecStart = "${pkgs.rsync}/bin/rsync -av /var/src/lnbits-extensions/ /var/lib/lnbits/extensions/"; - # }; - # }; } diff --git a/config/machines/example-machine/boot.nix b/config/machines/example-machine/boot.nix deleted file mode 100644 index b313ef9..0000000 --- a/config/machines/example-machine/boot.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ - # Bootloader configuration - # This example uses systemd-boot for UEFI systems - # For BIOS systems, use GRUB instead - - # UEFI boot loader (systemd-boot) - boot.loader.systemd-boot.enable = true; - boot.loader.efi.canTouchEfiVariables = true; - - # Alternative: GRUB for BIOS systems - # boot.loader.grub.enable = true; - # boot.loader.grub.device = "/dev/sda"; # or "nodev" for UEFI -} diff --git a/config/machines/example-machine/configuration.nix b/config/machines/example-machine/configuration.nix deleted file mode 100644 index c48bd02..0000000 --- a/config/machines/example-machine/configuration.nix +++ /dev/null @@ -1,23 +0,0 @@ -{ config, pkgs, ... }: -let - domain = "example.com"; -in -{ - imports = [ - { _module.args = { inherit domain; }; } - (import /var/src/config-shared { - inherit config pkgs domain; - }) - - # Import hardware-specific configuration - # This file is typically generated by nixos-generate-config - ./hardware-configuration.nix - - # Import boot configuration (bootloader settings) - ./boot.nix - - # Import any machine-specific services - # Comment out or remove if not needed - # ./example-service.nix - ]; -} diff --git a/config/machines/example-machine/example-service.nix b/config/machines/example-machine/example-service.nix deleted file mode 100644 index 0808f1d..0000000 --- a/config/machines/example-machine/example-service.nix +++ /dev/null @@ -1,71 +0,0 @@ -{ config, lib, pkgs, domain, ... }: - -{ - # Example: WireGuard VPN Service - # This is a machine-specific service that can be imported in configuration.nix - # Only machines that need WireGuard should import this file - - # Install WireGuard tools - environment.systemPackages = with pkgs; [ - wireguard-tools - ]; - - # Configure WireGuard interface - networking.wireguard.interfaces = { - wg0 = { - # Generate keys with: wg genkey | tee privatekey | wg pubkey > publickey - # Store the private key securely on the target machine - privateKeyFile = "/etc/wireguard/privatekey"; - - # VPN IP address for this machine - ips = [ "10.0.0.2/24" ]; - - # VPN peers (other machines or VPN server) - peers = [ - { - # Public key of the peer - publicKey = "PEER_PUBLIC_KEY_HERE"; - - # Which IPs should be routed through this peer - allowedIPs = [ "10.0.0.1/32" ]; - - # Endpoint address and port of the peer - endpoint = "vpn.example.com:51820"; - - # Send keepalive packets every 15 seconds - persistentKeepalive = 15; - } - ]; - }; - }; - - # Optional: Systemd service optimizations - systemd.services."wireguard-wg0".serviceConfig = { - # Restart the service if it fails - Restart = "on-failure"; - RestartSec = "5s"; - }; - - # Other example services you might add: - - # Example: Custom backup service - # services.restic.backups.daily = { - # user = "root"; - # repository = "s3:s3.amazonaws.com/my-backup-bucket"; - # passwordFile = "/etc/restic/password"; - # paths = [ "/var/lib" "/home" ]; - # timerConfig = { OnCalendar = "daily"; }; - # }; - - # Example: Development tools (for staging environments) - # environment.systemPackages = with pkgs; [ - # vim - # git - # htop - # tmux - # ]; - - # Example: Custom firewall rules - # networking.firewall.allowedTCPPorts = [ 8080 ]; - # networking.firewall.allowedUDPPorts = [ 51820 ]; -} diff --git a/config/modules/.gitkeep b/config/modules/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/config/modules/lnbits-service.nix b/config/modules/lnbits-service.nix new file mode 100644 index 0000000..358f300 --- /dev/null +++ b/config/modules/lnbits-service.nix @@ -0,0 +1,123 @@ +{ config, pkgs, lib, ... }: + +let + defaultUser = "lnbits"; + cfg = config.services.lnbits; + inherit (lib) mkOption mkIf types optionalAttrs literalExpression; +in + +{ + options = { + services.lnbits = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to enable the lnbits service + ''; + }; + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Whether to open the ports used by lnbits in the firewall for the server + ''; + }; + package = mkOption { + type = types.package; + defaultText = literalExpression "pkgs.lnbits"; + default = pkgs.lnbits; + description = '' + The lnbits package to use. + ''; + }; + stateDir = mkOption { + type = types.path; + default = "/var/lib/lnbits"; + description = '' + The lnbits state directory + ''; + }; + host = mkOption { + type = types.str; + default = "127.0.0.1"; + description = '' + The host to bind to + ''; + }; + port = mkOption { + type = types.port; + default = 8231; + description = '' + The port to run on + ''; + }; + user = mkOption { + type = types.str; + default = "lnbits"; + description = "user to run lnbits as"; + }; + group = mkOption { + type = types.str; + default = "lnbits"; + description = "group to run lnbits as"; + }; + env = mkOption { + type = types.attrsOf types.str; + default = {}; + description = '' + Additional environment variables that are passed to lnbits. + Reference Variables: https://github.com/lnbits/lnbits/blob/dev/.env.example + ''; + example = { + LNBITS_ADMIN_UI = "true"; + }; + }; + }; + }; + + config = mkIf cfg.enable { + users.users = optionalAttrs (cfg.user == defaultUser) { + ${defaultUser} = { + isSystemUser = true; + group = defaultUser; + }; + }; + + users.groups = optionalAttrs (cfg.group == defaultUser) { + ${defaultUser} = { }; + }; + + systemd.tmpfiles.rules = [ + "d ${cfg.stateDir} 0700 ${cfg.user} ${cfg.group} - -" + "d ${cfg.stateDir}/data 0700 ${cfg.user} ${cfg.group} - -" + ]; + + systemd.services.lnbits = { + enable = true; + description = "lnbits"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + environment = lib.mkMerge [ + { + LNBITS_DATA_FOLDER = "${cfg.stateDir}/data"; + # LNBits automatically appends '/extensions' to this path + LNBITS_EXTENSIONS_PATH = "${cfg.stateDir}"; + } + cfg.env + ]; + serviceConfig = { + User = cfg.user; + Group = cfg.group; + WorkingDirectory = "${cfg.package}/lib/python3.12/site-packages"; + StateDirectory = "lnbits"; + ExecStart = "${cfg.package}/bin/lnbits --port ${toString cfg.port} --host ${cfg.host}"; + Restart = "always"; + PrivateTmp = true; + }; + }; + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.port ]; + }; + }; +} diff --git a/config/shared.nix b/config/shared.nix index 2b23604..d751580 100644 --- a/config/shared.nix +++ b/config/shared.nix @@ -2,19 +2,16 @@ { imports = [ - # Note: 'domain' is made available via _module.args in the machine's configuration.nix - # It's passed to this module and propagated to all imports automatically - - /var/src/config-nginx - /var/src/config-pict-rs - /var/src/config-lnbits + ./nginx.nix + ./modules/lnbits-service.nix + { _module.args = { inherit domain; }; } + ./pict-rs.nix + ./lnbits.nix ]; # Set hostname (replace dots with hyphens, e.g., "demo.ariege.io" → "demo-ariege-io") networking.hostName = builtins.replaceStrings ["."] ["-"] domain; - nix.settings.experimental-features = [ "nix-command" "flakes" ]; - # System packages environment.systemPackages = with pkgs; [ vim @@ -41,4 +38,4 @@ # NixOS release version system.stateVersion = "25.05"; -} +} \ No newline at end of file diff --git a/docs/lnbits-flake-explanation.md b/docs/lnbits-flake-explanation.md deleted file mode 100644 index 78c8f68..0000000 --- a/docs/lnbits-flake-explanation.md +++ /dev/null @@ -1,264 +0,0 @@ -# How the LNBits Flake Works - -## Overview - -This document explains how the LNBits flake deployment works, particularly how it achieves the equivalent of running `uv run lnbits` on the deployed NixOS machine. - -## Architecture - -The LNBits flake uses `uv2nix` to convert `uv`'s lock file into a reproducible Nix build, creating a Python virtual environment that can be deployed as a NixOS service. - -## Key Components - -### 1. uv2nix: Converting uv.lock to Nix - -The flake uses `uv2nix` to read the `uv.lock` file and create a reproducible Nix build: - -```nix -# Read uv.lock and pyproject.toml -workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; - -# Create overlay preferring wheels (faster than building from source) -uvLockedOverlay = workspace.mkPyprojectOverlay { sourcePreference = "wheel"; }; -``` - -This converts the uv-managed dependencies into Nix packages. - -### 2. Building a Python Virtual Environment - -Instead of `uv` creating a venv at runtime, Nix creates one during the build: - -```nix -# Build venv with all dependencies from uv.lock -runtimeVenv = pythonSet.mkVirtualEnv "${projectName}-env" workspace.deps.default; -``` - -This creates an immutable virtual environment in `/nix/store/...-lnbits-env` with all Python packages installed from the locked dependencies. - -### 3. The Wrapper Script (Equivalent to `uv run`) - -The flake creates a wrapper that mimics `uv run lnbits`: - -```nix -lnbitsApp = pkgs.writeShellApplication { - name = "lnbits"; - text = '' - export SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt - export REQUESTS_CA_BUNDLE=$SSL_CERT_FILE - export PYTHONPATH="$PWD:${PYTHONPATH:-}" - exec ${runtimeVenv}/bin/lnbits "$@" - ''; -}; -``` - -This wrapper: -- Sets up SSL certificates for HTTPS requests -- Adds the current directory to `PYTHONPATH` (so it can find local source files) -- Executes the `lnbits` binary from the built venv - -### 4. The NixOS Service Module - -The service module (`lnbits-service.nix`) configures systemd to run LNBits: - -```nix -# The actual command that runs -ExecStart = "${lib.getExe cfg.package} --port ${toString cfg.port} --host ${cfg.host}"; - -# Environment variables -environment = { - LNBITS_DATA_FOLDER = "${cfg.stateDir}"; # /var/lib/lnbits - LNBITS_EXTENSIONS_PATH = "${cfg.stateDir}/extensions"; - LNBITS_PATH = "${cfg.package.src}"; # Points to source -} -``` - -Key points: -- `cfg.package` = the venv from the flake (`runtimeVenv`) -- `lib.getExe cfg.package` = extracts the executable path: `/nix/store/xxx-lnbits-env/bin/lnbits` -- `cfg.package.src` = points back to the LNBits source directory for templates/static files - -### 5. Flake Outputs - -The flake exposes the venv as a package: - -```nix -packages.default = runtimeVenv; -packages.${projectName} = runtimeVenv; # packages.lnbits = runtimeVenv -``` - -## How Your Deployment Uses It - -In your `config/lnbits.nix`: - -```nix -package = (builtins.getFlake "path:/var/src/lnbits-src").packages.${pkgs.system}.lnbits; -``` - -This breaks down as: - -1. `builtins.getFlake "path:/var/src/lnbits-src"` - Loads the flake from the deployed source -2. `.packages` - Accesses the packages output from the flake -3. `.${pkgs.system}` - Selects the right system architecture (e.g., `x86_64-linux`) -4. `.lnbits` - Gets the `lnbits` package (which equals `runtimeVenv`) - -## Understanding Flake References - -The **flake reference format** is crucial to understanding how this works: - -### Local Path Reference - -```nix -builtins.getFlake "path:/var/src/lnbits-src" -``` - -- Uses files from the local filesystem at `/var/src/lnbits-src` -- The `.src` attribute points to `/var/src/lnbits-src` -- Files are mutable - you can edit them -- Requires deploying the full source tree via krops - -### GitHub Reference - -```nix -builtins.getFlake "github:lnbits/lnbits/main" -``` - -- Nix fetches the repository from GitHub -- Stores it in `/nix/store/xxx-source/` (read-only) -- The `.src` attribute points to `/nix/store/xxx-source` -- Files are immutable -- No need to deploy source separately - -### Comparison - -| Aspect | `path:/var/src/lnbits-src` | `github:lnbits/lnbits` | -|--------|---------------------------|------------------------| -| **Source location** | `/var/src/lnbits-src` | `/nix/store/xxx-source` | -| **Mutable?** | Yes - can edit files | No - read-only | -| **Deployment** | Deploy via krops | Built-in to Nix | -| **Updates** | Redeploy source | Change flake ref | -| **Local changes** | Supported | Not possible | - -## Why Deploy the Full Source? - -The entire `lnbits` folder must be copied to `/var/src/lnbits-src` because: - -### 1. Build Time Requirements - -The flake needs these files to build the venv: -- `flake.nix` - Defines how to build the venv -- `uv.lock` - Contains locked dependency versions -- `pyproject.toml` - Defines project metadata - -### 2. Runtime Requirements - -LNBits needs the source tree at runtime for: -- Python modules in `lnbits/` -- HTML templates -- Static files (CSS, JavaScript, images) -- Extension loading system - -## Directory Structure - -### On the Deployed Machine - -``` -/var/src/lnbits-src/ ← Full source deployed by krops -├── flake.nix ← Used to build venv -├── uv.lock ← Used to build venv -├── pyproject.toml ← Used to build venv -└── lnbits/ ← Used at runtime - ├── templates/ - ├── static/ - └── ... - -/nix/store/xxx-lnbits-env/ ← Built venv (Python packages only) -├── bin/lnbits ← Executable -└── lib/python3.12/... ← Dependencies -``` - -### At Runtime - -The systemd service: -- Runs: `/nix/store/xxx-lnbits-env/bin/lnbits` -- With: `LNBITS_PATH=/var/src/lnbits-src` (to find templates/static/etc) -- With: `WorkingDirectory=/var/src/lnbits-src` - -## Comparison: `uv run` vs Nix Flake - -### Traditional `uv run lnbits` - -```bash -cd /path/to/lnbits -uv run lnbits --port 5000 --host 0.0.0.0 -``` - -This: -1. Reads `uv.lock` -2. Creates/updates a venv in `.venv/` -3. Installs dependencies if needed -4. Runs `lnbits` from the venv -5. Uses current directory for source files - -### Nix Flake Approach - -```nix -package = (builtins.getFlake "path:/var/src/lnbits-src").packages.${pkgs.system}.lnbits; -``` - -This: -1. ✅ Reads `uv.lock` via `uv2nix` -2. ✅ Creates a venv in `/nix/store` (immutable) -3. ✅ All dependencies are locked and reproducible -4. ✅ Runs `/nix/store/xxx-lnbits-env/bin/lnbits` -5. ✅ Sets `LNBITS_PATH` to source directory for templates/static/extensions -6. ✅ Runs as a systemd service with proper user/permissions -7. ✅ No runtime dependency on `uv` itself - -### Key Differences - -| Aspect | `uv run` | Nix Flake | -|--------|----------|-----------| -| **Venv location** | `.venv/` in source | `/nix/store/xxx-env` | -| **Mutability** | Mutable | Immutable | -| **Reproducibility** | Lock file only | Full Nix derivation | -| **Service management** | Manual | systemd integration | -| **Dependency on uv** | Required at runtime | Only at build time | - -## The `.src` Attribute Mystery - -A common question: where is `cfg.package.src` defined? - -### Answer: It's Automatic - -The `.src` attribute is **not explicitly defined** - it's automatically set by Nix when loading a flake: - -```nix -# When you do this: -builtins.getFlake "path:/var/src/lnbits-src" - -# Nix automatically: -# 1. Reads the flake at /var/src/lnbits-src -# 2. Evaluates it and builds outputs -# 3. Adds .src = /var/src/lnbits-src to the package -``` - -This is a built-in Nix flake feature - packages inherit the source location from where the flake was loaded. - -## Summary - -The LNBits flake deployment: - -1. **Converts uv dependencies to Nix** using `uv2nix` -2. **Builds an immutable venv** in `/nix/store` -3. **Deploys full source** to `/var/src/lnbits-src` via krops -4. **Loads the flake** from the deployed source using `path:/var/src/lnbits-src` -5. **Runs as a systemd service** with proper environment variables pointing to the source - -This provides: -- ✅ **Reproducibility** - exact same dependencies every time -- ✅ **Declarative configuration** - everything in `configuration.nix` -- ✅ **Source mutability** - can edit files in `/var/src/lnbits-src` -- ✅ **No uv dependency** - service doesn't need `uv` at runtime -- ✅ **Proper service management** - systemd integration with user permissions - -The key insight is that **`path:` vs `github:` in the flake reference** determines whether you use local deployed files or Nix fetches from a remote repository. diff --git a/example-build-local.nix b/example-build-local.nix deleted file mode 100644 index c268808..0000000 --- a/example-build-local.nix +++ /dev/null @@ -1,59 +0,0 @@ -let - pkgs = import {}; - - # Build script for a specific machine - buildForMachine = name: pkgs.writeShellScriptBin "build-${name}" '' - set -e - - BUILD_DIR="./build/${name}" - - echo "Building web-app for ${name}..." - - # Clean and create build directory - rm -rf "$BUILD_DIR" - mkdir -p "$BUILD_DIR" - - # Copy web-app source - cp -r ./web-app/* "$BUILD_DIR/" - - # Copy machine-specific .env - echo "Copying machine-specific .env..." - cp ./machine-specific/${name}/env/.env "$BUILD_DIR/.env" - - # Copy machine-specific images to public folder - echo "Copying machine-specific images to public..." - cp -r ./machine-specific/${name}/images/* "$BUILD_DIR/public/" - - # Copy machine-specific logo to assets - echo "Copying machine-specific logo to assets..." - mkdir -p "$BUILD_DIR/src/assets" - cp ./machine-specific/${name}/images/logo.png "$BUILD_DIR/src/assets/logo.png" - - # Build the web-app - echo "Running build..." - cd "$BUILD_DIR" - ${pkgs.nodejs}/bin/npm run build - - echo "Build complete for ${name}! Output in $BUILD_DIR/dist" - ''; - -in { - # Example machine build (copy this line for each machine) - # Replace "example-machine" with your machine name - example-machine = buildForMachine "example-machine"; - - # Add more machines here: - # machine1 = buildForMachine "machine1"; - # machine2 = buildForMachine "machine2"; - - # Build all machines (update this list with your machines) - all = pkgs.writeShellScriptBin "build-all" '' - set -e - echo "Building for all machines..." - ${(buildForMachine "example-machine")}/bin/build-example-machine - # Add your machines here: - # ${(buildForMachine "machine1")}/bin/build-machine1 - # ${(buildForMachine "machine2")}/bin/build-machine2 - echo "All builds complete!" - ''; -} diff --git a/example-krops.nix b/example-krops.nix deleted file mode 100644 index 107aa20..0000000 --- a/example-krops.nix +++ /dev/null @@ -1,83 +0,0 @@ -let - krops = builtins.fetchGit { - url = "https://cgit.krebsco.de/krops/"; - ref = "master"; - }; - - lib = import "${krops}/lib"; - pkgs = import "${krops}/pkgs" {}; - - # Define sources for each machine - source = name: lib.evalSource [ - { - # NixOS configuration entry point - nixos-config.symlink = "config-machine/configuration.nix"; - - # Use nixpkgs from local NIX_PATH (much smaller than git clone) - # This copies your local without .git history (~400MB vs 6GB) - nixpkgs.file = { - path = toString ; - useChecksum = true; - }; - - # Shared configuration files (only shared modules and files) - config-shared.file = toString ./config/shared.nix; - config-modules.file = toString ./config/modules; - config-nginx.file = toString ./config/nginx.nix; - config-pict-rs.file = toString ./config/pict-rs.nix; - config-lnbits.file = toString ./config/lnbits.nix; - - # Machine-specific configuration files (only this machine's config) - config-machine.file = toString (./config/machines + "/${name}"); - - # Pre-built web-app (built locally with machine-specific config) - web-app-dist.file = toString (./build + "/${name}/dist"); - - # LNBits flake source - lnbits-src.file = toString ./lnbits; - - # LNBits extensions (deployed to /var/lib/lnbits/extensions) - # Uncomment if you have custom extensions to deploy - # lnbits-extensions.file = toString ./lnbits-extensions; - } - ]; - - # Example machine deployment (copy this block for each machine) - # Replace "example-machine" with your machine name - # Replace "root@your-host" with your SSH target - example-machine = pkgs.krops.writeDeploy "deploy-example-machine" { - source = source "example-machine"; - target = "root@your-host-or-ip"; - - # Avoid having to create a sentinel file. - # Otherwise /var/src/.populate must be created on the target node to signal krops - # that it is allowed to deploy. - force = true; - }; - - # Add more machines here following the same pattern: - # machine1 = pkgs.krops.writeDeploy "deploy-machine1" { - # source = source "machine1"; - # target = "root@machine1-host"; - # force = true; - # }; - - # Deploy to all machines (update this list with your machines) - all = pkgs.writeScript "deploy-all" '' - #!${pkgs.bash}/bin/bash - set -e - echo "Deploying to example-machine..." - ${example-machine} - # Add your machines here: - # echo "Deploying to machine1..." - # ${machine1} - echo "All deployments completed!" - ''; - -in { - # Export your machine deployments here - inherit example-machine all; - - # Add your machines to the inherit list: - # inherit example-machine machine1 machine2 all; -}