Files
bincio-activity/docs/deployment/vps.md
T
2026-04-10 12:53:35 +02:00

299 lines
6.2 KiB
Markdown

# VPS deployment guide
Concrete setup for a Debian VPS running a private multi-user bincio instance.
Code is deployed directly from your laptop via `git push` — no GitHub required.
## Assumptions
- Bare Debian 12 VPS with root SSH access
- You own a domain pointed at the VPS
- You have Strava API credentials
- Up to ~30 users
---
## 1. Install system dependencies
```bash
apt update && apt upgrade -y
apt install -y git curl nginx certbot python3-certbot-nginx sqlite3
```
**Node.js 20 LTS** (the Debian package is too old):
```bash
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejs
```
**uv** (manages Python and all Python deps):
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
# add to PATH:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
```
---
## 2. Set up the code directory
```bash
mkdir -p /opt/bincio
git init --bare /opt/bincio-repo.git
```
Create the post-receive hook at `/opt/bincio-repo.git/hooks/post-receive`:
```bash
#!/bin/bash
set -e
REPO=/opt/bincio-repo.git
DEPLOY=/opt/bincio
DATA=/var/bincio/data
echo "--- Checking out code ---"
git --work-tree=$DEPLOY --git-dir=$REPO checkout -f
echo "--- Syncing Python deps ---"
cd $DEPLOY
~/.local/bin/uv sync
echo "--- Syncing JS deps ---"
cd $DEPLOY/site
npm install --silent
echo "--- Building site ---"
cd $DEPLOY
~/.local/bin/uv run bincio render --data-dir $DATA --site-dir $DEPLOY/site
echo "--- Copying dist to webroot ---"
rsync -a --delete $DEPLOY/site/dist/ /var/www/bincio/
echo "--- Restarting API ---"
systemctl restart bincio
echo "--- Done ---"
```
```bash
chmod +x /opt/bincio-repo.git/hooks/post-receive
mkdir -p /var/www/bincio
```
---
## 3. First deploy from your laptop
Add the VPS as a git remote (run this locally, once):
```bash
git remote add vps root@<your-vps-ip>:/opt/bincio-repo.git
```
Push your code:
```bash
git push vps main
```
The hook checks out the code, installs deps, and builds the site.
Subsequent pushes (including unpublished branches) work the same way:
```bash
git push vps mobile_app # deploy any branch directly
```
---
## 4. Initialise the instance
```bash
cd /opt/bincio
uv run bincio init \
--data-dir /var/bincio/data \
--handle dave \
--display-name "Dave" \
--name "My Bincio"
# prompted for password; prints a first invite code
```
Set the user cap:
```bash
sqlite3 /var/bincio/data/instance.db \
"INSERT INTO settings VALUES ('max_users', '30');"
```
---
## 5. Prepare your own activities
Source files (raw GPX/FIT) live separately from the BAS output:
```
/var/bincio/sources/dave/ ← raw activity files, rsync'd from laptop
/var/bincio/data/dave/ ← BAS JSON output (bincio extract writes here)
```
Configure `/opt/bincio/extract_config.yaml` on the server to point to your
source dir:
```yaml
sources:
- path: /var/bincio/sources/dave/activities
type: strava_export
- path: /var/bincio/sources/dave/activities.csv
type: strava_csv
output:
dir: /var/bincio/data
```
Sync and extract (run from your laptop or SSH in):
```bash
# push raw files from laptop
rsync -avz ~/your-activity-data/ root@<vps>:/var/bincio/sources/dave/
# extract on server
ssh root@<vps> "cd /opt/bincio && uv run bincio extract"
# rebuild site
ssh root@<vps> "cd /opt/bincio && \
uv run bincio render --data-dir /var/bincio/data --site-dir site && \
rsync -a --delete site/dist/ /var/www/bincio/"
```
---
## 6. systemd service
Create `/etc/systemd/system/bincio.service`:
```ini
[Unit]
Description=BincioActivity API
After=network.target
[Service]
WorkingDirectory=/opt/bincio
ExecStart=/root/.local/bin/uv run bincio serve \
--data-dir /var/bincio/data \
--site-dir /opt/bincio/site \
--host 127.0.0.1 \
--port 4041
EnvironmentFile=/etc/bincio/secrets.env
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
```
Create `/etc/bincio/secrets.env`:
```bash
mkdir -p /etc/bincio
chmod 700 /etc/bincio
cat > /etc/bincio/secrets.env <<EOF
STRAVA_CLIENT_ID=your_client_id
STRAVA_CLIENT_SECRET=your_client_secret
EOF
chmod 600 /etc/bincio/secrets.env
```
Enable and start:
```bash
systemctl daemon-reload
systemctl enable --now bincio
systemctl status bincio
```
---
## 7. nginx
Create `/etc/nginx/sites-available/bincio`:
```nginx
server {
listen 80;
server_name yourdomain.com;
root /var/www/bincio;
index index.html;
# API → bincio serve
location /api/ {
proxy_pass http://127.0.0.1:4041;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 120s; # Strava sync can be slow
}
# Static files
location / {
try_files $uri $uri/ $uri.html =404;
}
}
```
```bash
ln -s /etc/nginx/sites-available/bincio /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
```
---
## 8. SSL
```bash
certbot --nginx -d yourdomain.com
# certbot edits the nginx config and sets up automatic renewal
```
---
## 9. Invite users
After `bincio init` prints the first invite code, you can generate more from
the browser at `/u/{handle}/athlete/`**Invites** button (visible only to
the page owner), or directly via the CLI:
```bash
sqlite3 /var/bincio/data/instance.db \
"INSERT INTO invites (code, created_by, created_at) \
VALUES (upper(hex(randomblob(4))), 'dave', unixepoch());"
```
Share the link: `https://yourdomain.com/register/?code=XXXXXXXX`
Each new user uploads their activities via the **+** button in the top nav
(supports bulk GPX/FIT/TCX drop). They can later connect Strava for
incremental sync from the same modal.
---
## Day-to-day operations
| Task | Command |
|------|---------|
| Deploy code update | `git push vps main` (from laptop) |
| Sync your raw files | `rsync -avz ~/your-activity-data/ root@<vps>:/var/bincio/sources/dave/` |
| Re-extract after sync | `ssh root@<vps> "cd /opt/bincio && uv run bincio extract"` then push again to rebuild |
| View API logs | `journalctl -u bincio -f` |
| Restart API | `systemctl restart bincio` |
| Check nginx logs | `tail -f /var/log/nginx/error.log` |
| Renew SSL (auto) | `certbot renew --dry-run` |
---
## See also
- [Multi-user architecture](multi-user.md)
- [CLI reference](../reference/cli.md)
- [API reference](../reference/api.md)