Compare commits

..

No commits in common. "30a1ae28f7594db77166d88e6da8f5994e4c8c36" and "f6c7a1c3f6927b4c55b589dbe71a0f1a0c1d891c" have entirely different histories.

12 changed files with 155 additions and 852 deletions

27
.gitignore vendored
View file

@ -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

View file

@ -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

View file

@ -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/";
# };
# };
}

View file

@ -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
}

View file

@ -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
];
}

View file

@ -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 ];
}

View file

@ -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 ];
};
};
}

View file

@ -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";
}
}

View file

@ -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.

View file

@ -1,59 +0,0 @@
let
pkgs = import <nixpkgs> {};
# 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!"
'';
}

View file

@ -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 <nixpkgs> without .git history (~400MB vs 6GB)
nixpkgs.file = {
path = toString <nixpkgs>;
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;
}