warmbox/docs/deploy-ubuntu-docker.md

278 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
**Not suitable:** Netlify, GitHub Pages, Vercel — no background jobs, no SQLite, no mail sockets.
---
## Your setup (`warmbox.jonnyn.com`)
You already have:
- DNS **A** record → home public IP
- **Nginx Proxy Manager** (NPM) on Docker network `npm_network`
**Recommended for you: Option B — Nginx Proxy Manager** (simplest; reuses what you have).
Use **Option A — Cloudflare Tunnel** only if you want HTTPS without opening ports 80/443, or if your ISP blocks inbound traffic.
Do **not** run Caddy (Option C) alongside NPM — both want ports 80 and 443.
---
## 1. Prepare the server
```bash
cd ~/warmbox # or wherever you cloned the repo
cp .env.example .env
nano .env
```
Required in `.env`:
```bash
ENCRYPTION_KEY=<64 hex chars — openssl rand -hex 32>
API_KEY=<openssl rand -hex 24>
PUBLIC_BASE_URL=https://warmbox.jonnyn.com
AI_API_KEY=sk-or-v1-...
```
Keep `ENCRYPTION_KEY` forever. If migrating from your Mac, copy the same key and `prisma/dev.db`.
---
## 2. Fix: “port 3000 already allocated”
Warmbox listens on **port 3000 inside its container**. That does **not** conflict with `homepage` using host port `3000` — unless you publish Warmbox to the host with a `ports:` mapping.
**NPM and Cloudflare Tunnel only need Docker network access** (`warmbox:3000`). No host port is required.
### Verify your compose file on the server
```bash
cd /srv/docker-configs/warmbox
grep -n ports docker-compose.yml docker-compose.npm.yml 2>/dev/null
docker compose -f docker-compose.yml -f docker-compose.npm.yml config | grep -A2 published
```
- **Correct:** no `published` / `ports` for the `warmbox` service (empty grep for warmbox ports).
- **Wrong:** lines like `"3000:3000"` or `"127.0.0.1:3000:3000"` under `warmbox`**delete the entire `ports:` block** for that service.
Pull the latest repo (or remove `ports:` manually), then:
```bash
docker compose -f docker-compose.yml -f docker-compose.npm.yml down
docker compose -f docker-compose.yml -f docker-compose.npm.yml up -d --build
```
### Optional: direct LAN access on a different host port
Only if you want to hit Warmbox without NPM (debugging). This uses host **3001**, not 3000:
```yaml
ports:
- "127.0.0.1:3001:3000"
```
NPM still uses forward port **3000** (container port), not 3001.
### Find what uses host port 3000
```bash
docker ps --format '{{.Names}} {{.Ports}}' | grep '0.0.0.0:3000'
```
In your case `homepage` owns host 3000 — leave it alone; Warmbox does not need that port.
---
## Option B — Nginx Proxy Manager (recommended for your home server)
Traffic path:
```text
Internet → warmbox.jonnyn.com (DNS A → home IP) → NPM :443 → warmbox:3000 (Docker)
```
### Step 1 — Start Warmbox on `npm_network`
```bash
docker compose -f docker-compose.yml -f docker-compose.npm.yml up -d --build
```
Verify:
```bash
docker compose -f docker-compose.yml -f docker-compose.npm.yml ps
docker compose -f docker-compose.yml -f docker-compose.npm.yml logs -f warmbox
curl -s http://warmbox:3000/health # run from any container on npm_network, or skip
```
### Step 2 — NPM proxy host
In Nginx Proxy Manager UI:
| Field | Value |
|--------|--------|
| Domain names | `warmbox.jonnyn.com` |
| Scheme | `http` |
| Forward hostname / IP | `warmbox` |
| Forward port | `3000` (container port — not your host port 3000 used by homepage) |
| Cache assets | off |
| Block common exploits | on |
| Websockets support | on |
**SSL** tab:
- Request a new SSL Certificate (Let's Encrypt)
- Force SSL + HTTP/2
If you use **Cloudflare orange cloud** (proxied) on the A record, set Cloudflare SSL mode to **Full** or **Full (strict)** so NPMs certificate works.
### Step 3 — Login
1. Open `https://warmbox.jonnyn.com`
2. API key = your `API_KEY` from `.env`
3. Leave **Base URL** blank
### Step 4 — OAuth redirect URIs
| Integration | Redirect URI |
|-------------|----------------|
| Google Postmaster | `https://warmbox.jonnyn.com/api/integrations/google/callback` |
| Microsoft Outlook | `https://warmbox.jonnyn.com/api/integrations/microsoft/callback` |
---
## Option A — Cloudflare Tunnel
Use this if you prefer **no inbound ports** on your router (tunnel only).
Traffic path:
```text
Browser → Cloudflare edge → cloudflared (Docker) → warmbox:3000
```
### Important DNS note
For tunnel-only access, let the **tunnel** own the hostname. In Cloudflare Zero Trust → your tunnel → **Public Hostname**:
| Field | Value |
|--------|--------|
| Subdomain | `warmbox` |
| Domain | `jonnyn.com` |
| Type | HTTP |
| URL | `warmbox:3000` |
Cloudflare usually creates a CNAME for you. You can **remove the A record** for `warmbox` if everything goes through the tunnel (avoids split routing).
### Step 1 — `.env`
```bash
CLOUDFLARE_TUNNEL_TOKEN=eyJh... # from Zero Trust → Tunnels → your tunnel → Docker install
PUBLIC_BASE_URL=https://warmbox.jonnyn.com
```
### Step 2 — Start (no NPM override file)
```bash
docker compose --profile cloudflare up -d --build
```
`cloudflared` and `warmbox` share the `warmbox_internal` network — hostname `warmbox` resolves inside that network.
### Step 3 — Verify
```bash
docker compose --profile cloudflare ps
docker compose --profile cloudflare logs -f cloudflared
docker compose --profile cloudflare logs -f warmbox
curl -s https://warmbox.jonnyn.com/health
```
### Tunnel + NPM together?
Pick **one** public entry for `warmbox.jonnyn.com`:
- **NPM only:** A record → home IP, no tunnel hostname for the same subdomain.
- **Tunnel only:** tunnel hostname, no A record to home IP for that subdomain.
Running both for the same hostname causes confusing SSL or routing issues.
---
## Option C — Caddy (standalone, no NPM)
Only if NPM is **not** using 80/443 on this host.
1. Edit `deploy/Caddyfile` — set `warmbox.jonnyn.com`
2. DNS A record → home public IP; forward router 80/443 to the server
3. Start:
```bash
docker compose --profile caddy up -d --build
```
**Skip this** if Nginx Proxy Manager is already on the server.
---
## Migrate local database (optional)
```bash
# Mac
scp prisma/dev.db user@home-server:/tmp/warmbox.db
# Server — stack stopped
docker compose -f docker-compose.yml -f docker-compose.npm.yml down
docker run --rm -v warmbox_warmbox-data:/data -v /tmp:/backup alpine \
cp /backup/warmbox.db /data/warmbox.db
docker compose -f docker-compose.yml -f docker-compose.npm.yml up -d
```
Use the **same** `ENCRYPTION_KEY` as on your Mac.
---
## Day-to-day commands
**NPM deployment:**
```bash
docker compose -f docker-compose.yml -f docker-compose.npm.yml logs -f warmbox
docker compose -f docker-compose.yml -f docker-compose.npm.yml up -d --build
```
**Cloudflare deployment:**
```bash
docker compose --profile cloudflare logs -f warmbox
docker compose --profile cloudflare up -d --build
```
**Backup:**
```bash
docker compose exec warmbox sh -c 'cp /data/warmbox.db /data/warmbox-$(date +%F).db'
```
---
## Troubleshooting
| Symptom | Fix |
|---------|-----|
| `port 3000 already allocated` | Use updated compose (no host ports); `docker compose down`; find conflict with `docker ps \| grep 3000` |
| NPM 502 Bad Gateway | Warmbox not on `npm_network`; wrong forward host (`warmbox` not `localhost`); check `docker compose logs warmbox` |
| Tunnel 502 / error | Public hostname URL must be `http://warmbox:3000`; both containers on `warmbox_internal`; check `logs cloudflared` |
| Login fails | `API_KEY` in `.env` must match login; restart after `.env` change |
| OAuth redirect error | `PUBLIC_BASE_URL=https://warmbox.jonnyn.com` exactly; update Google/Microsoft redirect URIs |
| SSL errors with Cloudflare proxy | Cloudflare SSL mode **Full** or **Full (strict)** when NPM terminates TLS |
See also [self-host-hardening.md](./self-host-hardening.md).