Refactor shared configuration and update LNBits service for improved domain handling

Updated shared.nix to enhance domain parameter propagation and modified configuration.nix to utilize the inherited domain for machine-specific setups. Adjusted example-service.nix to accept the domain as an argument, improving modularity. Additionally, added a new documentation file explaining the LNBits flake deployment process, detailing architecture, key components, and deployment instructions for better onboarding and understanding of the system.
This commit is contained in:
padreug 2025-10-12 08:52:56 +02:00
parent ca5b78b561
commit 30a1ae28f7
4 changed files with 273 additions and 8 deletions

View file

@ -1,13 +1,12 @@
{ config, pkgs, ... }:
let
domain = "example.com";
in
{
# Import shared configuration and machine-specific modules
imports = [
# Import shared.nix with your domain parameter
# Replace "example.com" with your actual domain
{ _module.args = { inherit domain; }; }
(import /var/src/config-shared {
inherit config pkgs;
domain = "example.com";
inherit config pkgs domain;
})
# Import hardware-specific configuration

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs, ... }:
{ config, lib, pkgs, domain, ... }:
{
# Example: WireGuard VPN Service

View file

@ -2,8 +2,10 @@
{
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
{ _module.args = { inherit domain; }; } # passes <mydomain.com> for nginx virtualHosts
/var/src/config-pict-rs
/var/src/config-lnbits
];

View file

@ -0,0 +1,264 @@
# 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.