Files
PocketVeto/UPDATING.md
Jack Levy 4c86a5b9ca feat: PocketVeto v1.0.0 — initial public release
Self-hosted US Congress monitoring platform with AI policy briefs,
bill/member/topic follows, ntfy + RSS + email notifications,
alignment scoring, collections, and draft-letter generator.

Authored by: Jack Levy
2026-03-15 01:35:01 -04:00

5.8 KiB

Updating PocketVeto — Remote Server Setup & Deployment Workflow

How to push new code from your development machine and pull it on the production server.


Overview

The workflow is:

Local machine  →  git push  →  YOUR_GIT_REMOTE  →  (pull on server)  →  docker compose up --build -d

You develop locally, push to the Gitea remote, then update the production server — either manually over SSH or via an automated webhook.


1. SSH access to the production server

Make sure you can SSH into the server without a password:

# On your local machine — generate a key if you don't have one
ssh-keygen -t ed25519 -C "pocketveto-deploy"

# Copy the public key to the server
ssh-copy-id user@YOUR_SERVER_IP

Test:

ssh user@YOUR_SERVER_IP "echo ok"

2. Server: clone the repo and authenticate

On the server, clone from your Gitea instance:

ssh user@YOUR_SERVER_IP
cd /opt                           # or wherever you want to host it
git clone https://YOUR_GIT_REMOTE.git
cd civicstack

If your Gitea repo is private, create a deploy token in Gitea:

  • Gitea → Repository → Settings → Deploy Keys → Add Deploy Key (read-only is fine)
  • Or: Gitea → User Settings → Applications → Generate Token

Store credentials so git pull doesn't prompt:

# Using a personal access token stored in the URL
git remote set-url origin https://YOUR_TOKEN@YOUR_GIT_REMOTE.git

Verify:

git pull    # should succeed with no password prompt

3. Option A — Manual update (simplest)

SSH in and run:

ssh user@YOUR_SERVER_IP
cd /opt/civicstack

git pull origin main
docker compose up --build -d

That's it. Docker rebuilds only the images that changed (layer cache means unchanged services rebuild in seconds). Migrations run automatically when the API container restarts.

One-liner from your local machine:

ssh user@YOUR_SERVER_IP "cd /opt/civicstack && git pull origin main && docker compose up --build -d"

4. Option B — Deploy script

Create /opt/civicstack/deploy.sh on the server:

#!/bin/bash
set -e

cd /opt/civicstack

echo "==> Pulling latest code"
git pull origin main

echo "==> Building and restarting containers"
docker compose up --build -d

echo "==> Done. Current status:"
docker compose ps
chmod +x /opt/civicstack/deploy.sh

Now from your local machine:

ssh user@YOUR_SERVER_IP /opt/civicstack/deploy.sh

5. Option C — Automated webhook (Gitea → server)

This triggers a deploy automatically every time you push to main.

5a. Create a webhook listener on the server

Install a simple webhook runner. The easiest is webhook:

apt install webhook

Create /etc/webhook/hooks.json:

[
  {
    "id": "civicstack-deploy",
    "execute-command": "/opt/civicstack/deploy.sh",
    "command-working-directory": "/opt/civicstack",
    "response-message": "Deploying...",
    "trigger-rule": {
      "match": {
        "type": "payload-hmac-sha256",
        "secret": "your-webhook-secret",
        "parameter": { "source": "header", "name": "X-Gitea-Signature-256" }
      }
    }
  }
]

Start the webhook service:

# Test it first
webhook -hooks /etc/webhook/hooks.json -port 9000 -verbose

# Or create a systemd service (recommended)

/etc/systemd/system/webhook.service:

[Unit]
Description=Webhook listener for civicstack deploys
After=network.target

[Service]
ExecStart=/usr/bin/webhook -hooks /etc/webhook/hooks.json -port 9000
Restart=on-failure
User=root

[Install]
WantedBy=multi-user.target
systemctl enable --now webhook

Expose port 9000 (or proxy it through nginx/Caddy at a path like /hooks/).

5b. Add the webhook in Gitea

  • Gitea → Repository → Settings → Webhooks → Add Webhook → Gitea
  • Target URL: http://YOUR_SERVER_IP:9000/hooks/civicstack-deploy
  • Secret: same value as your-webhook-secret above
  • Trigger: Push events → branch main

Now every git push origin main automatically triggers a deploy.


6. Checking the deployed version

After any update you can confirm what's running:

# Check the git commit on the server
ssh user@YOUR_SERVER_IP "cd /opt/civicstack && git log --oneline -3"

# Check container status
ssh user@YOUR_SERVER_IP "cd /opt/civicstack && docker compose ps"

# Hit the health endpoint
curl http://YOUR_SERVER_IP/api/health

7. Rolling back

If a bad deploy goes out:

ssh user@YOUR_SERVER_IP
cd /opt/civicstack

# Roll back to the previous commit
git revert HEAD --no-edit      # preferred — creates a revert commit, keeps history clean

# Or hard reset if you're sure (discards the bad commit locally — use with care)
# git reset --hard HEAD~1

git push origin main           # if using Option C, this triggers a new deploy automatically
docker compose up --build -d   # if manual

8. Environment and secrets

.env is not tracked in git. If you need to update a secret or add a new API key on the server:

ssh user@YOUR_SERVER_IP
nano /opt/civicstack/.env

# Then restart only the affected services (usually api + worker)
cd /opt/civicstack
docker compose up -d --no-build api worker beat

--no-build skips the rebuild step — only a config reload is needed for env var changes.


Summary

Goal Command
Manual deploy ssh server "cd /opt/civicstack && git pull && docker compose up --build -d"
One-step deploy script ssh server /opt/civicstack/deploy.sh
Automated on push Gitea webhook → webhook listener → deploy.sh
Rollback git revert HEAD + redeploy
Update env only Edit .env on server + docker compose up -d --no-build api worker beat
Check what's running ssh server "cd /opt/civicstack && git log --oneline -1 && docker compose ps"