warmbox/docs/deploy-ubuntu-docker.md

7.9 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

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

cd ~/warmbox   # or wherever you cloned the repo
cp .env.example .env
nano .env

Required in .env:

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

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 warmboxdelete the entire ports: block for that service.

Pull the latest repo (or remove ports: manually), then:

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:

ports:
  - "127.0.0.1:3001:3000"

NPM still uses forward port 3000 (container port), not 3001.

Find what uses host port 3000

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.


Traffic path:

Internet → warmbox.jonnyn.com (DNS A → home IP) → NPM :443 → warmbox:3000 (Docker)

Step 1 — Start Warmbox on npm_network

docker compose -f docker-compose.yml -f docker-compose.npm.yml up -d --build

Verify:

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:

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

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)

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

cloudflared and warmbox share the warmbox_internal network — hostname warmbox resolves inside that network.

Step 3 — Verify

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:
docker compose --profile caddy up -d --build

Skip this if Nginx Proxy Manager is already on the server.


Migrate local database (optional)

# 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:

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:

docker compose --profile cloudflare logs -f warmbox
docker compose --profile cloudflare up -d --build

Backup:

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.