- nix.md: TimescaleDB now enabled for both dev and test databases - nix.md: added troubleshooting section for TimescaleDB reinitialization - New: docs/features/organization-settings.md (tabbed interface, all tabs documented) - New: docs/features/device-monitoring.md (schema field reference, activity feed fields, SNMP socket management)
648 lines
14 KiB
Markdown
648 lines
14 KiB
Markdown
# Nix Flakes Guide for Towerops Web
|
|
|
|
This document provides comprehensive guidance for using Nix flakes with the towerops-web project.
|
|
|
|
## Table of Contents
|
|
|
|
- [Overview](#overview)
|
|
- [Quick Start](#quick-start)
|
|
- [Installation](#installation)
|
|
- [Development Workflow](#development-workflow)
|
|
- [Building](#building)
|
|
- [CI/CD Integration](#cicd-integration)
|
|
- [Cachix Setup](#cachix-setup)
|
|
- [Troubleshooting](#troubleshooting)
|
|
- [Updating Dependencies](#updating-dependencies)
|
|
|
|
## Overview
|
|
|
|
The towerops-web project uses Nix flakes for:
|
|
|
|
- **Reproducible builds**: Identical builds across development, CI, and production
|
|
- **Development environment**: One command (`nix develop`) provides fully configured environment
|
|
- **Docker images**: Pure Nix OCI images (~150-200 MB vs ~500 MB Debian-based)
|
|
- **Binary caching**: Cachix eliminates redundant compilation across machines
|
|
- **Dependency management**: All dependencies pinned in `flake.lock`
|
|
|
|
### Architecture
|
|
|
|
```
|
|
flake.nix # Main flake definition
|
|
├── nix/
|
|
│ ├── c-nif.nix # C NIF shared library (cached separately)
|
|
│ ├── build.nix # Mix release derivation
|
|
│ ├── docker.nix # OCI image using dockerTools.buildLayeredImage
|
|
│ └── shell.nix # Development shell with PostgreSQL, Redis, LSPs
|
|
├── flake.lock # Locked dependency versions
|
|
├── .envrc.example # direnv configuration example
|
|
└── shell.nix # Compatibility shim for nix-shell
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
### With Nix Installed
|
|
|
|
```bash
|
|
# Enter development environment (auto-starts PostgreSQL and Redis)
|
|
nix develop
|
|
|
|
# Or with direnv (automatic on cd)
|
|
cp .envrc.example .envrc
|
|
direnv allow
|
|
|
|
# Start Phoenix server
|
|
mix phx.server
|
|
```
|
|
|
|
### First-Time Setup
|
|
|
|
1. **Install Nix** (if not already installed):
|
|
```bash
|
|
# macOS or Linux
|
|
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
|
|
```
|
|
|
|
2. **Clone the repository**:
|
|
```bash
|
|
git clone <repository-url>
|
|
cd towerops-web
|
|
```
|
|
|
|
3. **Enter development environment**:
|
|
```bash
|
|
nix develop
|
|
```
|
|
|
|
On first run, this will:
|
|
- Download and cache all dependencies
|
|
- Initialize PostgreSQL in `.nix-postgres/`
|
|
- Start Redis in `.nix-redis/`
|
|
- Create `towerops_dev` and `towerops_test` databases
|
|
- Run database migrations
|
|
- Install pre-commit hooks
|
|
|
|
4. **Start developing**:
|
|
```bash
|
|
mix phx.server
|
|
# Navigate to http://localhost:4000
|
|
```
|
|
|
|
## Installation
|
|
|
|
### Nix Installation
|
|
|
|
#### macOS
|
|
|
|
```bash
|
|
# Using Determinate Systems installer (recommended)
|
|
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
|
|
|
|
# Or using official installer
|
|
sh <(curl -L https://nixos.org/nix/install)
|
|
```
|
|
|
|
#### Linux
|
|
|
|
```bash
|
|
# Using Determinate Systems installer (recommended)
|
|
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
|
|
|
|
# Or using official installer
|
|
sh <(curl -L https://nixos.org/nix/install) --daemon
|
|
```
|
|
|
|
#### Verify Installation
|
|
|
|
```bash
|
|
nix --version
|
|
# Should output: nix (Nix) 2.x.x
|
|
```
|
|
|
|
### direnv Installation (Optional but Recommended)
|
|
|
|
direnv automatically loads the Nix environment when you `cd` into the project directory.
|
|
|
|
```bash
|
|
# macOS
|
|
brew install direnv
|
|
|
|
# Linux (Ubuntu/Debian)
|
|
sudo apt-get install direnv
|
|
|
|
# Add to your shell rc file (~/.bashrc, ~/.zshrc, etc.)
|
|
eval "$(direnv hook bash)" # or zsh, fish, etc.
|
|
```
|
|
|
|
Then in the project directory:
|
|
|
|
```bash
|
|
cp .envrc.example .envrc
|
|
direnv allow
|
|
```
|
|
|
|
## Development Workflow
|
|
|
|
### Entering the Development Environment
|
|
|
|
**With direnv** (automatic):
|
|
```bash
|
|
cd /path/to/towerops-web
|
|
# Environment loads automatically
|
|
```
|
|
|
|
**Without direnv** (manual):
|
|
```bash
|
|
nix develop
|
|
```
|
|
|
|
### Available Services
|
|
|
|
The development shell automatically starts:
|
|
|
|
- **PostgreSQL 16 + TimescaleDB**: Runs on `localhost:5432`
|
|
- Databases: `towerops_dev`, `towerops_test`
|
|
- Data directory: `.nix-postgres/`
|
|
- TimescaleDB extension enabled for both dev and test databases
|
|
- PostgreSQL built with `withPackages` to include TimescaleDB
|
|
- `shared_preload_libraries = 'timescaledb'` set in config
|
|
|
|
- **Redis**: Runs on `localhost:6379`
|
|
- Data directory: `.nix-redis/`
|
|
- No persistence (dev mode)
|
|
|
|
### Service Management
|
|
|
|
```bash
|
|
# Start services (if not auto-started)
|
|
start-services
|
|
|
|
# Stop services
|
|
stop-services
|
|
|
|
# Check if services are running
|
|
pg_isready -h localhost -p 5432
|
|
redis-cli ping
|
|
```
|
|
|
|
### Environment Variables
|
|
|
|
Automatically set in the dev shell:
|
|
|
|
- `DATABASE_URL`: PostgreSQL connection string
|
|
- `REDIS_URL`: Redis connection string
|
|
- `SECRET_KEY_BASE`: Development secret (DO NOT use in production)
|
|
- `CLOAK_KEY`: Development encryption key (DO NOT use in production)
|
|
- `MIX_ENV`: Set to `dev`
|
|
- `PHX_HOST`: Set to `localhost`
|
|
- `PORT`: Set to `4000`
|
|
|
|
### Pre-commit Hooks
|
|
|
|
Automatically installed in the dev shell:
|
|
|
|
- `mix format --check-formatted` - Elixir code formatting
|
|
- `mix credo --strict` - Elixir linting
|
|
- `nixfmt` - Nix file formatting
|
|
- `shellcheck` - Shell script linting
|
|
|
|
Run manually:
|
|
```bash
|
|
pre-commit run --all-files
|
|
```
|
|
|
|
## Building
|
|
|
|
### Build the Elixir Release
|
|
|
|
```bash
|
|
nix build .#towerops
|
|
# Output in ./result/
|
|
|
|
# Run the release
|
|
./result/bin/towerops start
|
|
|
|
# Or use RPC
|
|
./result/bin/towerops rpc "1 + 1"
|
|
```
|
|
|
|
### Build the Docker Image
|
|
|
|
```bash
|
|
nix build .#dockerImage
|
|
# Output in ./result
|
|
|
|
# Load into Docker
|
|
docker load < result
|
|
|
|
# Run the container
|
|
docker run --rm -p 4000:4000 \
|
|
-e DATABASE_URL="..." \
|
|
-e SECRET_KEY_BASE="..." \
|
|
registry.gitlab.com/towerops/towerops:latest
|
|
```
|
|
|
|
### Build the C NIF Separately
|
|
|
|
```bash
|
|
nix build .#towerops-nif
|
|
# Output in ./result/lib/towerops_nif.so
|
|
|
|
# Check dependencies
|
|
ldd result/lib/towerops_nif.so
|
|
```
|
|
|
|
### Check Build Closure Size
|
|
|
|
```bash
|
|
# Show all runtime dependencies and sizes
|
|
nix path-info -rS .#dockerImage
|
|
|
|
# Show only the image size
|
|
nix path-info -S .#dockerImage
|
|
```
|
|
|
|
## CI/CD Integration
|
|
|
|
### GitLab CI Configuration
|
|
|
|
The project includes a Nix-based GitLab CI configuration in `.gitlab-ci.yml.nix`.
|
|
|
|
**Requirements**:
|
|
- GitLab Runner with Nix installed
|
|
- Docker socket mounted (for `docker load`)
|
|
- Cachix authentication token in `CACHIX_AUTH_TOKEN` variable
|
|
|
|
### Setting Up NixOS GitLab Runner
|
|
|
|
1. **Install GitLab Runner on NixOS**:
|
|
```nix
|
|
# /etc/nixos/configuration.nix
|
|
services.gitlab-runner = {
|
|
enable = true;
|
|
services = {
|
|
nix = {
|
|
registrationConfigFile = "/etc/gitlab-runner/registration";
|
|
dockerImage = "nixos/nix:latest";
|
|
dockerVolumes = [
|
|
"/nix/store:/nix/store:ro"
|
|
"/nix/var/nix/db:/nix/var/nix/db:ro"
|
|
"/nix/var/nix/daemon-socket:/nix/var/nix/daemon-socket:ro"
|
|
];
|
|
tagList = [ "nix" ];
|
|
};
|
|
};
|
|
};
|
|
```
|
|
|
|
2. **Enable experimental features**:
|
|
```nix
|
|
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
|
```
|
|
|
|
### Activating Nix CI/CD
|
|
|
|
Once the NixOS runner is configured:
|
|
|
|
```bash
|
|
# Backup current CI config
|
|
mv .gitlab-ci.yml .gitlab-ci.yml.docker
|
|
|
|
# Activate Nix CI config
|
|
mv .gitlab-ci.yml.nix .gitlab-ci.yml
|
|
|
|
# Commit and push
|
|
git add .gitlab-ci.yml
|
|
git commit -m "ci: migrate to Nix builds"
|
|
git push
|
|
```
|
|
|
|
## Cachix Setup
|
|
|
|
Cachix provides binary caching to speed up builds across machines.
|
|
|
|
### 1. Create Cachix Account
|
|
|
|
Visit https://cachix.org and sign up.
|
|
|
|
### 2. Create Cache
|
|
|
|
```bash
|
|
# Install cachix
|
|
nix-env -iA cachix -f https://cachix.org/api/v1/install
|
|
|
|
# Create cache
|
|
cachix create towerops
|
|
|
|
# Generate keypair
|
|
cachix generate-keypair towerops
|
|
|
|
# Get public key
|
|
cachix get towerops
|
|
# Save the public key output
|
|
```
|
|
|
|
### 3. Update flake.nix
|
|
|
|
Replace the placeholder public key in `flake.nix`:
|
|
|
|
```nix
|
|
nixConfig = {
|
|
extra-substituters = [
|
|
"https://cache.nixos.org"
|
|
"https://towerops.cachix.org"
|
|
];
|
|
extra-trusted-public-keys = [
|
|
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
|
|
"towerops.cachix.org-1:YOUR_ACTUAL_PUBLIC_KEY_HERE"
|
|
];
|
|
};
|
|
```
|
|
|
|
### 4. Configure Local Machine
|
|
|
|
```bash
|
|
cachix use towerops
|
|
```
|
|
|
|
This updates `~/.config/nix/nix.conf` to use the cache.
|
|
|
|
### 5. Push Builds to Cache
|
|
|
|
```bash
|
|
# Push a specific build
|
|
nix build .#dockerImage
|
|
nix path-info --json .#dockerImage | jq -r '.[].path' | cachix push towerops
|
|
|
|
# Push all outputs
|
|
nix flake show --json | jq -r '.packages."x86_64-linux" | keys[]' | \
|
|
xargs -I {} nix build .#{} --print-build-logs
|
|
nix flake show --json | jq -r '.packages."x86_64-linux" | keys[]' | \
|
|
xargs -I {} nix path-info --json .#{} | jq -r '.[].path' | cachix push towerops
|
|
```
|
|
|
|
### 6. Configure CI/CD
|
|
|
|
Add `CACHIX_AUTH_TOKEN` to GitLab CI/CD variables:
|
|
|
|
1. Go to GitLab project → Settings → CI/CD → Variables
|
|
2. Add variable:
|
|
- Key: `CACHIX_AUTH_TOKEN`
|
|
- Value: (get from `cachix authtoken`)
|
|
- Masked: Yes
|
|
- Protected: Yes (optional)
|
|
|
|
## Troubleshooting
|
|
|
|
### Services Won't Start
|
|
|
|
**PostgreSQL fails to initialize**:
|
|
```bash
|
|
# Remove corrupted data directory
|
|
rm -rf .nix-postgres
|
|
# Re-enter shell to reinitialize
|
|
exit
|
|
nix develop
|
|
```
|
|
|
|
**Port already in use**:
|
|
```bash
|
|
# Check what's using the port
|
|
lsof -i :5432 # PostgreSQL
|
|
lsof -i :6379 # Redis
|
|
|
|
# Kill the process or change port in nix/shell.nix
|
|
```
|
|
|
|
### Nix Build Failures
|
|
|
|
**Error: experimental feature 'flakes' is not enabled**:
|
|
```bash
|
|
# Add to ~/.config/nix/nix.conf
|
|
experimental-features = nix-command flakes
|
|
|
|
# Or use --extra-experimental-features
|
|
nix --extra-experimental-features "nix-command flakes" develop
|
|
```
|
|
|
|
**Error: out of disk space**:
|
|
```bash
|
|
# Clean old generations
|
|
nix-collect-garbage -d
|
|
|
|
# Clean build artifacts
|
|
nix-store --gc
|
|
```
|
|
|
|
**C NIF compilation fails**:
|
|
```bash
|
|
# Check net-snmp is available
|
|
nix-shell -p net-snmp --run "pkg-config --libs netsnmp"
|
|
|
|
# Rebuild NIF separately
|
|
nix build .#towerops-nif --rebuild
|
|
```
|
|
|
|
### TimescaleDB Not Loading
|
|
|
|
If TimescaleDB was added after initial setup, you need to reinitialize PostgreSQL:
|
|
|
|
```bash
|
|
# Stop services and remove old data directory
|
|
stop-services
|
|
rm -rf .nix-postgres .nix-services-started
|
|
|
|
# Re-enter shell to reinitialize with TimescaleDB
|
|
exit
|
|
nix develop
|
|
```
|
|
|
|
This is required because `shared_preload_libraries` is set during `initdb` and the TimescaleDB shared library must be available in the PostgreSQL package.
|
|
|
|
### Mix Dependencies
|
|
|
|
**Error: Mix dependencies not found**:
|
|
```bash
|
|
# Ensure flake.lock is up to date
|
|
nix flake update
|
|
|
|
# Rebuild with fresh dependencies
|
|
nix build .#towerops --rebuild
|
|
```
|
|
|
|
**Vendored Oban packages**:
|
|
The `vendor/` directory is included in the Nix build. If you update vendored packages:
|
|
|
|
```bash
|
|
# Rebuild to pick up changes
|
|
nix build .#towerops --rebuild
|
|
```
|
|
|
|
### Docker Image Issues
|
|
|
|
**Image too large**:
|
|
```bash
|
|
# Check closure size
|
|
nix path-info -rS .#dockerImage
|
|
|
|
# Identify large dependencies
|
|
nix path-info -rS .#dockerImage | sort -k2 -h | tail -20
|
|
```
|
|
|
|
**Missing files in image**:
|
|
```bash
|
|
# List image contents
|
|
docker run --rm registry.gitlab.com/towerops/towerops:latest find / -name "*.mib" | head
|
|
```
|
|
|
|
### direnv Issues
|
|
|
|
**direnv: error .envrc is blocked**:
|
|
```bash
|
|
direnv allow
|
|
```
|
|
|
|
**Environment not loading**:
|
|
```bash
|
|
# Check direnv status
|
|
direnv status
|
|
|
|
# Reload manually
|
|
direnv reload
|
|
```
|
|
|
|
## Updating Dependencies
|
|
|
|
### Update Nix Flake Inputs
|
|
|
|
```bash
|
|
# Update all inputs
|
|
nix flake update
|
|
|
|
# Update specific input
|
|
nix flake update nixpkgs
|
|
|
|
# Update and rebuild
|
|
nix flake update && nix build .#towerops
|
|
```
|
|
|
|
### Update Elixir/Erlang Version
|
|
|
|
Edit `nix/shell.nix` and `nix/build.nix` to use different versions:
|
|
|
|
```nix
|
|
# Before
|
|
elixir
|
|
erlang_28
|
|
|
|
# After (if nixpkgs has newer versions)
|
|
elixir_1_20
|
|
erlang_29
|
|
```
|
|
|
|
Then rebuild:
|
|
```bash
|
|
nix flake update nixpkgs
|
|
nix build .#towerops --rebuild
|
|
```
|
|
|
|
### Pin Specific Nixpkgs Version
|
|
|
|
Edit `flake.nix` to pin to a specific commit:
|
|
|
|
```nix
|
|
inputs = {
|
|
# Pin to specific nixpkgs commit
|
|
nixpkgs.url = "github:NixOS/nixpkgs/abc123def456";
|
|
# Or pin to a release branch
|
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
|
|
};
|
|
```
|
|
|
|
### Update Mix Dependencies
|
|
|
|
Mix dependencies are managed in `mix.exs` and `mix.lock` as usual. Nix will use these files:
|
|
|
|
```bash
|
|
# Update Mix dependencies
|
|
mix deps.update --all
|
|
|
|
# Commit updated mix.lock
|
|
git add mix.lock
|
|
git commit -m "deps: update Elixir dependencies"
|
|
|
|
# Rebuild with new dependencies
|
|
nix build .#towerops --rebuild
|
|
```
|
|
|
|
## Advanced Topics
|
|
|
|
### Cross-Platform Builds
|
|
|
|
Build for multiple architectures:
|
|
|
|
```bash
|
|
# Build for Linux ARM64 (Apple Silicon servers)
|
|
nix build .#packages.aarch64-linux.dockerImage
|
|
|
|
# Build for macOS on Linux (if configured)
|
|
nix build .#packages.x86_64-darwin.towerops
|
|
```
|
|
|
|
### Custom Nix Overlays
|
|
|
|
Create `nix/overlays.nix` to override nixpkgs packages:
|
|
|
|
```nix
|
|
final: prev: {
|
|
# Example: Use specific Elixir version
|
|
elixir = prev.elixir.override {
|
|
version = "1.20.0";
|
|
};
|
|
}
|
|
```
|
|
|
|
Reference in `flake.nix`:
|
|
|
|
```nix
|
|
_module.args.pkgs = import nixpkgs {
|
|
inherit system;
|
|
overlays = [ (import ./nix/overlays.nix) ];
|
|
};
|
|
```
|
|
|
|
### Development Without Nix
|
|
|
|
If Nix is not available, use the traditional development setup:
|
|
|
|
```bash
|
|
# Install Elixir via asdf
|
|
asdf install erlang 28.3
|
|
asdf install elixir 1.19.5-otp-28
|
|
|
|
# Install PostgreSQL and Redis
|
|
brew install postgresql@16 redis # macOS
|
|
# Or: sudo apt-get install postgresql-16 redis # Linux
|
|
|
|
# Start services manually
|
|
brew services start postgresql@16
|
|
brew services start redis
|
|
|
|
# Run development server
|
|
mix deps.get
|
|
mix ecto.setup
|
|
mix phx.server
|
|
```
|
|
|
|
## Resources
|
|
|
|
- [Nix Flakes Documentation](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake.html)
|
|
- [NixOS Packages Search](https://search.nixos.org/packages)
|
|
- [Cachix Documentation](https://docs.cachix.org/)
|
|
- [direnv Documentation](https://direnv.net/)
|
|
- [Elixir on Nix](https://nixos.wiki/wiki/Elixir)
|
|
|
|
## Getting Help
|
|
|
|
- **Nix Discord**: https://discord.gg/RbvHtGa
|
|
- **NixOS Discourse**: https://discourse.nixos.org/
|
|
- **Project Issues**: File issues on GitLab for project-specific problems
|