Thunderbolt Deployment Pitfalls Full Record
thunderbolt is the open-source AI client from the team behind Mozilla Thunderbird mail. As of April 2026, it has 4,341 GitHub stars, supports all major platforms (Web/iOS/Android/Mac/Linux/Windows), and can connect to Ollama, llama.cpp, or any OpenAI-compatible API. Sounds great—but it took me two full days to get it running. I hit five distinct traps along the way.
This isn't a getting-started tutorial. It's a pitfall debrief. I'm assuming you've read the official docs; this covers what the docs don't explain clearly. Every pitfall includes real error messages and the exact steps I used to diagnose them.
Pitfall 1: Using Node.js Instead of Bun
**Symptom**: npm install errors out, or bun tauri:dev fails immediately
Cause: thunderbolt's frontend and backend require Bun 1.2+. Node.js is not supported.
This is the easiest trap to fall into. The official quick-start.md states Bun as a requirement upfront, but many developers instinctively run npm install—then get a confusing wall of dependency errors.
# Wrong: using Node.js package manager
npm install
# or
yarn install
# Example error (depends on your Node version):
# Error: @tauri-apps/cli@2.x requires Bun >= 1.2.0
# Right: confirm Bun is installed first
bun --version
# If version is below 1.2:
curl -fsSL https://bun.sh/install | bash
# Reload your shell, then:
bun --version # confirm 1.2+
bun install
Quick verification:
bun --version && ls package.json
# Output should show: 1.2.3 (or higher) and package.json exists
Once Bun is confirmed available, the actual setup sequence is:
git clone https://github.com/thunderbird/thunderbolt.git
cd thunderbolt
bun install
cd backend && bun install && cd ..
cp .env.example .env
cp backend/.env.example backend/.env
Pitfall 2: PostgreSQL Container Running on Port 5433 Instead of the Default 5432
Symptom: postgres container exits immediately after starting; or the application can't connect to the database
Cause: thunderbolt's docker-compose.yml maps PostgreSQL to host port 5433 (internally still 5432), to avoid clashing with any existing local PostgreSQL installation.
Here's the actual config from the official deploy/docker-compose.yml:
postgres:
image: postgres:18-alpine
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
- "${POSTGRES_PORT:-5433}:5432" # 5433 on host, not 5432
If you have a local PostgreSQL running on 5432, this design is sensible. But the problem is: documentation and config files reference port 5432 everywhere, while docker-compose.yml silently uses 5433.
How to diagnose:
# 1. Check postgres container status
docker compose ps
# If you see 'postgres exited (1)', check the logs:
docker compose logs postgres
# 2. Common error: port already in use
# docker-compose logs postgres shows:
# FATAL: could not create lock file "/var/run/postgresql/.s.PGSQL.5432.lock" : No such file or directory
# 3. Fix: set the port in thunderbolt/deploy/.env:
POSTGRES_PORT=5433
# 4. Also verify backend connection string uses the correct port:
# In backend/.env it should be:
DATABASE_URL=postgresql://postgres:postgres@localhost:5433/postgres
# NOT:
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres
Production tip: If you already run other services on PostgreSQL, change POSTGRES_PORT in .env to an unused port and sync the update to DATABASE_URL.
Pitfall 3: BETTER_AUTH_SECRET Is Missing or Using the Default Value in Production
Symptom: registration/login fails after startup; or all users share the same account
**Cause**: the .env.example ships with a default value BETTER_AUTH_SECRET=better-auth-secret-change-in-production-12345678901234567890. This default works fine for local solo development, but **on a server accessed by multiple users, sharing the same SECRET causes serious session and authentication bugs**.
This was documented in GitHub Issue #771: a Kubernetes deployment was missing the BETTER_AUTH_SECRET environment variable, breaking the backend entirely.
# Generate a random SECRET (macOS/Linux):
openssl rand -base64 32
# Output example: W9K3xL2pVN8mQrTlE5fH1sD6aJcM7nR4BvC8wGxT3kY=
# In backend/.env, set:
BETTER_AUTH_SECRET=W9K3xL2pVN8mQrTlE5fH1sD6aJcM7nR4BvC8wGxT3kY=
How to know this is your problem: if the app logs show Error: Better Auth secret is not set or is too short, the BETTER_AUTH_SECRET is missing or shorter than 32 characters.
Also, if you're deploying thunderbolt for multiple team members on the same server, each person needs a different BETTER_AUTH_SECRET value (or you need to switch to OIDC mode for SSO). The default SECRET causes all users to be recognized as the same session.
Pitfall 4: PowerSync Service Not Started Breaks Multi-Device Sync
Symptom: logging into the same account on phone and laptop shows different data; or the app appears as a fresh install every time you open it
**Cause**: thunderbolt's multi-device sync depends on PowerSync (a separate Docker container), but the quick-start guide only mentions make up, so many users don't realize PowerSync needs to be launched separately.
The complete correct startup sequence:
# 1. Start dependency services first (PostgreSQL + PowerSync)
make up
# This runs:
# docker compose -f powersync-service/docker-compose.yml up -d
# 2. Verify service status
make status
# Should show: postgres running, powersync running
# 3. If PowerSync isn't running, start it manually:
cd powersync-service
docker compose up -d
cd ..
# 4. Confirm PowerSync is listening on port 8080
curl http://localhost:8080/health
# Expected response: {"status":"ok"}
# 5. Then start the application
bun run dev # frontend on :5173
# or bun run backend (API only)
In backend/.env, verify the PowerSync-related variables are set:
DATABASE_DRIVER=postgres
DATABASE_URL=postgresql://postgres:postgres@localhost:5433/postgres
POWERSYNC_URL=http://localhost:8080
POWERSYNC_JWT_SECRET=powersync-dev-secret-change-in-production
If you don't need multi-device sync right now (single machine use only), you can fall back to the local Pglite driver by commenting out PowerSync variables in backend/.env:
DATABASE_DRIVER=pglite
DATABASE_URL=.pglite/data
# POWERSYNC_URL= # comment this out
Pitfall 5: VITE_THUNDERBOLT_CLOUD_URL Misconfiguration Causes All API Requests to Fail
Symptom: the UI loads fine, but sending any message shows "network error" or "server connection failed"; all API requests return 404 in the Network tab
**Cause**: thunderbolt's frontend needs to know where the backend service is. This is configured via the VITE_THUNDERBOLT_CLOUD_URL environment variable. The default is http://localhost:8000/v1, but if your backend runs on a different port or hostname, this value must be updated to match.
# In frontend/.env or .env (project root):
VITE_THUNDERBOLT_CLOUD_URL=http://localhost:8000/v1
# If you changed the backend port (e.g., to 8080):
VITE_THUNDERBOLT_CLOUD_URL=http://localhost:8080/v1
# For remote server deployment (假设IP是 203.0.113.42, frontend port 3000):
VITE_THUNDERBOLT_CLOUD_URL=http://203.0.113.42:8000/v1
# Also update in backend/.env:
TRUSTED_ORIGINS=http://203.0.113.42:3000
CORS_ORIGINS=http://203.0.113.42:3000
APP_URL=http://203.0.113.42:3000
BETTER_AUTH_URL=http://203.0.113.42:8000
How to diagnose:
# 1. Open browser DevTools -> Network tab
# 2. Find a failed API request and check the Request URL
# 3. If the URL is http://localhost:8000/v1/chat and similar:
# - Verify backend is actually running on 8000: curl http://localhost:8000/v1/health
# 4. If backend is running fine, check for CORS errors:
# - DevTools Console shows:
# "Access to fetch at 'http://localhost:8000' from origin 'http://localhost:5173'
# has been blocked by CORS policy"
# - Fix: in backend/.env, add/modify:
CORS_ORIGINS=http://localhost:5173,http://localhost:3000
Note: this gets more complicated behind an Nginx 性能调优 reverse proxy, because the backend address is hard-coded in the official deploy/config/nginx.conf (Issue #777). You'll need to update both nginx config and VITE_THUNDERBOLT_CLOUD_URL.
---
The Complete Correct Deployment Sequence
Based on all the above pitfall fixes, here's the full local development setup:
# 1. Install Bun (required—Node.js won't work)
curl -fsSL https://bun.sh/install | bash
source ~/.bashrc # or restart your terminal
# 2. Clone and install
git clone https://github.com/thunderbird/thunderbolt.git
cd thunderbolt
bun install
cd backend && bun install && cd ..
# 3. Configure environment variables
cp .env.example .env
cp backend/.env.example backend/.env
# In backend/.env, set these required values:
# BETTER_AUTH_SECRET=$(openssl rand -base64 32)
# ANTHROPIC_API_KEY=sk-... (or OLLAMA_URL + another API key)
# DATABASE_DRIVER=postgres
# DATABASE_URL=postgresql://postgres:postgres@localhost:5433/postgres
# POWERSYNC_URL=http://localhost:8080
# 4. Start dependency services (required)
docker compose -f powersync-service/docker-compose.yml up -d
# 5. Verify dependencies
curl http://localhost:8080/health # PowerSync health check
docker compose -f powersync-service/docker-compose.yml ps # PostgreSQL status
# 6. Start frontend + backend
bun run dev
# 7. Open http://localhost:5173
Is thunderbolt Worth the Effort?
After working through all five pitfalls, thunderbolt actually holds up as a fairly complete AI client. Its biggest strength is not being locked into any specific AI provider—you can connect to Ollama (local models), OpenAI, Anthropic, Mistral, and Fireworks simultaneously, making cross-provider quality comparison very convenient.
If you're primarily running Ollama for local models, thunderbolt's cross-platform UI is significantly better than calling curl. But the deployment does have a learning curve—it's not a "download and run" product. You need to be comfortable with Docker, Bun, and environment variables.
Worth it if: you already have experience with Ollama or other AI APIs and want a unified interface to manage multiple models/providers.
Not for you if: you're completely new to Docker and command-line tools (wait for the official hosted version).
👉 立即参与:https://platform.minimaxi.com/subscribe/token-plan?code=E5yur9NOub&source=link
🔗 Related Tech Articles
Deep dive into related technical topics: