- docker-compose.yml: replace named volumes with ./postgres/data and ./redis/data bind mounts - .gitignore: exclude postgres/ and redis/ data directories - DEPLOYING.md: update clone URL to public PocketVeto repo - UPDATING.md: fix paths (~/pocketveto), clone URL, webhook IDs Authored by: Jack Levy
6.0 KiB
Deploying PocketVeto
Step-by-step guide for standing up the full stack on a fresh server.
Prerequisites
Server:
- Linux (Ubuntu 22.04+ or Debian 12 recommended)
- Docker Engine 24+ and Docker Compose v2 (
docker compose— note: no hyphen) - At least 2 GB RAM (4 GB recommended if running an Ollama LLM locally)
- Port 80 open to the internet (and 443 if you add SSL)
API keys you will need:
| Key | Where to get it | Required? |
|---|---|---|
DATA_GOV_API_KEY |
api.data.gov/signup — free, instant | Yes |
| One LLM key (OpenAI / Anthropic / Gemini) | Provider dashboard | Yes (or use Ollama) |
NEWSAPI_KEY |
newsapi.org — free tier (100 req/day) | Optional |
Google Trends (pytrends) needs no key.
1. Get the code
git clone https://git.jackhlevy.com/jack/PocketVeto.git pocketveto
cd pocketveto
2. Configure environment
cp .env.example .env
nano .env # or your preferred editor
Minimum required values:
# Network
LOCAL_URL=http://YOUR_SERVER_IP # or https://yourdomain.com if behind SSL
PUBLIC_URL= # leave blank unless you have a public domain
# Auth — generate with: python -c "import secrets; print(secrets.token_hex(32))"
JWT_SECRET_KEY=your-generated-secret
# Encryption key for sensitive prefs (generate once, never change after data is written)
ENCRYPTION_SECRET_KEY= # generate: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
# PostgreSQL
POSTGRES_USER=congress
POSTGRES_PASSWORD=your-strong-password
POSTGRES_DB=pocketveto
# Redis
REDIS_URL=redis://redis:6379/0
# Congress.gov + GovInfo (shared api.data.gov key)
DATA_GOV_API_KEY=your-api-key
# LLM — pick one
LLM_PROVIDER=openai
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-4o-mini
Other providers (swap in place of the OpenAI block):
# Anthropic
LLM_PROVIDER=anthropic
ANTHROPIC_API_KEY=sk-ant-...
ANTHROPIC_MODEL=claude-sonnet-4-6
# Gemini
LLM_PROVIDER=gemini
GEMINI_API_KEY=AIza...
GEMINI_MODEL=gemini-2.0-flash
# Ollama (local model — server must be running on the host)
LLM_PROVIDER=ollama
OLLAMA_BASE_URL=http://host.docker.internal:11434
OLLAMA_MODEL=llama3.1
Optional extras:
NEWSAPI_KEY=your-newsapi-key # enables richer news correlation
PYTRENDS_ENABLED=true # Google Trends; disable if hitting rate limits
CONGRESS_POLL_INTERVAL_MINUTES=30 # how often to check Congress.gov
# Email notifications (optional — requires SMTP relay, e.g. Resend)
SMTP_HOST=smtp.resend.com
SMTP_PORT=465
SMTP_USER=resend
SMTP_PASSWORD=re_your-api-key
SMTP_FROM=alerts@yourdomain.com
3. Build and start
docker compose up --build -d
This will:
- Pull base images (postgres, redis, nginx, node)
- Build the API, worker, beat, and frontend images
- Start all 7 containers
- Run
alembic upgrade headautomatically inside the API container on startup - Seed the Celery Beat schedule in Redis
First build takes 3–8 minutes depending on your server. Subsequent builds are faster (Docker layer cache).
4. Verify it's running
docker compose ps
All services should show Up:
civicstack-api-1 Up
civicstack-beat-1 Up
civicstack-frontend-1 Up
civicstack-nginx-1 Up 0.0.0.0:80->80/tcp
civicstack-postgres-1 Up (healthy)
civicstack-redis-1 Up (healthy)
civicstack-worker-1 Up
Check the API health endpoint:
curl http://localhost/api/health
# → {"status":"ok","timestamp":"..."}
Open http://YOUR_SERVER_IP in a browser.
5. Create the admin account
Navigate to http://YOUR_SERVER_IP/register and create the first account.
The first registered account automatically becomes admin. All subsequent accounts are regular users. The admin account gets access to the Settings page with the pipeline controls, LLM switching, and user management.
6. Trigger initial data load
Log in as admin and go to Settings:
- Trigger Poll — fetches bills updated in the last 60 days from Congress.gov (~5–10 minutes to complete)
- Sync Members — syncs current Congress members (~2 minutes)
The Celery workers then automatically:
- Fetch bill text from GovInfo
- Generate AI briefs (rate-limited at 10/minute)
- Fetch news articles and calculate trend scores
You can watch progress in Settings → Pipeline Status.
7. Optional: Domain + SSL
If you have a domain name pointing to the server, add an SSL terminator in front of nginx. The simplest approach is Caddy as a reverse proxy:
# Install Caddy
apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update && apt install caddy
/etc/caddy/Caddyfile:
yourdomain.com {
reverse_proxy localhost:80
}
systemctl reload caddy
Caddy handles HTTPS certificates automatically via Let's Encrypt.
After adding SSL, update .env:
PUBLIC_URL=https://yourdomain.com
Then rebuild the API so the new URL is used in notification payloads:
docker compose up --build -d api
Useful commands
# View logs for a service
docker compose logs --tail=50 api
docker compose logs --tail=50 worker
docker compose logs -f worker # follow in real time
# Restart a service
docker compose restart worker
# Run a database query
docker compose exec postgres psql -U congress pocketveto
# Apply any pending migrations manually
docker compose exec api alembic upgrade head
# Open a Python shell inside the API container
docker compose exec api python
Troubleshooting
See TROUBLESHOOTING.md for common issues (502 errors after rebuild, wrong postgres user, frontend changes not showing, etc.).