Files
ngit-grasp/b454-nixos-module-execstartpre-datadir.md
T

6.7 KiB

NixOS module should auto-create data directories in ExecStartPre

ID: b454

Problem

When adding a new ngit-grasp instance with a custom dataDir (e.g., /persistent/grasp/archive), the service fails to start with:

status=226/NAMESPACE
ngit-grasp-archive.service: Failed to set up mount namespacing: /persistent/grasp/archive: No such file or directory

Root cause: The NixOS module creates tmpfiles rules for the data directory, but these rules aren't automatically executed during nixos-rebuild switch. Users must manually run sudo systemd-tmpfiles --create --prefix=/path/to/dataDir.

Plan

  • Phase 1: Add ExecStartPre to systemd service configuration in nix/module.nix
  • Phase 2: Create directories before service starts (dataDir, dataDir/git, dataDir/relay)
  • Phase 3: Set proper ownership and permissions
  • Phase 4: Investigate if there are specific custom path options for git and relay data directories
    • Confirmed: No separate options exist; git/relay paths are derived from dataDir
  • Phase 5: Update ExecStartPre logic to handle those paths if they exist
    • N/A: Paths are always ${dataDir}/git and ${dataDir}/relay

Implementation

Add to nix/module.nix in the mkService function:

serviceConfig = {
  # ... existing config ...
  
  # Ensure data directories exist before service starts
  ExecStartPre = [
    "+${pkgs.coreutils}/bin/mkdir -p '${cfg.dataDir}'"
    "+${pkgs.coreutils}/bin/mkdir -p '${cfg.dataDir}/git'"
    "+${pkgs.coreutils}/bin/mkdir -p '${cfg.dataDir}/relay'"
    "+${pkgs.coreutils}/bin/chown -R ${cfg.user}:${cfg.group} '${cfg.dataDir}'"
    "+${pkgs.coreutils}/bin/chmod 750 '${cfg.dataDir}'"
    "+${pkgs.coreutils}/bin/chmod 750 '${cfg.dataDir}/git'"
    "+${pkgs.coreutils}/bin/chmod 750 '${cfg.dataDir}/relay'"
  ];
};

Benefits

  • Guarantees directories exist before service starts
  • Works for any path (including /persistent/, /var/lib/, etc.)
  • Follows established NixOS patterns (silverbullet, icecast, logstash)
  • Negligible performance impact
  • Eliminates confusing failure mode

Progress

2026-01-20 [Session 10:30] - Verification Complete

  • Completed: All verification tests passed successfully
  • Verified: ExecStartPre directives work correctly on NixOS deployment
  • Tested: Fresh install, idempotency, permission correction, ownership correction
  • Result: Service starts successfully with auto-created directories
  • Status: Ready to merge to main

2026-01-20 [Session 09:00] - Testing Strategy Review

  • Reviewed: Implementation in commit f93fc0a691
  • Designed: Comprehensive testing strategy for NixOS deployment

Testing Strategy

Approach: Add a test ngit-grasp instance with archiveAll = true to the user's NixOS configuration. This will:

  1. Test the ExecStartPre directory creation with a fresh custom dataDir
  2. Exercise the git directory creation when archive mode accepts repositories
  3. Validate permissions are correct for service operation

Test Scenarios:

  1. Fresh Install (Primary Test)

    • Add new instance with non-existent custom dataDir (e.g., /persistent/grasp/test-archive)
    • Run nixos-rebuild switch
    • Expected: Service starts successfully, directories created with correct ownership
  2. Existing Directories (Idempotency)

    • Restart the service after initial setup
    • Expected: ExecStartPre completes without errors, permissions unchanged
  3. Permission Correction

    • Manually change directory permissions (e.g., chmod 777)
    • Restart service
    • Expected: Permissions reset to 750
  4. Ownership Correction

    • Manually change ownership (e.g., chown root:root)
    • Restart service
    • Expected: Ownership reset to service user:group

Recommended Test Configuration:

# Add to NixOS configuration alongside existing ngit-grasp instances
services.ngit-grasp.test-archive = {
  enable = true;
  domain = "test-archive.localhost";  # Won't be publicly accessible
  port = 7335;  # Different port from production
  dataDir = "/persistent/grasp/test-archive";  # Fresh path that doesn't exist
  archiveAll = true;  # Enables archive mode for testing git operations
  logLevel = "debug";  # Verbose logging for verification
};

Verification Commands:

# 1. Check service status
sudo systemctl status ngit-grasp-test-archive

# 2. Verify directories exist with correct permissions
ls -la /persistent/grasp/test-archive/
# Expected: drwxr-x--- ngit-grasp-test-archive ngit-grasp

ls -la /persistent/grasp/test-archive/git/
ls -la /persistent/grasp/test-archive/relay/
# Expected: drwxr-x--- ngit-grasp-test-archive ngit-grasp

# 3. Check ExecStartPre execution in journal
journalctl -u ngit-grasp-test-archive --no-pager | head -50

# 4. Test permission correction
sudo chmod 777 /persistent/grasp/test-archive
sudo systemctl restart ngit-grasp-test-archive
stat /persistent/grasp/test-archive
# Expected: 0750 permissions restored

# 5. Test ownership correction
sudo chown root:root /persistent/grasp/test-archive
sudo systemctl restart ngit-grasp-test-archive
stat /persistent/grasp/test-archive
# Expected: ngit-grasp-test-archive:ngit-grasp ownership restored

Cleanup After Testing:

# Remove test instance from NixOS config, then:
sudo nixos-rebuild switch
sudo rm -rf /persistent/grasp/test-archive
sudo userdel ngit-grasp-test-archive  # If user persists

Edge Cases to Consider:

  • Parent directory doesn't exist (e.g., /persistent/grasp/ doesn't exist)
    • mkdir -p handles this correctly
  • Symlinked dataDir paths
    • Should work, but worth verifying
  • SELinux/AppArmor contexts (if enabled)
    • May need additional testing on hardened systems

2026-01-20 [Session 08:15]

  • Completed: Implemented ExecStartPre directives in nix/module.nix
  • Implementation details:
    • Added 7 ExecStartPre commands with + prefix (runs as root)
    • Creates dataDir, dataDir/git, dataDir/relay with mkdir -p
    • Sets ownership with chown -R to cfg.user:cfg.group
    • Sets permissions 750 on all three directories
  • Decision: Kept existing tmpfiles.rules for boot-time setup and consistency
  • Verified: No separate git/relay path options exist; they're always derived from dataDir

2026-01-20 [Session 07:41]

  • Started work: Created worktree for implementation

2026-01-19 [Session 00:00]

  • Started: Issue created to track NixOS module enhancement for automatic data directory creation

Notes

  • Keep existing tmpfiles.rules for consistency and boot-time setup
  • The + prefix in ExecStartPre runs as root for directory creation
  • Discovered during relay.ngit.dev migration planning (issue 820a)
  • Additional investigation needed: Check if the module has (or should have) separate config options for git data directory path and relay data directory path