Files
ngit-grasp/docs/how-to/deploy.md
T
DanConwayDev 51a3d810d2 docs: add production deployment how-to guide
- Complete guide for deploying ngit-grasp to NixOS servers
- Step-by-step deployment instructions
- Configuration options reference
- Troubleshooting section
- Security hardening recommendations
- Multiple instance examples
- References nix/example-configuration.nix which has clear examples
2026-01-11 15:09:46 +00:00

11 KiB

How-To: Deploy ngit-grasp to Production

Purpose: Deploy ngit-grasp to a production NixOS server
Difficulty: Intermediate
Time: 30-60 minutes


Problem

You want to:

  • Deploy ngit-grasp to a NixOS server
  • Configure it as a systemd service
  • Set up reverse proxy (Caddy)
  • Ensure proper security and monitoring

Prerequisites

  • NixOS server with SSH access
  • Flakes enabled on server and local machine
  • Domain name configured (DNS pointing to server)
  • Basic knowledge of NixOS configuration

Solution

Step 1: Add ngit-grasp to Your Server's Flake

In your server's flake.nix, add ngit-grasp as an input:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
    ngit-grasp.url = "github:DanConwayDev/ngit-grasp";
    # or use a specific git repository:
    # ngit-grasp.url = "git+https://git.shakespeare.diy/npub.../ngit-grasp.git";
  };

  outputs = { self, nixpkgs, ngit-grasp, ... }@inputs: {
    nixosConfigurations.your-hostname = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      specialArgs = { inherit inputs; };
      modules = [
        ./configuration.nix
        # ... other modules
      ];
    };
  };
}

Step 2: Create Service Configuration

Create a new file for your ngit-grasp service (e.g., services/ngit-grasp.nix):

{ inputs, ... }:

{
  imports = [ inputs.ngit-grasp.nixosModules.default ];

  services.ngit-grasp.production = {
    enable = true;
    domain = "ngit.example.com";
    
    # Network
    bindAddress = "127.0.0.1";
    port = 8082;
    
    # Storage
    dataDir = "/persistent/ngit-grasp";
    
    # Identity
    relayName = "My GRASP Relay";
    relayDescription = "A Rust GRASP implementation with proactive sync";
    relayOwnerNsecFile = "/persistent/ngit-grasp/relay-owner.nsec";
    
    # Sync - bootstrap from relay.ngit.dev
    syncBootstrapRelayUrl = "wss://relay.ngit.dev";
    
    # Metrics
    metricsEnabled = true;
    
    # Logging
    logLevel = "info";
  };

  # Caddy reverse proxy
  services.caddy.virtualHosts."ngit.example.com" = {
    extraConfig = ''
      reverse_proxy 127.0.0.1:8082 {
        header_down X-Real-IP {http.request.remote}
        header_down X-Forwarded-For {http.request.remote}
      }
    '';
  };
}

Key configuration options:

  • Instance name (production): Can be any name. Used for systemd service (ngit-grasp-production)
  • domain: Your relay's domain (used in GRASP validation)
  • port: Local port (use reverse proxy for HTTPS)
  • dataDir: Where git repos and database are stored
  • relayOwnerNsecFile: Path to file containing relay owner's nsec
    • If file doesn't exist, ngit-grasp will auto-generate one
    • Alternative: relayOwnerNsec = "nsec1..." (less secure, in nix store)
  • syncBootstrapRelayUrl: Bootstrap relay to sync from on startup

See nix/example-configuration.nix for more examples.


Step 3: Import the Service

Import your service configuration in your main configuration file:

# In configuration.nix or services/default.nix
{
  imports = [
    ./services/ngit-grasp.nix
    # ... other services
  ];
}

Step 4: Update Flake Lock

cd /path/to/server/config
nix flake update ngit-grasp
git add flake.lock
git commit -m "Add ngit-grasp and update flake.lock"

Step 5: Validate Configuration

Before deploying, validate the configuration builds:

nix flake check

Step 6: Deploy to Server

Deploy the new configuration to your server:

# Build and switch in one command (builds on server)
nixos-rebuild switch --flake .#your-hostname \
  --target-host user@server.example.com \
  --use-remote-sudo \
  --build-host user@server.example.com

Alternative: Build locally, then deploy:

# Build locally
nixos-rebuild build --flake .#your-hostname

# Deploy to server
nixos-rebuild switch --flake .#your-hostname \
  --target-host user@server.example.com \
  --use-remote-sudo

Note: Building locally requires your machine to trust the server's nix signing key.


Step 7: Verify Deployment

SSH to the server and check the service:

ssh user@server.example.com

# Check service status
systemctl status ngit-grasp-production

# View logs
journalctl -u ngit-grasp-production -f

# Check if listening on port
ss -tlnp | grep 8082

Step 8: Test Functionality

From your local machine, test the relay:

# Test NIP-11 relay info
curl https://ngit.example.com -H "Accept: application/nostr+json" | jq

# Test WebSocket connection
websocat wss://ngit.example.com
# Then type: ["REQ","test",{}]
# Should receive events

# Test git clone (if you have repos)
git ls-remote https://ngit.example.com/<npub>/<repo>.git

Configuration Options

Required

  • enable - Enable this instance
  • domain - Domain where relay is hosted

Network

  • bindAddress - IP to bind to (default: "127.0.0.1")
  • port - Port to listen on (default: 8080)

Storage

  • dataDir - Base directory for data (default: /var/lib/ngit-grasp-{name})
  • databaseBackend - "lmdb" | "nostr-db" | "memory" (default: "lmdb")

