Skip to content

Deployment

Jason L. West edited this page Feb 6, 2026 · 5 revisions

Deployment

Docker Deployment

Core Agent

Build and run the core Nebulus Atom agent:

# Build the image
docker build -t nebulus-atom:latest .

# Run with Docker Compose
docker compose up -d agent

# View logs
docker compose logs -f agent

Flight Recorder Dashboard

docker compose up -d dashboard

Access at http://localhost:8501.

Full Core Stack

docker compose up -d

Starts both the agent and dashboard services.

Swarm Deployment

Build Images

# Overlord image
docker build -t nebulus-overlord:latest -f nebulus_swarm/overlord/Dockerfile .

# Minion image
docker build -t nebulus-minion:latest -f nebulus_swarm/minion/Dockerfile .

Start the Overlord

# Using Docker Compose
docker compose -f docker-compose.swarm.yml up -d overlord

# Or directly
docker run -d \
  --name overlord \
  -p 8080:8080 \
  -v overlord-state:/var/lib/overlord \
  -v /var/run/docker.sock:/var/run/docker.sock \
  --env-file .env.swarm \
  nebulus-overlord:latest

The Overlord needs access to the Docker socket to spawn minion containers.

Verify Health

curl http://localhost:8080/health
# {"status": "ok"}

curl http://localhost:8080/status
# {"paused": false, "active_minions": [], ...}

Swarm Dashboard

OVERLORD_URL=http://localhost:8080 \
STATE_DB_PATH=/path/to/state.db \
streamlit run nebulus_swarm/dashboard/app.py

Network Architecture

┌─────────────────────────────────────────┐
│            nebulus-swarm network         │
│                                         │
│  ┌───────────┐    ┌──────────────────┐  │
│  │  Overlord │    │   Minion(s)      │  │
│  │  :8080    │◄──►│   (ephemeral)    │  │
│  └─────┬─────┘    └────────┬─────────┘  │
│        │                    │            │
└────────┼────────────────────┼────────────┘
         │                    │
         ▼                    ▼
    Docker Socket        LLM Server
   (host mounted)     (external network)

The Overlord and Minions communicate over the nebulus-swarm Docker bridge network. The Overlord exposes port 8080 for external access (health checks, dashboard).

Production Considerations

Persistent State

Mount the state database to a persistent volume:

volumes:
  overlord-state:
    driver: local

Resource Limits

Minion containers are created with:

  • 2 GB memory limit
  • 1 CPU core
  • Auto-cleanup after exit

Monitoring

  • Health endpoint: GET /health returns 200 when Overlord is running
  • Status endpoint: GET /status returns active minions, config, pending questions
  • Docker health check: Configured in docker-compose with 30s intervals

Backup

Back up the SQLite state database regularly:

# Copy from Docker volume
docker cp overlord:/var/lib/overlord/state.db ./backup/state.db

# Or if using a mounted volume
cp /path/to/state.db ./backup/state.db

Log Management

Configure structured logging for production:

LOG_LEVEL=INFO
LOG_FORMAT=json
LOG_FILE=/var/log/overlord/overlord.log

Graceful Shutdown

The Overlord handles SIGTERM gracefully:

  1. Stops accepting new work
  2. Waits for active minions to finish (or timeout)
  3. Saves state to SQLite
  4. Exits cleanly
docker compose -f docker-compose.swarm.yml down

Overlord Daemon Deployment (Phase 3)

The Overlord daemon runs as a persistent background process with scheduled sweeps, Slack command routing, and proactive detection. It runs natively (not in Docker) and manages the cross-project ecosystem.

Prerequisites

  1. overlord.yml configured at ~/.atom/overlord.yml (see Configuration)
  2. Slack tokens set in environment (optional — daemon runs headless without them)
  3. croniter installed (pip install croniter)

Starting the Daemon

# Via CLI
nebulus-atom overlord daemon start

