# 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:

```nix
{
  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`):

```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](../../nix/example-configuration.nix) for more examples.

---

### Step 3: Import the Service

Import your service configuration in your main configuration file:

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

---

### Step 4: Update Flake Lock

```bash
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:

```bash
nix flake check
```

---

### Step 6: Deploy to Server

Deploy the new configuration to your server:

```bash
# 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:

```bash
# 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:

```bash
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:

```bash
# 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: 7334)

### Storage
- `dataDir` - Base directory for data (default: /var/lib/ngit-grasp-{name})
- `databaseBackend` - "lmdb" | "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](../../nix/module.nix) for complete option definitions.

---

## Systemd Service

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

```bash
# 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:

```nix
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:**
```bash
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:**
```bash
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:**
```bash
curl http://localhost:8082/metrics
```

**Tune configuration:**
- Reduce `metricsTopNRepos`
- Increase `syncMaxBackoffSecs`
- Tune `syncMaxBackoffSecs` for your network conditions

---

## Rollback

If deployment fails, rollback to previous configuration:

```bash
# 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:

```bash
# 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:**
   ```nix
   relayOwnerNsecFile = "/persistent/ngit-grasp/relay-owner.nsec";
   # NOT: relayOwnerNsec = "nsec1...";  # Ends up in nix store!
   ```

2. **Restrict data directory permissions:**
   ```bash
   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:**
   ```bash
   journalctl -u ngit-grasp-production --since today | grep -i error
   ```

---

## Monitoring

### Prometheus Metrics

ngit-grasp exposes Prometheus metrics at `/metrics`:

```bash
curl http://localhost:8082/metrics
```

See [Prometheus Setup](./prometheus-setup.md) for complete monitoring guide.

### Basic Health Checks

```bash
# 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/*
```

---

## Related Documentation

- [Configuration Reference](../reference/configuration.md) - All configuration options
- [NixOS Module](../../nix/module.nix) - Module source code
- [Example Configuration](../../nix/example-configuration.nix) - More examples
- [Prometheus Setup](./prometheus-setup.md) - Monitoring guide
- [Nix Flakes How-To](./nix-flakes.md) - Nix development environment

---

*Part of the [ngit-grasp how-to guides](./)*
