warmbox/docs/deploy-ubuntu-docker.md

6 KiB
Raw Blame History

Deploy Warmbox on Ubuntu (Docker)

Warmbox is a long-running worker (cron dispatcher, rescuer, SMTP/IMAP). It needs:

  • A persistent database (SQLite volume)
  • Outbound SMTP/IMAP from the server
  • A public HTTPS URL for the dashboard and OAuth callbacks

Recommended for your case: Ubuntu home server + Docker + Cloudflare Tunnel (free HTTPS, no router port forwarding).

Not suitable: Netlify, GitHub Pages, Vercel static — no background jobs, no SQLite, no mail sockets.


Quick comparison

Option Best for Pros Cons
Ubuntu + Docker + Cloudflare Tunnel Home server (your preference) Free, persistent, full control You maintain the box
Ubuntu + Docker + Caddy Home server with public IP + domain Simple TLS Open ports 80/443 on router
Hetzner / DigitalOcean VPS + same Docker No home server Always on, static IP ~$56/mo
Railway / Fly.io Managed container Easy deploy Cost, less control for 30-day test

1. Prepare the Ubuntu server

# SSH into your home server
sudo apt update && sudo apt upgrade -y
sudo apt install -y git docker.io docker-compose-v2
sudo usermod -aG docker $USER
# log out and back in so docker group applies

Clone the repo:

git clone https://github.com/YOUR_ORG/warmbox.git
cd warmbox

2. Configure environment

cp .env.example .env
nano .env

Set these before first start:

# Generate once — keep forever (changing it breaks encrypted mailbox passwords)
openssl rand -hex 32    # → ENCRYPTION_KEY

# Dashboard login password
openssl rand -hex 24    # → API_KEY

PUBLIC_BASE_URL=https://warmbox.yourdomain.com   # your real public URL
AI_API_KEY=sk-or-v1-...                          # OpenRouter or compatible

Migrating from local: copy your existing prisma/dev.db to the server and use the same ENCRYPTION_KEY from your local .env. Otherwise mailbox credentials cannot be decrypted.


3. Choose how the world reaches your server

No port forwarding. Works behind CGNAT.

  1. Cloudflare Zero TrustNetworksTunnels → Create tunnel

  2. Name it warmbox, install connector via Docker — copy the token

  3. In the tunnel Public Hostname tab, route warmbox.yourdomain.comhttp://warmbox:3000 (Docker network)
    If cloudflared runs in the same compose file, use hostname warmbox and port 3000.

  4. Add to .env:

    CLOUDFLARE_TUNNEL_TOKEN=eyJh...
    PUBLIC_BASE_URL=https://warmbox.yourdomain.com
    
  5. Start:

    docker compose --profile cloudflare up -d --build
    

Option B — Caddy on a public IP

  1. Point DNS warmbox.yourdomain.com → your home public IP

  2. Forward router ports 80 and 443 to the Ubuntu server

  3. Edit deploy/Caddyfile with your domain

  4. Start:

    docker compose --profile caddy up -d --build
    

Option C — LAN / direct port (testing only)

# In docker-compose.yml change ports to "3000:3000"
docker compose up -d --build

Use http://SERVER_LAN_IP:3000 — not suitable for OAuth or remote access.


4. First login

  1. Open https://warmbox.yourdomain.com
  2. Login with your API_KEY
  3. Leave Base URL blank (dashboard and API are same origin in production)

5. Update OAuth redirect URIs

Replace localhost with your public URL in:

Integration Redirect URI
Google Postmaster https://warmbox.yourdomain.com/api/integrations/google/callback
Microsoft Outlook https://warmbox.yourdomain.com/api/integrations/microsoft/callback

Re-connect integrations in Settings after deploy.


6. Migrate local data (optional)

If you already configured mailboxes and campaigns locally:

# On your Mac — stop local dev server first
scp prisma/dev.db user@home-server:/tmp/warmbox.db

# On Ubuntu — before first start OR with stack stopped:
docker compose down
docker volume create warmbox_warmbox-data 2>/dev/null || true
docker run --rm -v warmbox_warmbox-data:/data -v /tmp:/backup alpine \
  cp /backup/warmbox.db /data/warmbox.db
docker compose --profile cloudflare up -d

Copy the same ENCRYPTION_KEY and API_KEY (or set a new API_KEY and use that to log in).


7. Operations

# Logs
docker compose logs -f warmbox

# Restart after code pull
git pull
docker compose --profile cloudflare up -d --build

# Backup database (run daily via cron)
docker compose exec warmbox sh -c 'cp /data/warmbox.db /data/warmbox-$(date +%F).db'

# Health check
curl -s https://warmbox.yourdomain.com/health

30-day warmup checklist

  • Container restart: unless-stopped (default in compose)
  • warmbox-data volume backed up
  • ENCRYPTION_KEY saved in your password manager
  • Campaign started and status active
  • Settings → Jobs shows dispatcher/rescuer running (or check logs every few hours day 1)
  • OAuth redirect URIs updated if using Postmaster / Microsoft

8. Security notes (single-tenant MVP)

  • Always set API_KEY when exposed to the internet.
  • Do not commit .env to git.
  • Restrict dashboard access further with Cloudflare Access (optional) or VPN if desired.
  • Mailbox app passwords live encrypted in SQLite — protect the volume and ENCRYPTION_KEY.

Troubleshooting

Symptom Fix
Login fails Check API_KEY in .env matches what you enter; restart container after .env change
OAuth redirect error PUBLIC_BASE_URL must match browser URL; update provider redirect URI
Jobs not sending docker compose logs warmbox — verify cron lines; confirm SMTP/IMAP from server IP isn't blocked
ENCRYPTION_KEY error on decrypt Wrong key vs database — restore original key or re-add mailboxes
Container unhealthy docker compose logs warmbox — migration or port conflict

See also self-host-hardening.md for operator workflows.