206 lines
6.4 KiB
Markdown
206 lines
6.4 KiB
Markdown
|
|
# Deployment auf gemietetem Ubuntu-VPS
|
||
|
|
|
||
|
|
End-to-end Anleitung. Annahme: frischer Ubuntu 22.04/24.04 VPS, du hast root via SSH, Domain optional. Trading läuft gegen Kraken **Demo** (kein echtes Geld).
|
||
|
|
|
||
|
|
## 0. Was du brauchst
|
||
|
|
|
||
|
|
- SSH-Zugriff zum VPS als `root` oder Sudo-User
|
||
|
|
- Git-Remote (GitHub/GitLab/self-hosted) für lokal↔Server-Sync
|
||
|
|
- API-Keys: Kraken Demo (https://demo-futures.kraken.com), Gemini, Anthropic, optional CryptoPanic
|
||
|
|
- Discord-Webhook-URL
|
||
|
|
- Tailscale-Account (kostenlos, https://tailscale.com)
|
||
|
|
|
||
|
|
## 1. Lokales Repo zu Git-Remote pushen
|
||
|
|
|
||
|
|
Auf deinem Laptop:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cd ~/code/aitrader
|
||
|
|
git init
|
||
|
|
git add .
|
||
|
|
git commit -m "initial: aitrader bot + discord notifier"
|
||
|
|
# auf GitHub: neues *privates* Repo "aitrader" anlegen, dann:
|
||
|
|
git remote add origin git@github.com:DEIN_USER/aitrader.git
|
||
|
|
git branch -M main
|
||
|
|
git push -u origin main
|
||
|
|
```
|
||
|
|
|
||
|
|
> ⚠️ **Repo MUSS privat sein.** `.env` ist via `.gitignore` ausgeschlossen, aber Keys gehören niemals ins Repo.
|
||
|
|
|
||
|
|
## 2. VPS vorbereiten
|
||
|
|
|
||
|
|
SSH zum VPS und als root:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# UFW: nur SSH öffentlich
|
||
|
|
ufw default deny incoming
|
||
|
|
ufw default allow outgoing
|
||
|
|
ufw allow 22/tcp
|
||
|
|
ufw enable
|
||
|
|
ufw status
|
||
|
|
```
|
||
|
|
|
||
|
|
## 3. Tailscale installieren
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl -fsSL https://tailscale.com/install.sh | sh
|
||
|
|
tailscale up
|
||
|
|
```
|
||
|
|
|
||
|
|
`tailscale up` zeigt einen Login-Link → in Browser öffnen → mit Google/GitHub einloggen → Server taucht in deinem Tailnet auf. Schreib dir den **Tailscale-Hostnamen** auf (z.B. `aitrader-vps`), den nutzt du gleich fürs Dashboard.
|
||
|
|
|
||
|
|
```bash
|
||
|
|
tailscale ip -4 # zeigt 100.x.y.z — die private Tailnet-IP
|
||
|
|
tailscale status
|
||
|
|
```
|
||
|
|
|
||
|
|
Auch auf **deinem Laptop und Phone** Tailscale installieren und einloggen → alle drei Geräte sind im selben Mesh.
|
||
|
|
|
||
|
|
**Test:** Vom Laptop aus `ping aitrader-vps` (Magic-DNS aktiviert in Tailscale-Admin) oder `ping 100.x.y.z`.
|
||
|
|
|
||
|
|
## 4. Repo auf den VPS clonen
|
||
|
|
|
||
|
|
Als root oder Sudo-User auf dem VPS:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
sudo mkdir -p /opt
|
||
|
|
cd /opt
|
||
|
|
sudo git clone https://github.com/DEIN_USER/aitrader.git aitrader
|
||
|
|
# oder mit SSH-Key:
|
||
|
|
# sudo git clone git@github.com:DEIN_USER/aitrader.git aitrader
|
||
|
|
sudo chown -R $USER:$USER /opt/aitrader
|
||
|
|
cd /opt/aitrader
|
||
|
|
```
|
||
|
|
|
||
|
|
Wenn du **SSH-Keys** für GitHub nutzen willst (für `git pull` ohne Passwort):
|
||
|
|
```bash
|
||
|
|
sudo -u aitrader bash -c 'ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519'
|
||
|
|
sudo cat /home/aitrader/.ssh/id_ed25519.pub
|
||
|
|
# diesen Pub-Key in GitHub → Settings → Deploy Keys (read-only) fürs Repo eintragen
|
||
|
|
```
|
||
|
|
|
||
|
|
(Der `aitrader`-User wird im nächsten Schritt vom Install-Script erstellt — Reihenfolge: erst install.sh, dann SSH-Key generieren.)
|
||
|
|
|
||
|
|
## 5. Install-Script ausführen
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cd /opt/aitrader
|
||
|
|
sudo bash deploy/install.sh
|
||
|
|
```
|
||
|
|
|
||
|
|
Das macht:
|
||
|
|
- erstellt System-User `aitrader`
|
||
|
|
- installiert Python 3.12, uv
|
||
|
|
- legt venv unter `/opt/aitrader/.venv` an, installiert dependencies
|
||
|
|
- kopiert systemd-Units nach `/etc/systemd/system/`
|
||
|
|
|
||
|
|
## 6. `.env` auf dem Server einrichten
|
||
|
|
|
||
|
|
```bash
|
||
|
|
sudo cp /opt/aitrader/.env.example /opt/aitrader/.env
|
||
|
|
sudo nano /opt/aitrader/.env
|
||
|
|
# Trage ein:
|
||
|
|
# KRAKEN_DEMO_KEY=...
|
||
|
|
# KRAKEN_DEMO_SECRET=...
|
||
|
|
# GEMINI_API_KEY=...
|
||
|
|
# ANTHROPIC_API_KEY=...
|
||
|
|
# DISCORD_WEBHOOK_URL=...
|
||
|
|
# CRYPTOPANIC_API_KEY= (optional)
|
||
|
|
sudo chown aitrader:aitrader /opt/aitrader/.env
|
||
|
|
sudo chmod 600 /opt/aitrader/.env
|
||
|
|
```
|
||
|
|
|
||
|
|
## 7. Smoke-Tests vor dem Start
|
||
|
|
|
||
|
|
```bash
|
||
|
|
sudo -u aitrader /opt/aitrader/.venv/bin/python /opt/aitrader/scripts/smoke_kraken.py
|
||
|
|
sudo -u aitrader /opt/aitrader/.venv/bin/python /opt/aitrader/scripts/smoke_ai.py
|
||
|
|
sudo -u aitrader /opt/aitrader/.venv/bin/python /opt/aitrader/scripts/smoke_discord.py
|
||
|
|
```
|
||
|
|
|
||
|
|
Jeder Test sollte ohne Fehler laufen + der Discord-Webhook sollte 2 Embeds posten.
|
||
|
|
|
||
|
|
## 8. Bot + Dashboard als Services starten
|
||
|
|
|
||
|
|
```bash
|
||
|
|
sudo systemctl enable --now aitrader.service
|
||
|
|
sudo systemctl enable --now aitrader-dashboard.service
|
||
|
|
sudo systemctl status aitrader aitrader-dashboard
|
||
|
|
```
|
||
|
|
|
||
|
|
Logs live anschauen:
|
||
|
|
```bash
|
||
|
|
sudo journalctl -u aitrader -f
|
||
|
|
sudo journalctl -u aitrader-dashboard -f
|
||
|
|
```
|
||
|
|
|
||
|
|
## 9. Dashboard via Tailscale erreichen
|
||
|
|
|
||
|
|
Auf deinem Laptop (Tailscale läuft):
|
||
|
|
```
|
||
|
|
http://aitrader-vps:8501
|
||
|
|
```
|
||
|
|
oder
|
||
|
|
```
|
||
|
|
http://100.x.y.z:8501
|
||
|
|
```
|
||
|
|
|
||
|
|
Port 8501 ist **nicht** öffentlich — UFW blockt ihn, Streamlit lauscht auf `0.0.0.0`, aber der Tailscale-Tunnel ist der einzige Weg rein. Wenn du noch paranoider sein willst, in `aitrader-dashboard.service` `--server.address` auf die Tailscale-IP setzen.
|
||
|
|
|
||
|
|
## 10. Updates deployen (Workflow)
|
||
|
|
|
||
|
|
Lokal arbeiten, Änderung pushen, Server pullt + restartet:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# auf dem Laptop
|
||
|
|
cd ~/code/aitrader
|
||
|
|
# … Änderungen mit Claude Code …
|
||
|
|
git add . && git commit -m "fix: foo" && git push
|
||
|
|
|
||
|
|
# auf dem VPS
|
||
|
|
cd /opt/aitrader
|
||
|
|
sudo -u aitrader git pull
|
||
|
|
# bei Code-Änderungen reicht restart; bei neuen Dependencies erst:
|
||
|
|
# sudo -u aitrader /home/aitrader/.local/bin/uv pip install -e .
|
||
|
|
sudo systemctl restart aitrader aitrader-dashboard
|
||
|
|
sudo journalctl -u aitrader -f
|
||
|
|
```
|
||
|
|
|
||
|
|
**Tipp:** Falls du häufig deployst, leg dir lokal eine Funktion an:
|
||
|
|
```bash
|
||
|
|
# in ~/.bashrc
|
||
|
|
deploy-aitrader() {
|
||
|
|
ssh DEIN_VPS 'cd /opt/aitrader && sudo -u aitrader git pull && sudo systemctl restart aitrader aitrader-dashboard && sudo journalctl -u aitrader -n 30'
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 11. Backup
|
||
|
|
|
||
|
|
Die SQLite-DB unter `/opt/aitrader/data/aitrader.db` enthält alle Trades + Decisions. Backup z.B. via cron:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
sudo crontab -e
|
||
|
|
# Tägliches Backup nach /root/backups
|
||
|
|
0 3 * * * cp /opt/aitrader/data/aitrader.db /root/backups/aitrader-$(date +\%F).db
|
||
|
|
```
|
||
|
|
|
||
|
|
## Troubleshooting
|
||
|
|
|
||
|
|
| Problem | Check |
|
||
|
|
|---|---|
|
||
|
|
| `aitrader.service: status=1` | `journalctl -u aitrader -n 100` — meist fehlende ENV-Vars |
|
||
|
|
| Kraken-Symbol nicht gefunden | Kraken Futures nutzt `PI_XBTUSD`-Symbole. `python -c "import ccxt; x=ccxt.krakenfutures(); x.set_sandbox_mode(True); x.load_markets(); print(list(x.symbols)[:30])"` und `pairs:` in `config.yaml` anpassen |
|
||
|
|
| Dashboard nicht erreichbar | `sudo ss -tlnp \| grep 8501` (lauscht?), `tailscale ping aitrader-vps` (Mesh ok?) |
|
||
|
|
| Webhook tot | Discord rate-limit (429)? `journalctl -u aitrader \| grep discord` |
|
||
|
|
| Bot pausiert | `journalctl \| grep daily_loss_limit_hit` — Risk-Limit greift |
|
||
|
|
|
||
|
|
## Sicherheits-Checkliste
|
||
|
|
|
||
|
|
- [ ] Repo ist **privat** auf GitHub
|
||
|
|
- [ ] `.env` hat `chmod 600` und gehört `aitrader:aitrader`
|
||
|
|
- [ ] UFW: nur Port 22 öffentlich offen
|
||
|
|
- [ ] Tailscale `up`, Server im Tailnet
|
||
|
|
- [ ] SSH: Password-Auth aus, nur Key-Auth (`PasswordAuthentication no` in `/etc/ssh/sshd_config`)
|
||
|
|
- [ ] `exchange.sandbox: true` in `config.yaml` — **niemals** versehentlich auf `false`
|
||
|
|
- [ ] Discord-Webhook regelmäßig rotieren falls geleakt
|