# With Slack integration
export SLACK_BOT_TOKEN=xoxb-your-bot-token
export SLACK_APP_TOKEN=xapp-your-app-token
export SLACK_CHANNEL_ID=C0123456789
nebulus-atom overlord daemon start

# Headless mode (scheduler only, no Slack)
nebulus-atom overlord daemon start

Running as a Background Service

systemd (Linux)

# /etc/systemd/system/overlord-daemon.service
[Unit]
Description=Nebulus Overlord Daemon
After=network.target

[Service]
Type=simple
User=jlwestsr
WorkingDirectory=/home/jlwestsr/projects/west_ai_labs/nebulus-atom
EnvironmentFile=/home/jlwestsr/.atom/overlord.env
ExecStart=/home/jlwestsr/projects/west_ai_labs/nebulus-atom/venv/bin/python -m nebulus_atom.main overlord daemon start
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable overlord-daemon
sudo systemctl start overlord-daemon
sudo journalctl -u overlord-daemon -f

PM2 (macOS/Linux)

pm2 start "nebulus-atom overlord daemon start" --name overlord-daemon
pm2 save
pm2 startup

Daemon Architecture

OverlordDaemon
├── SlackBot (Socket Mode)        # Listens for @atom mentions
│   ├── SlackCommandRouter        # Routes to Phase 2 modules
│   └── ProposalManager           # Thread-based approval workflow
├── Scheduler Loop (croniter)     # Fires tasks on cron schedule
│   ├── scan (hourly)             # Health check + detection
│   ├── test-all (nightly)        # Test sweep
│   └── clean-stale-branches      # Weekly branch cleanup
├── Cleanup Loop (5 min)          # Expires stale proposals
├── DetectionEngine               # Stale, ahead-of-main, failing
├── NotificationManager           # Urgent + buffered digest
└── Signal Handler                # SIGINT/SIGTERM → graceful stop

Stopping the Daemon

The daemon supports proper lifecycle management via the CLI:

# Check status
nebulus-atom overlord daemon status

# Stop gracefully (sends SIGTERM, waits up to 5s)
nebulus-atom overlord daemon stop

# Restart (stop + start)
nebulus-atom overlord daemon restart

# If running in foreground
Ctrl+C

# If running as a service
sudo systemctl stop overlord-daemon
# or
pm2 stop overlord-daemon

Graceful Shutdown

The daemon handles SIGINT and SIGTERM for clean shutdown:

  1. Sets the shutdown event
  2. Cancels scheduler, Slack bot, and cleanup tasks
  3. Waits for in-progress tasks to complete
  4. Stops the Slack bot connection
  5. Removes the PID file
  6. Logs shutdown complete

Persistent State

Data Location Description
PID file ~/.atom/overlord/daemon.pid Running daemon process ID
Proposals DB ~/.atom/overlord/proposals.db Pending/completed proposals
Memory DB ~/.atom/overlord/memory.db Cross-project observations
State DB /var/lib/overlord/state.db Minion state (Phase 0)

Back up proposal and memory databases regularly:

cp ~/.atom/overlord/proposals.db ~/backup/
cp ~/.atom/overlord/memory.db ~/backup/

Monitoring the Daemon

# Check logs (systemd)
sudo journalctl -u overlord-daemon -f

# Check logs (PM2)
pm2 logs overlord-daemon

# Slack: ask the daemon directly
@atom status

The daemon logs all scheduled task executions, detection results, proposal state changes, and notification sends at INFO level.

Headless vs. Slack Mode

Feature Headless With Slack
Scheduled scans Yes Yes
Proactive detection Yes (logged) Yes (posted to Slack)
Slack commands No Yes
Proposal workflow No Yes (thread-based)
Urgent notifications No Yes
Daily digest No Yes
Proposal cleanup Yes Yes

Local Development

For development without Docker:

# Core agent
source .venv/bin/activate
python3 -m nebulus_atom.main start

# Swarm dashboard
streamlit run nebulus_swarm/dashboard/app.py

# Run tests
python3 -m pytest tests/ -v

Related Pages

Clone this wiki locally