# Deployment Plan: motief.sgeboers.nl **Date:** 2026-03-26 **Subdomain:** `motief.sgeboers.nl` **Stack:** Streamlit · DuckDB · Docker · Nginx · Drone CI **Target:** VPS, `webapps` user at `/home/webapps/motief/` --- ## What's already ready (no changes needed) - `Dockerfile` — builds `streamlit run Home.py --server.port=8501` - `docker-compose.yml` — `motief` + `scheduler` services, `DATA_DIR` env override - `.drone.yml` — builds image, pushes to registry, SSH-deploys on push to `main` - `Home.py`, `pages/1_Stemwijzer.py`, `pages/2_Explorer.py` — all exist --- ## Step A — VPS: one-time directory setup SSH in as `webapps`: ```bash mkdir -p /home/webapps/motief/data ``` Create `/home/webapps/motief/.env`: ```env DOCKER_REGISTRY= DOCKER_USERNAME= DOCKER_PASSWORD= OPENROUTER_API_KEY= OPENAI_API_KEY= ``` Copy `docker-compose.yml` into place: ```bash # From local machine scp docker-compose.yml webapps@:/home/webapps/motief/ ``` Or just clone the repo there and symlink — either works since Drone will overwrite it. --- ## Step B — Transfer the database From local machine (~4 GB, takes a few minutes): ```bash rsync -avz --progress data/motions.db webapps@:/home/webapps/motief/data/motions.db ``` Do this as close to go-live as possible so the data isn't stale on launch. --- ## Step C — DNS Add an **A record** in your DNS provider: ``` stematlas → (obsolete, skip) motief → ``` TTL 300 for the first deploy so you can iterate quickly; bump to 3600 after it's stable. --- ## Step D — Nginx vhost Create `/etc/nginx/sites-available/motief`: ```nginx server { listen 80; server_name motief.sgeboers.nl; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name motief.sgeboers.nl; ssl_certificate /etc/letsencrypt/live/motief.sgeboers.nl/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/motief.sgeboers.nl/privkey.pem; # Streamlit requires WebSocket upgrade for live updates location / { proxy_pass http://127.0.0.1:8501; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 86400; } } ``` Enable and reload: ```bash sudo ln -s /etc/nginx/sites-available/motief /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx ``` --- ## Step E — TLS cert ```bash sudo certbot --nginx -d motief.sgeboers.nl ``` (Assumes Certbot is already installed and working for other subdomains.) --- ## Step F — Configure Drone secrets In the Gitea/Drone repo settings for `sgeboers/stemwijzer`, add: | Secret | Value | |--------|-------| | `DOCKER_REGISTRY` | Your registry URL | | `DOCKER_USERNAME` | Registry login | | `DOCKER_PASSWORD` | Registry password | | `DEPLOY_HOST` | VPS hostname/IP | | `DEPLOY_SSH_PORT` | SSH port (usually 22) | | `DEPLOY_USER` | `webapps` | | `DEPLOY_PASSWORD` | webapps SSH password | --- ## Step G — First deploy Option 1 — trigger Drone automatically: ```bash git push origin main ``` Drone builds → pushes image → SSH into VPS → `docker-compose up -d`. Option 2 — manual first deploy (on VPS): ```bash cd /home/webapps/motief docker-compose pull docker-compose up -d ``` --- ## Step H — Verify ```bash # On VPS docker-compose -f /home/webapps/motief/docker-compose.yml logs -f motief # From local browser open https://motief.sgeboers.nl ``` Checklist: - [ ] Home.py loads with nav to Stemwijzer and Explorer - [ ] Compass tab renders with correct party positions (GL-PvdA top-left, PVV bottom-right) - [ ] SVD tab scree plot shows with highlighted top-2 bars - [ ] Similarity search returns results - [ ] Scheduler container is running (`docker-compose ps`) --- ## Ongoing: data updates The `scheduler` service runs the weekly pipeline inside the container: - Scrapes new motions from the TK OData API - Re-embeds new motion text via OpenRouter - Updates similarity cache The `motions.db` file on the VPS is the single source of truth — it's bind-mounted into both containers. No cron job needed on the host. If you ever need to force a full re-run: ```bash docker-compose exec scheduler python pipeline/run_pipeline.py --db-path data/motions.db ``` --- ## Dependency order ``` A (dirs + .env) ─┐ B (rsync DB) ─┤─► G (first deploy) ─► H (verify) C (DNS) ─┤ D (nginx) ─┤ E (certbot) ─┘ F (Drone secrets) ──► future auto-deploys on push to main ``` Steps A–F can all be done in one SSH session. Total estimated time: **45 minutes** (mostly waiting on rsync).