# Next Steps: Docker Deployment to VPS
## Current Status ✅
- [x] Obsidian vault created and Git-backed
- [x] GitHub repository created (https://github.com/townofnolan/obsidian-vault)
- [x] MCP server built and tested locally
- [x] Environment variables configured
- [x] Bug fix: Added `dotenv` package for .env file loading
- [x] Server running successfully on localhost:3000
## What's Next: Multi-Project Docker Deployment
### Project Overview
This VPS will host **two Docker containers**:
1. **Obsidian MCP Server** (this project) - AI-powered note management
2. **MusicBot** (../MusicBot) - Discord music bot with voice support
---
## VPS Recommendation: Hetzner CPX31
### Recommended Server: **CPX31** (8GB RAM, 4 vCPU, €15.99/month)
**Why CPX31 over CPX21 (4GB)?**
Based on detailed resource analysis:
| Component | Idle RAM | Peak RAM | Notes |
|-----------|----------|----------|-------|
| **System (Ubuntu + Docker)** | 550-1000 MB | 550-1000 MB | Base OS overhead |
| **Obsidian MCP Server** | 120-220 MB | 170-310 MB | Very lightweight |
| **MusicBot (idle)** | 310-570 MB | 310-570 MB | Discord.js + player |
| **MusicBot (1 stream)** | - | 790-1550 MB | Voice + FFmpeg + YouTube |
| **MusicBot (2-3 streams)** | - | 1650-2750 MB | Multiple concurrent users |
| **System buffers** | 600-1200 MB | 600-1200 MB | Disk cache, networking |
| **TOTAL** | **1630-3080 MB** | **3490-5060 MB** | |
**CPX21 (4GB) Analysis:**
- Peak usage: **87-126% of capacity** ⚠️
- **EXCEEDS available RAM** during concurrent music streaming
- Risk of OOM (Out of Memory) kills
- Audio stuttering/buffering under load
- ❌ **Not recommended for production**
**CPX31 (8GB) Analysis:**
- Peak usage: **43-63% of capacity** ✅
- Comfortable **37-57% free memory** at peak
- Can handle 3-5 concurrent Discord voice channels
- Room for growth and traffic spikes
- ✅ **Recommended for production stability**
**Cost:** Only €7/month more than CPX21, worth it for reliability.
---
## Docker Deployment Plan
### Architecture
```
Hetzner CPX31 VPS (8GB RAM, 4 vCPU)
├─ Docker Engine
├─ docker-compose.yml (orchestrates both containers)
│
├─ 🐳 Container 1: obsidian-mcp
│ ├─ Image: Node 20 Alpine
│ ├─ Resource Limits: 512MB RAM, 0.5 CPU
│ ├─ Port: 3000
│ ├─ Volume: ./vault (persistent Git data)
│ └─ Restart: unless-stopped
│
└─ 🐳 Container 2: discord-musicbot
├─ Image: Node 20 + FFmpeg
├─ Resource Limits: 6GB RAM, 3.0 CPU
├─ Volume: ./cache (persistent MP3 cache)
└─ Restart: unless-stopped
```
### Files to Create
1. **`Dockerfile`** (in this repo)
- Multi-stage build (TypeScript → JavaScript)
- Node 20 Alpine base
- Production dependencies only
- Health check endpoint
2. **`../MusicBot/Dockerfile`**
- Node 20 with FFmpeg
- Discord.js voice dependencies
- System libraries for audio processing
3. **`../docker-compose.yml`** (parent directory)
- Orchestrates both containers
- Sets resource limits
- Manages volumes and networking
- Configures restart policies
4. **`deploy-to-vps.sh`**
- Automated VPS setup script
- Installs Docker & Docker Compose
- Clones repositories
- Configures environment variables
- Starts containers
---
## Deployment Steps (When Ready)
### 1. Prepare VPS
```bash
# SSH into your Hetzner CPX31 VPS
ssh root@YOUR_VPS_IP
# Update system
apt update && apt upgrade -y
# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
# Install Docker Compose
apt install docker-compose -y
```
### 2. Clone Repositories
```bash
cd /opt
git clone https://github.com/townofnolan/obsidian-mcp-server.git
git clone https://github.com/townofnolan/MusicBot.git
```
### 3. Configure Environment Variables
```bash
# Create .env for MCP server
cd /opt/obsidian-mcp-server
nano .env
```
Add:
```env
GIT_REPO_URL=https://github.com/townofnolan/obsidian-vault.git
GIT_BRANCH=main
GIT_TOKEN=your_github_token_here
PORT=3000
NODE_ENV=production
API_KEY=your_secure_api_key_here
BASE_URL=http://YOUR_VPS_IP:3000
```
```bash
# Create .env for MusicBot
cd /opt/MusicBot
nano .env
```
Add your Discord bot token and config.
### 4. Create docker-compose.yml
```bash
cd /opt
nano docker-compose.yml
```
```yaml
version: '3.8'
services:
obsidian-mcp:
build: ./obsidian-mcp-server
container_name: obsidian-mcp
restart: unless-stopped
ports:
- "3000:3000"
env_file:
- ./obsidian-mcp-server/.env
volumes:
- ./obsidian-mcp-server/vault:/app/vault
mem_limit: 512m
cpus: 0.5
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
discord-bot:
build: ./MusicBot
container_name: discord-bot
restart: unless-stopped
env_file:
- ./MusicBot/.env
volumes:
- ./MusicBot/cache:/app/cache
mem_limit: 6g
cpus: 3.0
```
### 5. Deploy
```bash
cd /opt
docker-compose up -d
# Check status
docker-compose ps
docker-compose logs -f
# Monitor resources
docker stats
```
### 6. Test
```bash
# Test MCP server
curl http://YOUR_VPS_IP:3000/health
# Should return: {"status":"ok","server":"obsidian-mcp-server"}
```
### 7. Configure Firewall
```bash
ufw allow 22/tcp # SSH
ufw allow 3000/tcp # MCP Server
ufw enable
```
---
## Adding HTTPS with Domain (Future)
When you purchase a domain:
1. **Point domain to VPS** (A record in DNS)
2. **Install Caddy** (automatic SSL)
```bash
apt install caddy
nano /etc/caddy/Caddyfile
```
```
yourdomain.com {
reverse_proxy localhost:3000
}
```
3. **Restart Caddy**
```bash
systemctl restart caddy
```
Caddy automatically handles Let's Encrypt SSL certificates!
---
## Monitoring & Maintenance
### View Logs
```bash
docker-compose logs -f obsidian-mcp
docker-compose logs -f discord-bot
```
### Restart Services
```bash
docker-compose restart obsidian-mcp
docker-compose restart discord-bot
```
### Update Code
```bash
cd /opt/obsidian-mcp-server
git pull
docker-compose build obsidian-mcp
docker-compose up -d obsidian-mcp
```
### Monitor Resources
```bash
docker stats
htop
```
---
## Resource Analysis Summary
### MusicBot Resource Details
**Why MusicBot needs significant resources:**
1. **FFmpeg Audio Processing** - Transcodes audio in real-time (150-300MB per stream)
2. **MP3Cow Caching** - Downloads full YouTube videos, converts to MP3 (200-500MB spikes)
3. **Discord Voice Connections** - Maintains WebSocket + Opus encoding (100-150MB each)
4. **YouTube Streaming** - Buffers video data before audio extraction (100-200MB)
5. **Queue Management** - Stores track metadata and manages playback state
**Concurrent Usage:**
- 1 user streaming: ~790-1550 MB RAM
- 2-3 users streaming: ~1650-2750 MB RAM
- Each additional stream adds ~400-600 MB
**CPU Usage:**
- FFmpeg Opus encoding: 10-15% per stream (single-threaded)
- YouTube download/processing: 20-40% (spikes)
- Peak with 2-3 streams: 40-80% of available CPUs
### Obsidian MCP Server Resource Details
**Why MCP server is lightweight:**
1. **Text-only operations** - Serving markdown files (tiny compared to audio)
2. **Occasional Git operations** - Only when notes are modified
3. **Few concurrent users** - Personal/small team use
4. **No media processing** - Just HTTP + file I/O
**Typical usage:** 200-350 MB RAM total
---
## Cost Summary
| Item | Cost |
|------|------|
| Hetzner CPX31 VPS | €15.99/month |
| Domain (future) | €1-12/year |
| SSL Certificate | €0 (Let's Encrypt via Caddy) |
| **Total** | **~€16-17/month** |
---
## Security Checklist
- [ ] Strong API key for MCP server (`openssl rand -base64 32`)
- [ ] Firewall configured (UFW)
- [ ] SSH key authentication only (disable password auth)
- [ ] Regular system updates (`apt update && apt upgrade`)
- [ ] Monitor logs for suspicious activity
- [ ] Secrets in `.env` files (never commit to Git)
- [ ] HTTPS when domain added
---
## References
- [MCP Server Docs](docs/deployment.md)
- [Hetzner Cloud](https://www.hetzner.com/cloud)
- [Docker Compose Docs](https://docs.docker.com/compose/)
- [Caddy Server](https://caddyserver.com/)
---
**Last Updated:** 2025-11-15
**Status:** Ready for Docker implementation
**Next Action:** Create Dockerfiles and docker-compose.yml when ready to deploy