---
title: "Production Deployment"
description: "Run SlackHive as a long-lived service - systemd/launchd, reverse proxy, SQLite backup, key rotation."
---

For production use, you want SlackHive to survive reboots, serve HTTPS, and get backed up. This page covers the operational recipe on top of the standard CLI install.

If you haven't installed yet, start with [Install with the CLI](/getting-started/install-cli).

## Security checklist

Before exposing SlackHive to a production network:

- [ ] Strong `ADMIN_PASSWORD` in `.env` (regenerate if it was auto-filled during testing)
- [ ] `AUTH_SECRET` and `ENV_SECRET_KEY` generated with `openssl rand -hex 32` (`slackhive init` does this automatically)
- [ ] `NODE_ENV=production` in `.env`
- [ ] Reverse proxy terminating TLS - never expose port 3001 directly
- [ ] `.env` not in version control
- [ ] MCP secrets stored in **Settings → Env Vars** (encrypted), not in plain MCP config `env` fields
- [ ] `~/.slackhive/` directory permissions limited to the runtime user

## Run as a service

### macOS - launchd

Create `~/Library/LaunchAgents/co.pelago.slackhive.plist`:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>co.pelago.slackhive</string>
    <key>ProgramArguments</key>
    <array>
      <string>/usr/local/bin/slackhive</string>
      <string>start</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/tmp/slackhive.out.log</string>
    <key>StandardErrorPath</key>
    <string>/tmp/slackhive.err.log</string>
  </dict>
</plist>
```

Load:

```bash
launchctl load ~/Library/LaunchAgents/co.pelago.slackhive.plist
```

### Linux - systemd

For OAuth subscription mode on Linux, make sure the service user has access to either:
- A running keyring daemon (`secret-tool` works for that user), **or**
- A pre-populated `~/.claude/.credentials.json` in the service user's home

Headless servers almost always want option 2 - run `claude login` once as that user before enabling the service.

Create `/etc/systemd/system/slackhive.service`:

```ini
[Unit]
Description=SlackHive
After=network.target

[Service]
Type=simple
User=slackhive
ExecStart=/usr/local/bin/slackhive start
ExecStop=/usr/local/bin/slackhive stop
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
```

Enable:

```bash
systemctl daemon-reload
systemctl enable --now slackhive
```

The service runs `slackhive start`, which acquires the singleton lock, negotiates ports, and starts the web + runner. On stop, `slackhive stop` sweeps orphans and unlinks the lock.

## Reverse proxy with nginx

```nginx
server {
    listen 443 ssl http2;
    server_name slackhive.example.com;

    ssl_certificate     /etc/ssl/certs/slackhive.crt;
    ssl_certificate_key /etc/ssl/private/slackhive.key;

    location / {
        proxy_pass http://127.0.0.1:3001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Required for live logs (SSE)
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 3600s;
    }
}
```

<Note>
  `proxy_buffering off` is required for the live logs stream - otherwise the UI shows log lines in lumps instead of real time.
</Note>

## Reverse proxy with Caddy

```caddyfile
slackhive.example.com {
    reverse_proxy 127.0.0.1:3001 {
        flush_interval -1
    }
}
```

`flush_interval -1` disables buffering for SSE.

## Change the web port

`slackhive init` picks `3001` by default and auto-negotiates if taken. To pin a different port, set `PORT` in `.env`:

```bash
PORT=8080
```

Then restart:

```bash
slackhive stop
slackhive start
```

## Backup

Everything that matters is on disk under `~/.slackhive/`. Back up the whole directory:

```bash
tar czf slackhive-backup-$(date +%Y%m%d).tar.gz ~/.slackhive
```

For a live install, stop first to guarantee a consistent SQLite snapshot:

```bash
slackhive stop
tar czf slackhive-backup-$(date +%Y%m%d).tar.gz ~/.slackhive
slackhive start
```

Or use SQLite's online backup API:

```bash
sqlite3 ~/.slackhive/data.db ".backup '/backups/slackhive-$(date +%Y%m%d).db'"
```

The online backup is atomic and safe while SlackHive is running.

### What's in the backup

| Path | Contents |
|---|---|
| `~/.slackhive/data.db` | All agents, memories, skills, history, MCP catalog, users, settings |
| `~/.slackhive/agents/<slug>/` | Per-agent workspace - CLAUDE.md, sessions, copies of assigned wiki folders |
| `~/.slackhive/knowledge/<folderId>/wiki/` | Compiled wikis for the Knowledge Library (source of truth) |
| `~/.slackhive/.env` | Encryption keys, admin password, Claude auth |

<Warning>
  The `.env` is required to read encrypted secrets in `data.db`. Back up both or store `.env` separately in a secret manager.
</Warning>

### Automated daily backup

```bash
# /etc/cron.daily/slackhive-backup
sqlite3 /home/slackhive/.slackhive/data.db \
  ".backup '/backups/slackhive-$(date +%Y%m%d).db'"
find /backups -name 'slackhive-*.db' -mtime +30 -delete
```

## Update

```bash
slackhive update
```

`update` runs `git pull`, reinstalls dependencies, rebuilds, and restarts. Your `data.db` is preserved.

Review [releases](https://github.com/pelago-labs/slackhive/releases) before updating in production. Database migrations run automatically on the next start.

## Resource requirements

Minimum recommended for a small team (5–10 agents, moderate traffic):

| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 2 cores | 4 cores |
| RAM | 2 GB | 4 GB |
| Disk | 10 GB | 20 GB |

Each active agent maintains a Slack Bolt Socket Mode connection. Memory scales with the number of active agents and the size of their assigned MCP processes.

## Key rotation

### `AUTH_SECRET`

Rotating invalidates every active session - everyone is logged out.

1. Generate: `openssl rand -hex 32`
2. Update `.env`
3. Restart: `slackhive stop && slackhive start`

### `ENV_SECRET_KEY`

Rotating makes every stored secret unreadable. You must re-enter each secret after rotation.

1. Export every value from **Settings → Env Vars** by hand (the API never returns plaintext)
2. Generate: `openssl rand -hex 32`
3. Update `.env`
4. Restart: `slackhive stop && slackhive start`
5. Paste each secret back into **Settings → Env Vars**

Plan the rotation during off-hours - MCP servers referencing `${env:NAME}` will fail between steps 3 and 5.

## Monitoring

SlackHive doesn't ship built-in metrics or alerting. For production monitoring:

- **Live Logs** tab in the UI - real-time diagnostics
- **Runner log** - `~/.slackhive/logs/runner.log` streams JSON-per-line; pipe to Loki, Datadog, CloudWatch
- **Health check** - `curl http://127.0.0.1:3001/api/health` returns 200 when the stack is live
- **Status command** - `slackhive status` reports process state and ports