Identity

  • relayName - Relay name for NIP-11 (default: "{domain} grasp relay")
  • relayDescription - Relay description
  • relayOwnerNsecFile - Path to file with relay owner nsec (recommended)
  • relayOwnerNsec - Inline nsec (less secure)

Sync

  • syncBootstrapRelayUrl - Bootstrap relay URL (optional)
  • syncDisableNegentropy - Disable NIP-77 negentropy (default: false)
  • syncMaxBackoffSecs - Max backoff for reconnection (default: 3600)
  • syncDisconnectCheckIntervalSecs - Check interval (default: 60)
  • syncBaseBackoffSecs - Base backoff time (default: 5)

Metrics

  • metricsEnabled - Enable /metrics endpoint (default: true)
  • metricsConnectionPerIpAbuseThreshold - Abuse threshold (default: 10)
  • metricsTopNRepos - Number of top repos to track (default: 10)

Logging

  • logLevel - "trace" | "debug" | "info" | "warn" | "error" (default: "info")

Security

  • user - User to run as (default: "ngit-grasp-{name}")
  • group - Group to run as (default: "ngit-grasp")

See nix/module.nix for complete option definitions.


Systemd Service

The NixOS module creates a systemd service: ngit-grasp-{instance-name}

# Start/stop/restart
systemctl start ngit-grasp-production
systemctl stop ngit-grasp-production
systemctl restart ngit-grasp-production

# Enable/disable autostart
systemctl enable ngit-grasp-production
systemctl disable ngit-grasp-production

# View logs
journalctl -u ngit-grasp-production -f
journalctl -u ngit-grasp-production --since "1 hour ago"

# Check status
systemctl status ngit-grasp-production

Multiple Instances

You can run multiple instances on the same server:

services.ngit-grasp = {
  production = {
    enable = true;
    domain = "ngit.example.com";
    port = 8082;
    dataDir = "/persistent/ngit-production";
  };
  
  staging = {
    enable = true;
    domain = "ngit-staging.example.com";
    port = 8083;
    dataDir = "/persistent/ngit-staging";
    logLevel = "debug";
  };
};

Each instance:

  • Runs as separate systemd service: ngit-grasp-production, ngit-grasp-staging
  • Has its own user: ngit-grasp-production, ngit-grasp-staging
  • Stores data in separate directory
  • Can have different configuration

Troubleshooting

Service won't start

Check logs:

journalctl -u ngit-grasp-production -n 50

Common issues:

  • Port already in use: Check with ss -tlnp | grep 8082
  • Data directory permissions: Should be owned by service user
  • Invalid nsec file: Check file exists and contains valid nsec

Can't connect via WebSocket

Check:

  • Service is running: systemctl status ngit-grasp-production
  • Firewall allows connections: nix-shell -p nmap --run "nmap -p 443 ngit.example.com"
  • Caddy is configured correctly: systemctl status caddy
  • DNS resolves: dig ngit.example.com

Sync not working

Check logs for sync errors:

journalctl -u ngit-grasp-production | grep -i sync

Common issues:

  • Bootstrap relay URL incorrect or unreachable
  • Network connectivity issues
  • Bootstrap relay doesn't support negentropy (disable with syncDisableNegentropy = true)

High memory/CPU usage

Monitor metrics:

curl http://localhost:8082/metrics

Tune configuration:

  • Reduce metricsTopNRepos
  • Increase syncMaxBackoffSecs
  • Switch to databaseBackend = "nostr-db" for better performance

Rollback

If deployment fails, rollback to previous configuration:

# On the server
nixos-rebuild switch --rollback

# Or remotely
nixos-rebuild switch --rollback \
  --target-host user@server.example.com \
  --use-remote-sudo

Upgrading

To upgrade ngit-grasp:

# Update flake input
nix flake update ngit-grasp

# Review changes
git diff flake.lock

# Commit
git add flake.lock
git commit -m "Update ngit-grasp"

# Deploy
nixos-rebuild switch --flake .#your-hostname \
  --target-host user@server.example.com \
  --use-remote-sudo \
  --build-host user@server.example.com

Security Hardening

The NixOS module includes systemd hardening:

  • NoNewPrivileges = true - Prevents privilege escalation
  • ProtectSystem = "strict" - Read-only filesystem except dataDir
  • ProtectHome = true - No access to home directories
  • PrivateTmp = true - Private /tmp
  • RestrictAddressFamilies - Only allow needed network families
  • SystemCallFilter - Restrict system calls

Additional recommendations:

  1. Use nsec file instead of inline:

    relayOwnerNsecFile = "/persistent/ngit-grasp/relay-owner.nsec";
    # NOT: relayOwnerNsec = "nsec1...";  # Ends up in nix store!
    
  2. Restrict data directory permissions:

    chmod 750 /persistent/ngit-grasp
    chown ngit-grasp-production:ngit-grasp /persistent/ngit-grasp
    
  3. Use HTTPS (reverse proxy required):

    • ngit-grasp binds to localhost by default
    • Use Caddy/nginx for TLS termination
    • Caddy handles certificates automatically
  4. Monitor logs regularly:

    journalctl -u ngit-grasp-production --since today | grep -i error
    

Monitoring

Prometheus Metrics

ngit-grasp exposes Prometheus metrics at /metrics:

curl http://localhost:8082/metrics

See Prometheus Setup for complete monitoring guide.

Basic Health Checks

# Check if service is running
systemctl is-active ngit-grasp-production

# Check if port is listening
nc -zv localhost 8082

# Check relay info
curl https://ngit.example.com -H "Accept: application/nostr+json"

# Check disk usage
du -sh /persistent/ngit-grasp/*


Part of the ngit-grasp how-to guides