Compare commits
No commits in common. "30a1ae28f7594db77166d88e6da8f5994e4c8c36" and "f6c7a1c3f6927b4c55b589dbe71a0f1a0c1d891c" have entirely different histories.
30a1ae28f7
...
f6c7a1c3f6
12 changed files with 155 additions and 852 deletions
27
.gitignore
vendored
27
.gitignore
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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/";
|
||||
# };
|
||||
# };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
];
|
||||
}
|
||||
|
|
@ -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 ];
|
||||
}
|
||||
123
config/modules/lnbits-service.nix
Normal file
123
config/modules/lnbits-service.nix
Normal 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 ];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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!"
|
||||
'';
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue