Compare commits
No commits in common. "9777e88032150a5a6eb94b48081be7524ca56937" and "2e6f88a2d9b4c896d45cad54fdd23936e6f0a258" have entirely different histories.
9777e88032
...
2e6f88a2d9
8 changed files with 862 additions and 15 deletions
16
README.md
16
README.md
|
|
@ -1,11 +1,3 @@
|
||||||
# NixOS Configuration Repository
|
|
||||||
|
|
||||||
## Session Start Protocol
|
|
||||||
Always begin by reading agents.md for workflow instructions.
|
|
||||||
|
|
||||||
## System Installation
|
|
||||||
|
|
||||||
1. Partition the disk:
|
|
||||||
```
|
```
|
||||||
sudo parted /dev/nvme0n1 -- mklabel gpt
|
sudo parted /dev/nvme0n1 -- mklabel gpt
|
||||||
sudo parted /dev/nvme0n1 -- mkpart primary ext4 512MB 100%
|
sudo parted /dev/nvme0n1 -- mkpart primary ext4 512MB 100%
|
||||||
|
|
@ -13,10 +5,7 @@ sudo parted /dev/nvme0n1 -- mkpart ESP fat32 1MB 512MB
|
||||||
sudo parted /dev/nvme0n1 -- set 2 esp on
|
sudo parted /dev/nvme0n1 -- set 2 esp on
|
||||||
sleep 2
|
sleep 2
|
||||||
sudo mkfs.ext4 /dev/disk/by-partlabel/primary
|
sudo mkfs.ext4 /dev/disk/by-partlabel/primary
|
||||||
```
|
|
||||||
|
|
||||||
2. Mount the filesystems:
|
|
||||||
```
|
|
||||||
sudo mount -o rw /dev/disk/by-partlabel/primary /mnt/
|
sudo mount -o rw /dev/disk/by-partlabel/primary /mnt/
|
||||||
sudo mkdir /mnt/boot
|
sudo mkdir /mnt/boot
|
||||||
sudo mkfs.vfat /dev/disk/by-partlabel/ESP
|
sudo mkfs.vfat /dev/disk/by-partlabel/ESP
|
||||||
|
|
@ -25,14 +14,13 @@ sudo mkdir /mnt/root
|
||||||
sudo git clone https://jsutter:b9cf9383b20dc6efe4d0a732d659709097879b67@git.symbiotrip.com/jsutter/nixos /mnt/root/nixos
|
sudo git clone https://jsutter:b9cf9383b20dc6efe4d0a732d659709097879b67@git.symbiotrip.com/jsutter/nixos /mnt/root/nixos
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Install NixOS:
|
Then:
|
||||||
```
|
```
|
||||||
sudo -i
|
sudo -i
|
||||||
cd /mnt/root/nixos
|
cd /mnt/root/nixos
|
||||||
nixos-install --flake .#<name> --no-root-password --impure
|
nixos-install --flake .#<name> --no-root-password --impure
|
||||||
```
|
```
|
||||||
|
Finally:
|
||||||
4. Set user password:
|
|
||||||
```
|
```
|
||||||
nixos-enter --root '/mnt'
|
nixos-enter --root '/mnt'
|
||||||
passwd jsutter
|
passwd jsutter
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@
|
||||||
|
|
||||||
## Important
|
## Important
|
||||||
- Server configs may contain hardcoded credentials - use agenix or systemd credentials for production
|
- Server configs may contain hardcoded credentials - use agenix or systemd credentials for production
|
||||||
- **Always carefully inspect the NixOS wiki for instructions before adding new applications to the repo**
|
|
||||||
- Both warp and skip build successfully
|
- Both warp and skip build successfully
|
||||||
- Repository root: `/home/jsutter/src/nixos`
|
- Repository root: `/home/jsutter/src/nixos`
|
||||||
|
|
||||||
|
|
|
||||||
356
appflakes/immich/README.md
Executable file
356
appflakes/immich/README.md
Executable file
|
|
@ -0,0 +1,356 @@
|
||||||
|
# Immich NixOS Module
|
||||||
|
|
||||||
|
Self-hosted photo and video backup solution packaged as a NixOS module.
|
||||||
|
|
||||||
|
## About Immich
|
||||||
|
|
||||||
|
Immich is a high-performance self-hosted photo and video backup solution. This module deploys Immich using Docker Compose with automatic service management.
|
||||||
|
|
||||||
|
**Key Features:**
|
||||||
|
- Automatic backup from mobile devices
|
||||||
|
- AI-powered face recognition
|
||||||
|
- Smart search with object and location detection
|
||||||
|
- Sharing albums with family and friends
|
||||||
|
- Timeline and map views
|
||||||
|
- Duplicate detection
|
||||||
|
- Video transcoding and playback
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic Configuration
|
||||||
|
|
||||||
|
Add this module to your NixOS configuration:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
# Import the Immich Docker Compose module
|
||||||
|
(builtins.getFlake "path:/path/to/appflakes/immich").nixosModules.immich-docker
|
||||||
|
];
|
||||||
|
|
||||||
|
services.immich-docker = {
|
||||||
|
enable = true;
|
||||||
|
domain = "immich.example.com";
|
||||||
|
port = 2283;
|
||||||
|
dataDir = "/mnt/userdata/immich";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Flake Inputs
|
||||||
|
|
||||||
|
If using flakes, add Immich as an input:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
immich = {
|
||||||
|
url = "path:./appflakes/immich";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, immich, ... }: {
|
||||||
|
nixosConfigurations.your-hostname = nixpkgs.lib.nixosSystem {
|
||||||
|
modules = [
|
||||||
|
immich.nixosModules.immich-docker
|
||||||
|
./configuration.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example for Warp Server
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{ config, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
(builtins.getFlake "path:${self}/appflakes/immich").nixosModules.immich-docker
|
||||||
|
];
|
||||||
|
|
||||||
|
services.immich-docker = {
|
||||||
|
enable = true;
|
||||||
|
domain = "warp.example.com";
|
||||||
|
port = 2283;
|
||||||
|
dataDir = "/mnt/userdata/immich";
|
||||||
|
dbPassword = "your-secure-password-here";
|
||||||
|
typesenseApiKey = "your-typesense-key-here";
|
||||||
|
enableWatchtower = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
### `services.immich-docker.enable` (boolean)
|
||||||
|
**Default:** `false`
|
||||||
|
|
||||||
|
Enable the Immich Docker Compose service.
|
||||||
|
|
||||||
|
### `services.immich-docker.domain` (string)
|
||||||
|
**Default:** `"immich.local"`
|
||||||
|
|
||||||
|
The domain name for the Immich web interface.
|
||||||
|
|
||||||
|
### `services.immich-docker.port` (port)
|
||||||
|
**Default:** `2283`
|
||||||
|
|
||||||
|
The port number for the Immich web interface.
|
||||||
|
|
||||||
|
### `services.immich-docker.dataDir` (path)
|
||||||
|
**Default:** `"/mnt/userdata/immich"`
|
||||||
|
|
||||||
|
Directory where Immich will store all data including:
|
||||||
|
- Uploaded photos and videos
|
||||||
|
- Processed images
|
||||||
|
- Database files
|
||||||
|
- Search indexes
|
||||||
|
|
||||||
|
### `services.immich-docker.dbPassword` (string)
|
||||||
|
**Default:** `"immich"`
|
||||||
|
|
||||||
|
PostgreSQL database password. **Change this in production!**
|
||||||
|
|
||||||
|
### `services.immich-docker.typesenseApiKey` (string)
|
||||||
|
**Default:** `"immich-typesense-secret-key-change-this"`
|
||||||
|
|
||||||
|
API key for the Typesense search engine. **Change this in production!**
|
||||||
|
|
||||||
|
### `services.immich-docker.enableWatchtower` (boolean)
|
||||||
|
**Default:** `false`
|
||||||
|
|
||||||
|
Enable automatic container updates via Watchtower. When enabled, Watchtower will update containers when new versions are available.
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
### Automatic Deployment
|
||||||
|
|
||||||
|
The NixOS module handles everything automatically:
|
||||||
|
|
||||||
|
1. Creates required directories
|
||||||
|
2. Generates docker-compose configuration
|
||||||
|
3. Configures systemd service
|
||||||
|
4. Starts all containers on boot
|
||||||
|
|
||||||
|
### Manual Service Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start Immich
|
||||||
|
sudo systemctl start docker-compose-immich
|
||||||
|
|
||||||
|
# Stop Immich
|
||||||
|
sudo systemctl stop docker-compose-immich
|
||||||
|
|
||||||
|
# Restart Immich
|
||||||
|
sudo systemctl restart docker-compose-immich
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
sudo systemctl status docker-compose-immich
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
sudo journalctl -u docker-compose-immich -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Container Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# View all Immich containers
|
||||||
|
docker ps | grep immich
|
||||||
|
|
||||||
|
# View logs for specific service
|
||||||
|
docker logs immich_server
|
||||||
|
|
||||||
|
# Execute commands in container
|
||||||
|
docker exec -it immich_server bash
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
This module deploys Immich as a Docker Compose stack with the following containers:
|
||||||
|
|
||||||
|
- **immich_server**: Web API and interface
|
||||||
|
- **immich_microservices**: Background jobs and machine learning
|
||||||
|
- **immich_postgres**: PostgreSQL database with pgvector
|
||||||
|
- **immich_redis**: Redis cache
|
||||||
|
- **immich_typesense**: Search engine
|
||||||
|
|
||||||
|
The stack is managed by systemd and starts automatically on boot.
|
||||||
|
|
||||||
|
## Access
|
||||||
|
|
||||||
|
Once deployed, access Immich at:
|
||||||
|
|
||||||
|
- **Web Interface:** `http://your-domain:port` (default: `http://localhost:2283`)
|
||||||
|
- **Mobile App:** Configure using your domain and port
|
||||||
|
|
||||||
|
## Storage Requirements
|
||||||
|
|
||||||
|
Plan for significant storage based on your photo/video collection:
|
||||||
|
|
||||||
|
- **High-res photos:** 5-15 MB per photo
|
||||||
|
- **Processed versions:** 2-5 MB additional per photo
|
||||||
|
- **Thumbnails:** ~500 KB per photo
|
||||||
|
- **4K videos:** 100-500 MB per minute
|
||||||
|
- **Database:** ~100-500 MB for 10,000 photos
|
||||||
|
|
||||||
|
**Recommended:**
|
||||||
|
- Start with at least 100GB for small collections
|
||||||
|
- 500GB+ for growing collections
|
||||||
|
- 1TB+ for serious photographers
|
||||||
|
|
||||||
|
## Security Recommendations
|
||||||
|
|
||||||
|
1. **Change Default Passwords**: Update `dbPassword` and `typesenseApiKey` in your NixOS configuration
|
||||||
|
2. **Reverse Proxy:** Use Nginx with SSL/TLS for production
|
||||||
|
3. **Firewall:** Restrict access to port 2283
|
||||||
|
4. **Backups:** Regularly backup the `dataDir`
|
||||||
|
5. **Updates**: Keep Immich containers updated (enable Watchtower or update the image tag in the module)
|
||||||
|
6. **Network:** Consider VPN access for remote management
|
||||||
|
|
||||||
|
## Backup Strategy
|
||||||
|
|
||||||
|
### Automated Backup Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
services.immich-docker.enable = true;
|
||||||
|
|
||||||
|
# Example: Regular backups using restic
|
||||||
|
services.restic.backups.immich = {
|
||||||
|
paths = [ "/mnt/userdata/immich" ];
|
||||||
|
repository = "s3:backup-bucket/immich";
|
||||||
|
passwordFile = "/etc/restic/password";
|
||||||
|
timerConfig = {
|
||||||
|
OnCalendar = "daily";
|
||||||
|
Persistent = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Important Backup Locations
|
||||||
|
|
||||||
|
Ensure these are included in backups:
|
||||||
|
- `${dataDir}/upload/` - Original photos and videos
|
||||||
|
- `${dataDir}/postgres/` - Database
|
||||||
|
- `${dataDir}/typesense/` - Search indexes
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Service Won't Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check service logs
|
||||||
|
sudo journalctl -u docker-compose-immich -n 100
|
||||||
|
|
||||||
|
# Check Docker status
|
||||||
|
sudo systemctl status docker
|
||||||
|
|
||||||
|
# Verify directories exist
|
||||||
|
ls -la /mnt/userdata/immich
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Connection Issues
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check PostgreSQL container
|
||||||
|
docker logs immich_postgres
|
||||||
|
|
||||||
|
# Test database connectivity
|
||||||
|
docker exec -it immich_postgres psql -U immich -d immich
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Issues
|
||||||
|
|
||||||
|
- Ensure adequate CPU (4+ cores recommended)
|
||||||
|
- Allocate sufficient RAM (8GB+ recommended)
|
||||||
|
- Monitor Docker resource usage
|
||||||
|
- Check storage space: `df -h /mnt/userdata`
|
||||||
|
|
||||||
|
### Port Already In Use
|
||||||
|
|
||||||
|
If port 2283 is already in use, change the port:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
services.immich.port = 8080; # Use alternative port
|
||||||
|
```
|
||||||
|
|
||||||
|
## Migration
|
||||||
|
|
||||||
|
### From Manual Docker Compose
|
||||||
|
|
||||||
|
1. Stop existing containers
|
||||||
|
2. Backup current data directory
|
||||||
|
3. Update NixOS configuration to use this module
|
||||||
|
4. Set `dataDir` to your current data directory
|
||||||
|
5. Apply new configuration
|
||||||
|
6. Start service: `sudo systemctl start docker-compose-immich`
|
||||||
|
|
||||||
|
## Version Updates
|
||||||
|
|
||||||
|
### Manual Updates
|
||||||
|
|
||||||
|
Update the Immich version by modifying the image tag in the flake:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# In flake.nix, change:
|
||||||
|
image: ghcr.io/immich-app/immich-server:v1.122.2
|
||||||
|
|
||||||
|
# To:
|
||||||
|
image: ghcr.io/immich-app/immich-server:v1.123.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Then rebuild the NixOS configuration.
|
||||||
|
|
||||||
|
### Automatic Updates
|
||||||
|
|
||||||
|
Enable Watchtower for automatic updates:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
services.immich = {
|
||||||
|
enable = true;
|
||||||
|
enableWatchtower = true; # Enables automatic container updates
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [Immich Official Website](https://immich.app)
|
||||||
|
- [Immich Documentation](https://immich.app/docs)
|
||||||
|
- [Immich GitHub](https://github.com/immich-app/immich)
|
||||||
|
- [Docker Compose Reference](https://docs.docker.com/compose/)
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For Immich-specific issues, refer to:
|
||||||
|
- [Immich GitHub Issues](https://github.com/immich-app/immich/issues)
|
||||||
|
- [Immich Discord Community](https://discord.gg/DSbk7SPrCj)
|
||||||
|
|
||||||
|
For NixOS module issues, check:
|
||||||
|
- Module configuration and syntax
|
||||||
|
- Docker integration
|
||||||
|
- Systemd service logs
|
||||||
|
|
||||||
|
## Version Updates
|
||||||
|
|
||||||
|
To update Immich to a newer version, modify the image tag in the flake:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# In appflakes/immich/flake.nix, change:
|
||||||
|
image: ghcr.io/immich-app/immich-server:v1.122.2
|
||||||
|
|
||||||
|
# To:
|
||||||
|
image: ghcr.io/immich-app/immich-server:v1.123.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Then rebuild your NixOS configuration.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This NixOS module follows the same license as the Immich project (AGPL-3.0).
|
||||||
61
appflakes/immich/flake.lock
generated
Executable file
61
appflakes/immich/flake.lock
generated
Executable file
|
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1770115704,
|
||||||
|
"narHash": "sha256-KHFT9UWOF2yRPlAnSXQJh6uVcgNcWlFqqiAZ7OVlHNc=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "e6eae2ee2110f3d31110d5c222cd395303343b08",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
209
appflakes/immich/flake.nix
Executable file
209
appflakes/immich/flake.nix
Executable file
|
|
@ -0,0 +1,209 @@
|
||||||
|
{
|
||||||
|
description = "Immich - Self-hosted photo and video backup solution (Docker Compose deployment)";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils }:
|
||||||
|
let
|
||||||
|
# NixOS module to integrate Immich with the system
|
||||||
|
nixosModule = { config, lib, pkgs, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.services.immich-docker;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.immich-docker = {
|
||||||
|
enable = mkEnableOption "Immich photo management server (Docker Compose deployment)";
|
||||||
|
|
||||||
|
domain = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "immich.local";
|
||||||
|
description = "Domain name for Immich web interface";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 2283;
|
||||||
|
description = "Port for Immich web interface";
|
||||||
|
};
|
||||||
|
|
||||||
|
dataDir = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "/mnt/userdata/immich";
|
||||||
|
description = "Directory for Immich data";
|
||||||
|
};
|
||||||
|
|
||||||
|
dbPassword = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "immich";
|
||||||
|
description = "PostgreSQL database password";
|
||||||
|
};
|
||||||
|
|
||||||
|
typesenseApiKey = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "immich-typesense-secret-key-change-this";
|
||||||
|
description = "Typesense search API key";
|
||||||
|
};
|
||||||
|
|
||||||
|
enableWatchtower = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Enable automatic container updates via Watchtower";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
# Check for conflicts with the built-in Immich module
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = !(config.services.immich.enable or false);
|
||||||
|
message = "services.immich-docker conflicts with services.immich. Please disable the default Nixpkgs Immich module by setting services.immich.enable = false.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
# Ensure Docker is available
|
||||||
|
virtualisation.docker.enable = mkDefault true;
|
||||||
|
|
||||||
|
# Install docker-compose
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
docker-compose
|
||||||
|
];
|
||||||
|
|
||||||
|
# Create required directories
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d /etc/compose/immich 0755 root root -"
|
||||||
|
"d ${cfg.dataDir}/upload 0755 root root -"
|
||||||
|
"d ${cfg.dataDir}/library 0755 root root -"
|
||||||
|
"d ${cfg.dataDir}/postgres 0755 root root -"
|
||||||
|
"d ${cfg.dataDir}/typesense 0755 root root -"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Generate docker-compose configuration
|
||||||
|
environment.etc."compose/immich/docker-compose.yml".text = ''
|
||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
immich_server:
|
||||||
|
image: ghcr.io/immich-app/immich-server:v1.122.2
|
||||||
|
container_name: immich_server
|
||||||
|
ports:
|
||||||
|
- "${toString cfg.port}:3000"
|
||||||
|
environment:
|
||||||
|
- DB_HOSTNAME=immich_postgres
|
||||||
|
- DB_USERNAME=immich
|
||||||
|
- DB_PASSWORD=${cfg.dbPassword}
|
||||||
|
- DB_DATABASE_NAME=immich
|
||||||
|
- REDIS_HOSTNAME=immich_redis
|
||||||
|
- TYPESENSE_HOST=immich_typesense
|
||||||
|
- IMMICH_WEB_URL=http://localhost:${toString cfg.port}
|
||||||
|
- DISABLE_REVERSE_GEOCODING=false
|
||||||
|
volumes:
|
||||||
|
- ${cfg.dataDir}/upload:/usr/src/app/upload
|
||||||
|
- ${cfg.dataDir}/library:/usr/src/app/library
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
depends_on:
|
||||||
|
- immich_postgres
|
||||||
|
- immich_redis
|
||||||
|
- immich_typesense
|
||||||
|
restart: unless-stopped
|
||||||
|
${if cfg.enableWatchtower then ''labels:
|
||||||
|
com.centurylinklabs.watchtower.enable: "true"'' else ""}
|
||||||
|
|
||||||
|
immich_microservices:
|
||||||
|
image: ghcr.io/immich-app/immich-server:v1.122.2
|
||||||
|
container_name: immich_microservices
|
||||||
|
command: ["start-microservices.sh"]
|
||||||
|
environment:
|
||||||
|
- DB_HOSTNAME=immich_postgres
|
||||||
|
- DB_USERNAME=immich
|
||||||
|
- DB_PASSWORD=${cfg.dbPassword}
|
||||||
|
- DB_DATABASE_NAME=immich
|
||||||
|
- REDIS_HOSTNAME=immich_redis
|
||||||
|
- TYPESENSE_HOST=immich_typesense
|
||||||
|
- DISABLE_REVERSE_GEOCODING=false
|
||||||
|
volumes:
|
||||||
|
- ${cfg.dataDir}/upload:/usr/src/app/upload
|
||||||
|
- ${cfg.dataDir}/library:/usr/src/app/library
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
depends_on:
|
||||||
|
- immich_postgres
|
||||||
|
- immich_redis
|
||||||
|
- immich_typesense
|
||||||
|
restart: unless-stopped
|
||||||
|
${if cfg.enableWatchtower then ''labels:
|
||||||
|
com.centurylinklabs.watchtower.enable: "true"'' else ""}
|
||||||
|
|
||||||
|
immich_postgres:
|
||||||
|
image: tensorchord/pgvecto-rs:pg14-v0.2.0
|
||||||
|
container_name: immich_postgres
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=immich
|
||||||
|
- POSTGRES_PASSWORD=${cfg.dbPassword}
|
||||||
|
- POSTGRES_DB=immich
|
||||||
|
volumes:
|
||||||
|
- ${cfg.dataDir}/postgres:/var/lib/postgresql/data
|
||||||
|
restart: unless-stopped
|
||||||
|
${if cfg.enableWatchtower then ''labels:
|
||||||
|
com.centurylinklabs.watchtower.enable: "true"'' else ""}
|
||||||
|
|
||||||
|
immich_redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
container_name: immich_redis
|
||||||
|
restart: unless-stopped
|
||||||
|
${if cfg.enableWatchtower then ''labels:
|
||||||
|
com.centurylinklabs.watchtower.enable: "true"'' else ""}
|
||||||
|
|
||||||
|
immich_typesense:
|
||||||
|
image: typesense/typesense:0.24.0
|
||||||
|
container_name: immich_typesense
|
||||||
|
environment:
|
||||||
|
- TYPESENSE_API_KEY=${cfg.typesenseApiKey}
|
||||||
|
- TYPESENSE_DATA_DIR=/data
|
||||||
|
volumes:
|
||||||
|
- ${cfg.dataDir}/typesense:/data
|
||||||
|
restart: unless-stopped
|
||||||
|
${if cfg.enableWatchtower then ''labels:
|
||||||
|
com.centurylinklabs.watchtower.enable: "true"'' else ""}
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
name: immich_network
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Systemd service to manage Immich docker-compose stack
|
||||||
|
systemd.services.docker-compose-immich = {
|
||||||
|
description = "Immich Docker Compose Stack";
|
||||||
|
after = [ "docker.service" "network-online.target" ];
|
||||||
|
wants = [ "network-online.target" ];
|
||||||
|
requiredBy = [ "multi-user.target" ];
|
||||||
|
path = [ pkgs.docker-compose pkgs.docker ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = "yes";
|
||||||
|
WorkingDirectory = "/etc/compose/immich";
|
||||||
|
ExecStart = "${pkgs.docker-compose}/bin/docker-compose up -d";
|
||||||
|
ExecStop = "${pkgs.docker-compose}/bin/docker-compose down";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Network firewall configuration (optional, enable if using firewall)
|
||||||
|
# networking.firewall.allowedTCPPorts = [ cfg.port ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Provide NixOS module
|
||||||
|
nixosModules.immich-docker = nixosModule;
|
||||||
|
|
||||||
|
# Provide the module as the default package
|
||||||
|
packages = flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
{
|
||||||
|
immich-docker-module = nixosModule;
|
||||||
|
default = nixosModule;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
64
appflakes/octofriend/README.md
Executable file
64
appflakes/octofriend/README.md
Executable file
|
|
@ -0,0 +1,64 @@
|
||||||
|
# Octofriend flake for Nix/NixOS
|
||||||
|
|
||||||
|
This flake packages [octofriend](https://github.com/synthetic-lab/octofriend), a CLI coding assistant.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix build .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix run .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Add to your NixOS configuration:
|
||||||
|
```nix
|
||||||
|
inputs.octofriend.url = "path:/home/jsutter/src/nixos/appflakes/octofriend";
|
||||||
|
|
||||||
|
# Then in your environment.systemPackages or home.packages:
|
||||||
|
self.inputs.octofriend.packages.${system}.default
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
The octofriend flake includes default configuration files at `/share/octofriend/config/octofriend.json5`. However, API keys should not be stored in the repository for security reasons.
|
||||||
|
|
||||||
|
### Default Configuration
|
||||||
|
|
||||||
|
The included configuration provides:
|
||||||
|
- Your name as 'Jules'
|
||||||
|
- Two model configurations (GLM-4.6 and MiniMax M2) pointing to https://api.synthetic.new
|
||||||
|
- Specialized models for diff-apply and fix-json operations
|
||||||
|
|
||||||
|
### API Keys
|
||||||
|
|
||||||
|
To configure API keys, create a `keys.json5` file in your configuration directory:
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
'https://api.synthetic.new/v1': 'your-api-key-here'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When using octofriend, you can either:
|
||||||
|
1. Place your keys in `~/.config/octofriend/keys.json5` (default location)
|
||||||
|
2. Set the `OCTOFRIEND_CONFIG_DIR` environment variable to a custom directory containing your files
|
||||||
|
|
||||||
|
This ensures sensitive API keys are not committed to the repository while still providing functional default configuration.
|
||||||
|
|
||||||
|
### Custom Configuration
|
||||||
|
|
||||||
|
If you want to override the default configuration, you can create your own `octofriend.json5` file in your config directory. The wrapper script will check for configuration files in the following order:
|
||||||
|
1. `$OCTOFRIEND_CONFIG_DIR/octofriend.json5` (if OCTOFRIEND_CONFIG_DIR is set)
|
||||||
|
2. `~/.config/octofriend/octofriend.json5`
|
||||||
|
3. Fall back to the included default at `$OCTOFRIEND_PACKAGE_DIR/share/octofriend/config/octofriend.json5`
|
||||||
|
|
||||||
|
## Security Note
|
||||||
|
|
||||||
|
- The `keys.json5` file containing API keys is intentionally NOT included in this flake
|
||||||
|
- Never commit API keys to any repository
|
||||||
|
- The included configuration uses placeholder settings and should be customized with your actual model preferences and endpoints
|
||||||
78
appflakes/octofriend/flake.lock
generated
Executable file
78
appflakes/octofriend/flake.lock
generated
Executable file
|
|
@ -0,0 +1,78 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1765779637,
|
||||||
|
"narHash": "sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "1306659b587dc277866c7b69eb97e5f07864d8c4",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"octofriend": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1766013652,
|
||||||
|
"narHash": "sha256-X7t2CGNZP+OS2pbMtRAg3XIGq3HA3N3rPk5ELV1FNRQ=",
|
||||||
|
"owner": "synthetic-lab",
|
||||||
|
"repo": "octofriend",
|
||||||
|
"rev": "6d9c260743e4b645fd4b216ce1cd979d5c0dd473",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "synthetic-lab",
|
||||||
|
"repo": "octofriend",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"octofriend": "octofriend"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
92
appflakes/octofriend/flake.nix
Executable file
92
appflakes/octofriend/flake.nix
Executable file
|
|
@ -0,0 +1,92 @@
|
||||||
|
{
|
||||||
|
description = "Octofriend (synthetic-lab/octofriend) packaged for Nix/NixOS";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
|
||||||
|
# Upstream source (not a flake)
|
||||||
|
octofriend = {
|
||||||
|
url = "github:synthetic-lab/octofriend";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils, octofriend }:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
lib = pkgs.lib;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages.default = pkgs.buildNpmPackage {
|
||||||
|
pname = "octofriend";
|
||||||
|
version = "git-${builtins.substring 0 7 (octofriend.rev or "unknown")}";
|
||||||
|
|
||||||
|
src = octofriend;
|
||||||
|
|
||||||
|
# First build will fail telling you the correct value. Paste it here.
|
||||||
|
npmDepsHash = "sha256-luAdTozvb0MTuh+xa5qhZPH8YflF9R4GtREAaR8m6Y8=";
|
||||||
|
|
||||||
|
# Ensure a modern node (octofriend is a Node CLI). :contentReference[oaicite:1]{index=1}
|
||||||
|
nodejs = pkgs.nodejs_22;
|
||||||
|
|
||||||
|
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||||
|
|
||||||
|
# If octofriend shells out to git/ssh/etc, keep PATH sane.
|
||||||
|
postInstall = ''
|
||||||
|
if [ -x "$out/bin/octofriend" ]; then
|
||||||
|
wrapProgram "$out/bin/octofriend" \
|
||||||
|
--prefix PATH : ${lib.makeBinPath [ pkgs.git pkgs.openssh ]} \
|
||||||
|
--set OCTOFRIEND_CONFIG_DIR "$out/share/octofriend/config"
|
||||||
|
fi
|
||||||
|
# Provide the short alias if upstream didn't already.
|
||||||
|
if [ -x "$out/bin/octofriend" ] && [ ! -e "$out/bin/octo" ]; then
|
||||||
|
ln -s "$out/bin/octofriend" "$out/bin/octo"
|
||||||
|
fi
|
||||||
|
# Install the config file
|
||||||
|
mkdir -p "$out/share/octofriend/config"
|
||||||
|
cat << 'EOF' > "$out/share/octofriend/config/octofriend.json5"
|
||||||
|
{
|
||||||
|
yourName: 'Jules',
|
||||||
|
models: [
|
||||||
|
{
|
||||||
|
model: 'hf:zai-org/GLM-4.6',
|
||||||
|
nickname: 'GLM-4.6 (Synthetic)',
|
||||||
|
context: 131072,
|
||||||
|
baseUrl: 'https://api.synthetic.new/v1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'hf:MiniMaxAI/MiniMax-M2',
|
||||||
|
nickname: 'MiniMax M2 (Synthetic)',
|
||||||
|
context: 98304,
|
||||||
|
baseUrl: 'https://api.synthetic.new/v1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
defaultApiKeyOverrides: {},
|
||||||
|
diffApply: {
|
||||||
|
baseUrl: 'https://api.synthetic.new/v1',
|
||||||
|
model: 'hf:syntheticlab/diff-apply',
|
||||||
|
},
|
||||||
|
fixJson: {
|
||||||
|
baseUrl: 'https://api.synthetic.new/v1',
|
||||||
|
model: 'hf:syntheticlab/fix-json',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
description = "Octofriend CLI coding assistant";
|
||||||
|
homepage = "https://github.com/synthetic-lab/octofriend";
|
||||||
|
license = licenses.mit;
|
||||||
|
mainProgram = "octofriend";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
apps.default = flake-utils.lib.mkApp {
|
||||||
|
drv = self.packages.${system}.default;
|
||||||
|
exePath = "/bin/octofriend";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue