|
| 1 | +# Deploy Baudbot on DigitalOcean |
| 2 | + |
| 3 | +This guide walks through deploying baudbot on a DigitalOcean Droplet β a Linux virtual machine with dedicated resources and a static IP. Droplets are a good fit for baudbot because they give you a full Linux server with root access, persistent storage, and straightforward networking. |
| 4 | + |
| 5 | +## Prerequisites |
| 6 | + |
| 7 | +- A [DigitalOcean account](https://cloud.digitalocean.com/registrations/new) with a payment method on file |
| 8 | +- An SSH key pair on your local machine (`ssh-keygen -t ed25519` if you don't have one) |
| 9 | +- Your SSH public key uploaded to DigitalOcean: **Settings β Security β SSH Keys β Add SSH Key** |
| 10 | +- API keys ready for configuration (see [CONFIGURATION.md](../../CONFIGURATION.md)): |
| 11 | + - At least one LLM API key (Anthropic, OpenAI, Gemini, or OpenCode Zen) |
| 12 | + - Slack app tokens (bot token + app-level token) |
| 13 | + - GitHub account for the agent |
| 14 | + |
| 15 | +## System requirements |
| 16 | + |
| 17 | +| Resource | Minimum | Recommended | |
| 18 | +|----------|---------|-------------| |
| 19 | +| OS | Ubuntu 24.04 LTS | Ubuntu 24.04 LTS | |
| 20 | +| RAM | 4 GB | 8 GB | |
| 21 | +| CPU | 2 vCPU | 4 vCPU | |
| 22 | +| Disk | 20 GB SSD | 40 GB+ SSD | |
| 23 | + |
| 24 | +## Step 1: Create the Droplet |
| 25 | + |
| 26 | +### Option A: DigitalOcean Console |
| 27 | + |
| 28 | +1. Log in to [cloud.digitalocean.com](https://cloud.digitalocean.com) |
| 29 | +2. Click **Create β Droplets** |
| 30 | +3. **Region**: Choose the datacenter nearest to you (e.g. `nyc1`, `sfo3`, `ams3`) |
| 31 | +4. **Image**: Ubuntu 24.04 (LTS) x64 |
| 32 | +5. **Size**: Basic β Regular β **4 GB / 2 vCPUs / 80 GB SSD ($24/mo)** or **8 GB / 4 vCPUs / 160 GB SSD ($48/mo)** |
| 33 | +6. **Authentication**: Select your SSH key |
| 34 | +7. **Hostname**: `baudbot` (or whatever you prefer) |
| 35 | +8. Click **Create Droplet** |
| 36 | + |
| 37 | +### Option B: `doctl` CLI |
| 38 | + |
| 39 | +Install the DigitalOcean CLI: |
| 40 | + |
| 41 | +```bash |
| 42 | +# macOS |
| 43 | +brew install doctl |
| 44 | + |
| 45 | +# Linux (snap) |
| 46 | +sudo snap install doctl |
| 47 | + |
| 48 | +# Or download from https://docs.digitalocean.com/reference/doctl/how-to/install/ |
| 49 | +``` |
| 50 | + |
| 51 | +Authenticate: |
| 52 | + |
| 53 | +```bash |
| 54 | +doctl auth init |
| 55 | +# Paste your API token from https://cloud.digitalocean.com/account/api/tokens |
| 56 | +``` |
| 57 | + |
| 58 | +Create the Droplet: |
| 59 | + |
| 60 | +```bash |
| 61 | +# List your SSH key fingerprints |
| 62 | +doctl compute ssh-key list |
| 63 | + |
| 64 | +# Create a 4 GB / 2 vCPU Droplet (Basic, Regular) |
| 65 | +doctl compute droplet create baudbot \ |
| 66 | + --image ubuntu-24-04-x64 \ |
| 67 | + --size s-2vcpu-4gb \ |
| 68 | + --region nyc1 \ |
| 69 | + --ssh-keys <your-ssh-key-fingerprint> \ |
| 70 | + --wait |
| 71 | + |
| 72 | +# Or 8 GB / 4 vCPU for heavier workloads |
| 73 | +doctl compute droplet create baudbot \ |
| 74 | + --image ubuntu-24-04-x64 \ |
| 75 | + --size s-4vcpu-8gb \ |
| 76 | + --region nyc1 \ |
| 77 | + --ssh-keys <your-ssh-key-fingerprint> \ |
| 78 | + --wait |
| 79 | +``` |
| 80 | + |
| 81 | +Get the IP address: |
| 82 | + |
| 83 | +```bash |
| 84 | +doctl compute droplet list --format Name,PublicIPv4 |
| 85 | +``` |
| 86 | + |
| 87 | +## Step 2: Connect and install baudbot |
| 88 | + |
| 89 | +SSH into the Droplet: |
| 90 | + |
| 91 | +```bash |
| 92 | +ssh root@<droplet-ip> |
| 93 | +``` |
| 94 | + |
| 95 | +Clone and install: |
| 96 | + |
| 97 | +```bash |
| 98 | +git clone https://github.com/modem-dev/baudbot.git ~/baudbot |
| 99 | +sudo ~/baudbot/install.sh |
| 100 | +``` |
| 101 | + |
| 102 | +The installer handles everything: |
| 103 | +- Installs prerequisites (git, curl, tmux, iptables, Docker, gh) |
| 104 | +- Creates the `baudbot_agent` user |
| 105 | +- Installs Node.js and the pi agent |
| 106 | +- Generates an SSH key for GitHub |
| 107 | +- Sets up the firewall and process isolation |
| 108 | +- Walks you through secrets configuration |
| 109 | + |
| 110 | +After the installer finishes, complete the manual steps it prints: |
| 111 | + |
| 112 | +```bash |
| 113 | +# 1. Add the agent's SSH key to your GitHub account |
| 114 | +cat /home/baudbot_agent/.ssh/id_ed25519.pub |
| 115 | +# Copy this and add it at https://github.com/settings/keys |
| 116 | + |
| 117 | +# 2. Authenticate the GitHub CLI |
| 118 | +sudo -u baudbot_agent gh auth login |
| 119 | +# Follow the device code flow |
| 120 | +``` |
| 121 | + |
| 122 | +## Step 3: Configure secrets |
| 123 | + |
| 124 | +If you skipped secrets during install, or need to update them: |
| 125 | + |
| 126 | +```bash |
| 127 | +sudo baudbot config |
| 128 | +sudo baudbot deploy |
| 129 | +``` |
| 130 | + |
| 131 | +Or edit the secrets file directly: |
| 132 | + |
| 133 | +```bash |
| 134 | +sudo nano /home/baudbot_agent/.config/.env |
| 135 | +sudo baudbot deploy |
| 136 | +``` |
| 137 | + |
| 138 | +See [CONFIGURATION.md](../../CONFIGURATION.md) for the full list of environment variables. |
| 139 | + |
| 140 | +## Step 4: Networking and firewall |
| 141 | + |
| 142 | +### Baudbot's built-in firewall |
| 143 | + |
| 144 | +Baudbot's installer sets up `iptables` rules that restrict the `baudbot_agent` user's network access to an allowlist of ports: |
| 145 | + |
| 146 | +- **Outbound internet**: HTTP/S (80/443), SSH (22), DNS (53), cloud databases (3306, 5432, 6379, 27017) |
| 147 | +- **Localhost**: dev server ports (3000β5999), databases, the Slack bridge (7890) |
| 148 | +- **Everything else**: blocked and logged |
| 149 | + |
| 150 | +These rules persist across reboots via a systemd unit (`baudbot-firewall.service`). |
| 151 | + |
| 152 | +### DigitalOcean Cloud Firewall (optional, recommended) |
| 153 | + |
| 154 | +Add a DigitalOcean Cloud Firewall for defense-in-depth. Since baudbot communicates outbound only (via Slack Socket Mode and SSH for git), you need very few inbound rules: |
| 155 | + |
| 156 | +```bash |
| 157 | +doctl compute firewall create \ |
| 158 | + --name baudbot-fw \ |
| 159 | + --droplet-ids <droplet-id> \ |
| 160 | + --inbound-rules "protocol:tcp,ports:22,address:0.0.0.0/0,address:::/0" \ |
| 161 | + --outbound-rules "protocol:tcp,ports:all,address:0.0.0.0/0,address:::/0 protocol:udp,ports:all,address:0.0.0.0/0,address:::/0" |
| 162 | +``` |
| 163 | + |
| 164 | +**Inbound rules**: |
| 165 | +| Protocol | Port | Source | Purpose | |
| 166 | +|----------|------|--------|---------| |
| 167 | +| TCP | 22 | Your IP (or `0.0.0.0/0`) | SSH access for admin | |
| 168 | + |
| 169 | +> **Tip**: Restrict SSH to your IP address for better security. Update the firewall when your IP changes. |
| 170 | +
|
| 171 | +**Outbound rules**: Allow all (baudbot's iptables handles per-user egress filtering). |
| 172 | + |
| 173 | +No other inbound ports are needed β baudbot uses Slack's Socket Mode (outbound WebSocket on port 443), not incoming webhooks. |
| 174 | + |
| 175 | +### Console alternative |
| 176 | + |
| 177 | +1. Go to **Networking β Firewalls β Create Firewall** |
| 178 | +2. Add an inbound rule for SSH (TCP 22) |
| 179 | +3. Keep all outbound rules (or allow all) |
| 180 | +4. Under **Apply to Droplets**, select your baudbot Droplet |
| 181 | + |
| 182 | +## Step 5: Start baudbot |
| 183 | + |
| 184 | +```bash |
| 185 | +# Start the agent (uses systemd) |
| 186 | +sudo baudbot start |
| 187 | + |
| 188 | +# Check status |
| 189 | +sudo baudbot status |
| 190 | + |
| 191 | +# View logs |
| 192 | +sudo baudbot logs |
| 193 | +``` |
| 194 | + |
| 195 | +## Step 6: Verify it's working |
| 196 | + |
| 197 | +```bash |
| 198 | +# Run the health check |
| 199 | +sudo baudbot doctor |
| 200 | + |
| 201 | +# Check the systemd service |
| 202 | +systemctl status baudbot |
| 203 | + |
| 204 | +# Check the firewall is active |
| 205 | +sudo iptables -L BAUDBOT_OUTPUT -n -v --line-numbers |
| 206 | +``` |
| 207 | + |
| 208 | +If you configured Slack, send a message mentioning @baudbot in an allowed channel β it should respond. |
| 209 | + |
| 210 | +## Monitoring |
| 211 | + |
| 212 | +### Logs |
| 213 | + |
| 214 | +```bash |
| 215 | +# Tail live logs |
| 216 | +sudo baudbot logs |
| 217 | + |
| 218 | +# View systemd journal |
| 219 | +journalctl -u baudbot -f |
| 220 | + |
| 221 | +# Check firewall blocked connections |
| 222 | +journalctl -k | grep BAUDBOT_BLOCKED |
| 223 | +``` |
| 224 | + |
| 225 | +### DigitalOcean Monitoring |
| 226 | + |
| 227 | +Enable DigitalOcean's built-in monitoring for CPU, memory, and disk alerts: |
| 228 | + |
| 229 | +```bash |
| 230 | +doctl compute droplet create baudbot \ |
| 231 | + ... \ |
| 232 | + --enable-monitoring |
| 233 | +``` |
| 234 | + |
| 235 | +Or enable it on an existing Droplet from the **Monitoring** tab in the console. Set up alert policies under **Monitoring β Create Alert Policy** for: |
| 236 | +- CPU usage > 80% sustained |
| 237 | +- Memory usage > 90% |
| 238 | +- Disk usage > 80% |
| 239 | + |
| 240 | +### Automatic backups |
| 241 | + |
| 242 | +Enable weekly backups when creating the Droplet (`--enable-backups` with doctl), or enable them later from the **Backups** tab. Backups cost 20% of the Droplet price (e.g. $4.80/mo for a $24/mo Droplet). |
| 243 | + |
| 244 | +## Cost estimate |
| 245 | + |
| 246 | +| Component | Spec | Monthly cost | |
| 247 | +|-----------|------|-------------| |
| 248 | +| **Droplet (minimum)** | 4 GB / 2 vCPU Basic | $24/mo | |
| 249 | +| **Droplet (recommended)** | 8 GB / 4 vCPU Basic | $48/mo | |
| 250 | +| Backups (optional) | Weekly | +20% of Droplet | |
| 251 | +| Cloud Firewall | β | Free | |
| 252 | +| Snapshots (optional) | Per GB | $0.06/GB/mo | |
| 253 | +| **Total (minimum)** | | **~$24/mo** | |
| 254 | +| **Total (recommended)** | | **~$48β58/mo** | |
| 255 | + |
| 256 | +Pricing as of January 2026. Effective January 1, 2026, DigitalOcean uses per-second billing with a minimum charge of 60 seconds or $0.01. Check [digitalocean.com/pricing/droplets](https://www.digitalocean.com/pricing/droplets) for current rates. |
| 257 | + |
| 258 | +## Platform-specific tips |
| 259 | + |
| 260 | +- **Snapshots before updates**: Take a snapshot before running `baudbot update` β it's cheap insurance. From the console: **Droplet β Snapshots β Take Snapshot**, or via CLI: `doctl compute droplet-action snapshot <droplet-id> --snapshot-name baudbot-pre-update`. |
| 261 | +- **Resize without downtime**: If the agent needs more RAM, power off and resize the Droplet from the console. CPU/RAM-only resizes (no disk increase) are reversible. |
| 262 | +- **Droplet Console**: If you lose SSH access, use the **Access β Droplet Console** in the DigitalOcean web UI for emergency access. |
| 263 | +- **User data for automation**: For repeatable installs, use `--user-data-file` to pass a cloud-init script that clones and runs the installer automatically: |
| 264 | + ```bash |
| 265 | + doctl compute droplet create baudbot \ |
| 266 | + --image ubuntu-24-04-x64 \ |
| 267 | + --size s-2vcpu-4gb \ |
| 268 | + --region nyc1 \ |
| 269 | + --ssh-keys <fingerprint> \ |
| 270 | + --user-data-file baudbot-init.sh \ |
| 271 | + --wait |
| 272 | + ``` |
| 273 | +- **VPC networking**: Droplets are automatically placed in a default VPC. If you run other services (databases, etc.) on DigitalOcean, they can communicate over the private network without traversing the public internet. |
| 274 | +- **IPv6**: Enable IPv6 on the Droplet if your team needs it (`--enable-ipv6` with doctl). |
| 275 | + |
| 276 | +## Updating baudbot |
| 277 | + |
| 278 | +```bash |
| 279 | +ssh root@<droplet-ip> |
| 280 | + |
| 281 | +# Pull latest and redeploy |
| 282 | +sudo baudbot update |
| 283 | + |
| 284 | +# Or rollback if something breaks |
| 285 | +sudo baudbot rollback previous |
| 286 | +``` |
| 287 | + |
| 288 | +## Destroying the Droplet |
| 289 | + |
| 290 | +```bash |
| 291 | +# Via CLI |
| 292 | +doctl compute droplet delete baudbot --force |
| 293 | + |
| 294 | +# Don't forget to clean up the firewall too |
| 295 | +doctl compute firewall delete <firewall-id> |
| 296 | +``` |
| 297 | + |
| 298 | +Or from the console: **Droplet β Destroy β Destroy this Droplet**. |
| 299 | + |
| 300 | +> **Warning**: Destroying a Droplet deletes all data. Take a snapshot first if you want to preserve anything. |
0 commit comments