From 1f11a65b0bbb1bdf2223f22eb5ec3a7f858932e9 Mon Sep 17 00:00:00 2001 From: David Kiania Date: Sat, 11 Apr 2026 08:23:01 +0300 Subject: [PATCH] =?UTF-8?q?Add=2002=5Ftracksolid=5Fdocker=5Fcommands.md=20?= =?UTF-8?q?=E2=80=94=20remote=20DB=20command=20reference?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Comprehensive reference for SSH + docker exec psql access to the TimescaleDB instance on rahamafresh.com. Covers: - How it works (SSH → docker exec → psql layers explained) - tsdb() shell function setup for the server - Mac one-liners for single queries, interactive sessions, piping SQL files - Fleet & live positions queries (active vehicles, silent devices, anomalies) - Trips & movement (today's KPIs, speeding, after-hours, utilisation rate) - Alarms (summary, unacknowledged, acknowledge) - Parking & idle time - Position history & route replay - Ingestion pipeline health checks - Device registry (metadata gaps, odometer, subscriptions) - Schema & migration operations - Container & service operations (logs, restart, disk, chunk sizes) - Quick reference table for all flags Co-Authored-By: Claude Sonnet 4.6 --- 02_tracksolid_docker_commands.md | 733 +++++++++++++++++++++++++++++++ 1 file changed, 733 insertions(+) create mode 100644 02_tracksolid_docker_commands.md diff --git a/02_tracksolid_docker_commands.md b/02_tracksolid_docker_commands.md new file mode 100644 index 0000000..e4a421a --- /dev/null +++ b/02_tracksolid_docker_commands.md @@ -0,0 +1,733 @@ +# Tracksolid — Docker & Remote Database Command Reference + +**Server:** `rahamafresh.com` · **User:** `kianiadee` +**DB:** `tracksolid_db` · **DB user:** `postgres` +**Container:** TimescaleDB — name includes a Coolify-generated suffix that changes on every redeploy + +--- + +## Table of Contents + +1. [How It Works](#1-how-it-works) +2. [Setup — Shell Function on the Server](#2-setup--shell-function-on-the-server) +3. [Connecting From Your Mac](#3-connecting-from-your-mac) +4. [Fleet & Live Positions](#4-fleet--live-positions) +5. [Trips & Movement](#5-trips--movement) +6. [Alarms](#6-alarms) +7. [Parking & Idle Time](#7-parking--idle-time) +8. [Position History & Route Replay](#8-position-history--route-replay) +9. [Ingestion Pipeline Health](#9-ingestion-pipeline-health) +10. [Device & Fleet Registry](#10-device--fleet-registry) +11. [Schema & Migrations](#11-schema--migrations) +12. [Container & Service Operations](#12-container--service-operations) +13. [Quick Reference — Flag Meanings](#13-quick-reference--flag-meanings) + +--- + +## 1. How It Works + +All remote database access uses two layers: + +``` +Your Mac → SSH to rahamafresh.com → docker exec into timescale_db → psql +``` + +**Layer 1 — find the container.** Coolify appends a random suffix to container names on every redeploy, so the container name is never hardcoded. Instead, resolve it dynamically: + +```bash +TS_DB=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) +``` + +**Layer 2 — run psql inside it.** + +```bash +docker exec -i "$TS_DB" psql -U postgres -d tracksolid_db -c "SELECT ..." +``` + +Use `-i` (not `-it`) when running non-interactively or piping input — the `-t` TTY flag conflicts with stdin redirection and causes errors. + +--- + +## 2. Setup — Shell Function on the Server + +Add this once to `~/.zshrc` on the server. It resolves the container automatically and passes all arguments through to `psql`. + +```bash +# Auto-resolves current TimescaleDB container — survives Coolify redeployments +tsdb() { + local container + container=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) + if [[ -z "$container" ]]; then + echo "ERROR: no running timescale_db container found" >&2 + return 1 + fi + docker exec -it "$container" psql -U postgres -d tracksolid_db "$@" +} +``` + +**Activate:** +```bash +source ~/.zshrc +``` + +**Usage on the server:** +```bash +tsdb # open interactive psql prompt +tsdb -c "SELECT COUNT(*) FROM tracksolid.trips;" +tsdb -c "\dt tracksolid.*" # list all tracksolid tables +tsdb -c "\d tracksolid.trips" # describe a table +tsdb -f /app/my_query.sql # run a SQL file +``` + +--- + +## 3. Connecting From Your Mac + +### Open an interactive psql session +```bash +ssh kianiadee@rahamafresh.com -t \ + 'TS=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) && + docker exec -it "$TS" psql -U postgres -d tracksolid_db' +``` + +### Run a single query and return output to your terminal +```bash +ssh kianiadee@rahamafresh.com \ + 'TS=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) && + docker exec -i "$TS" psql -U postgres -d tracksolid_db -c "SELECT now();"' +``` + +### Pipe a local SQL file into the remote database +```bash +ssh kianiadee@rahamafresh.com \ + 'TS=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) && + docker exec -i "$TS" psql -U postgres -d tracksolid_db' \ + < /path/to/local_query.sql +``` + +### Run a migration file from your Mac +```bash +ssh kianiadee@rahamafresh.com \ + 'TS=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) && + docker exec -i "$TS" psql -U postgres -d tracksolid_db' \ + < 05_enhancement_migration.sql +``` + +> **Note on quoting:** When embedding SQL in a shell one-liner, single-quote literals inside the SQL need to be escaped as `'"'"'`. The `tsdb()` function avoids this entirely — use it for complex queries. + +--- + +## 4. Fleet & Live Positions + +### All devices with a position in the last hour +```bash +ssh kianiadee@rahamafresh.com \ + 'TS=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) && + docker exec -i "$TS" psql -U postgres -d tracksolid_db -c " +SELECT + d.device_name, + d.mc_type, + ROUND(lp.lat::numeric, 5) AS lat, + ROUND(lp.lng::numeric, 5) AS lng, + lp.speed, + lp.acc_status, + lp.gps_signal, + lp.gps_num AS satellites, + lp.gps_time AT TIME ZONE '"'"'Africa/Nairobi'"'"' AS last_fix_eat, + ROUND(EXTRACT(EPOCH FROM (now() - lp.gps_time)) / 60.0, 0) AS mins_ago +FROM tracksolid.live_positions lp +JOIN tracksolid.devices d ON d.imei = lp.imei +WHERE lp.gps_time > now() - interval '"'"'1 hour'"'"' +ORDER BY lp.gps_time DESC;"' +``` + +### All 19 live positions — current snapshot +```sql +SELECT + d.device_name, + d.mc_type, + ROUND(lp.lat::numeric, 5) AS lat, + ROUND(lp.lng::numeric, 5) AS lng, + lp.speed, + lp.acc_status, + lp.gps_time AT TIME ZONE 'Africa/Nairobi' AS last_fix_eat, + ROUND(EXTRACT(EPOCH FROM (now() - lp.gps_time)) / 3600.0, 1) AS hours_ago, + lp.current_mileage AS odometer_km +FROM tracksolid.live_positions lp +JOIN tracksolid.devices d ON d.imei = lp.imei +ORDER BY lp.gps_time DESC; +``` + +### Devices with no position (silent fleet) +```sql +SELECT d.imei, d.device_name, d.mc_type, d.sim, d.expiration::date +FROM tracksolid.devices d +LEFT JOIN tracksolid.live_positions lp ON lp.imei = d.imei +WHERE lp.imei IS NULL +ORDER BY d.mc_type, d.device_name; +``` + +### Vehicles currently moving (ACC on OR speed > 0) +```sql +SELECT + d.device_name, + lp.speed, + lp.acc_status, + lp.gps_time AT TIME ZONE 'Africa/Nairobi' AS last_fix_eat +FROM tracksolid.live_positions lp +JOIN tracksolid.devices d ON d.imei = lp.imei +WHERE lp.speed > 0 OR lp.acc_status = '1' +ORDER BY lp.speed DESC; +``` + +### Geographic anomaly check — vehicles outside Kenya +```sql +SELECT + d.device_name, + ROUND(lp.lat::numeric, 4) AS lat, + ROUND(lp.lng::numeric, 4) AS lng, + lp.gps_time AT TIME ZONE 'Africa/Nairobi' AS last_fix_eat +FROM tracksolid.live_positions lp +JOIN tracksolid.devices d ON d.imei = lp.imei +WHERE lp.lat NOT BETWEEN -5.0 AND 5.0 + OR lp.lng NOT BETWEEN 33.9 AND 42.0; +``` + +--- + +## 5. Trips & Movement + +### Trips today — summary per vehicle +```sql +SELECT + d.device_name, + d.vehicle_number, + d.driver_name, + COUNT(*) AS trips, + ROUND(SUM(t.distance_km)::numeric, 2) AS total_km, + ROUND(AVG(t.avg_speed_kmh)::numeric, 1) AS avg_speed_kmh, + MAX(t.max_speed_kmh) AS top_speed_kmh, + ROUND(SUM(t.driving_time_s) / 3600.0, 2) AS drive_hours, + ROUND(SUM(t.idle_time_s) / 3600.0, 2) AS idle_hours, + MIN(t.start_time AT TIME ZONE 'Africa/Nairobi') AS day_start, + MAX(t.end_time AT TIME ZONE 'Africa/Nairobi') AS day_end +FROM tracksolid.trips t +JOIN tracksolid.devices d ON d.imei = t.imei +WHERE t.start_time >= CURRENT_DATE AT TIME ZONE 'Africa/Nairobi' +GROUP BY d.device_name, d.vehicle_number, d.driver_name +ORDER BY total_km DESC; +``` + +### Trips in last 24 hours — full detail +```sql +SELECT + d.device_name, + t.start_time AT TIME ZONE 'Africa/Nairobi' AS start_eat, + t.end_time AT TIME ZONE 'Africa/Nairobi' AS end_eat, + ROUND(t.distance_km::numeric, 2) AS distance_km, + t.avg_speed_kmh, + t.max_speed_kmh, + ROUND(t.driving_time_s / 60.0, 0) AS drive_mins, + ROUND(t.idle_time_s / 60.0, 0) AS idle_mins, + t.source +FROM tracksolid.trips t +JOIN tracksolid.devices d ON d.imei = t.imei +WHERE t.start_time > now() - interval '24 hours' +ORDER BY t.start_time DESC; +``` + +### Speeding incidents — trips where max speed exceeded threshold +```sql +-- Change 80 to your speed limit in km/h +SELECT + d.device_name, + d.driver_name, + t.start_time AT TIME ZONE 'Africa/Nairobi' AS trip_start, + t.max_speed_kmh, + ROUND(t.distance_km::numeric, 2) AS distance_km +FROM tracksolid.trips t +JOIN tracksolid.devices d ON d.imei = t.imei +WHERE t.max_speed_kmh > 80 +ORDER BY t.max_speed_kmh DESC; +``` + +### After-hours trips (before 06:00 or after 20:00 EAT) +```sql +SELECT + d.device_name, + d.driver_name, + t.start_time AT TIME ZONE 'Africa/Nairobi' AS start_eat, + ROUND(t.distance_km::numeric, 2) AS distance_km +FROM tracksolid.trips t +JOIN tracksolid.devices d ON d.imei = t.imei +WHERE EXTRACT(HOUR FROM t.start_time AT TIME ZONE 'Africa/Nairobi') >= 20 + OR EXTRACT(HOUR FROM t.start_time AT TIME ZONE 'Africa/Nairobi') < 6 +ORDER BY t.start_time DESC; +``` + +### Fleet utilisation rate per vehicle (today) +```sql +SELECT + d.device_name, + d.driver_name, + ROUND(SUM(t.driving_time_s) / 3600.0, 2) AS drive_hours, + ROUND(SUM(t.idle_time_s) / 3600.0, 2) AS idle_hours, + ROUND(SUM(t.driving_time_s) / (10.0 * 3600) * 100, 1) AS utilisation_pct +FROM tracksolid.trips t +JOIN tracksolid.devices d ON d.imei = t.imei +WHERE t.start_time >= CURRENT_DATE AT TIME ZONE 'Africa/Nairobi' +GROUP BY d.device_name, d.driver_name +ORDER BY utilisation_pct DESC; +``` + +### Vehicles that did not move today +```sql +SELECT d.device_name, d.mc_type, d.driver_name +FROM tracksolid.devices d +LEFT JOIN tracksolid.trips t + ON t.imei = d.imei + AND t.start_time >= CURRENT_DATE AT TIME ZONE 'Africa/Nairobi' +WHERE t.imei IS NULL +ORDER BY d.device_name; +``` + +### Distance per driver — last 30 days +```sql +SELECT + d.driver_name, + COUNT(DISTINCT DATE(t.start_time AT TIME ZONE 'Africa/Nairobi')) AS active_days, + COUNT(*) AS total_trips, + ROUND(SUM(t.distance_km)::numeric, 0) AS total_km, + ROUND(AVG(t.distance_km)::numeric, 1) AS avg_km_per_trip, + MAX(t.max_speed_kmh) AS top_speed_ever +FROM tracksolid.trips t +JOIN tracksolid.devices d ON d.imei = t.imei +WHERE t.start_time > now() - interval '30 days' + AND d.driver_name IS NOT NULL AND d.driver_name != '' +GROUP BY d.driver_name +ORDER BY total_km DESC; +``` + +--- + +## 6. Alarms + +### All alarms — last 24 hours +```sql +SELECT + d.device_name, + a.alarm_type, + a.alarm_name, + a.alarm_time AT TIME ZONE 'Africa/Nairobi' AS alarm_time_eat, + ROUND(a.lat::numeric, 5) AS lat, + ROUND(a.lng::numeric, 5) AS lng, + a.speed, + a.severity, + a.acknowledged_at +FROM tracksolid.alarms a +JOIN tracksolid.devices d ON d.imei = a.imei +WHERE a.alarm_time > now() - interval '24 hours' +ORDER BY a.alarm_time DESC; +``` + +### Alarm summary by type — last 7 days +```sql +SELECT + a.alarm_name, + a.alarm_type, + COUNT(*) AS occurrences, + COUNT(DISTINCT a.imei) AS devices_affected, + MAX(a.alarm_time AT TIME ZONE 'Africa/Nairobi') AS last_seen_eat +FROM tracksolid.alarms a +WHERE a.alarm_time > now() - interval '7 days' +GROUP BY a.alarm_name, a.alarm_type +ORDER BY occurrences DESC; +``` + +### Unacknowledged alarms +```sql +SELECT + d.device_name, + a.alarm_name, + a.alarm_time AT TIME ZONE 'Africa/Nairobi' AS alarm_time_eat, + ROUND(EXTRACT(EPOCH FROM (now() - a.alarm_time)) / 3600.0, 1) AS hours_open +FROM tracksolid.alarms a +JOIN tracksolid.devices d ON d.imei = a.imei +WHERE a.acknowledged_at IS NULL +ORDER BY a.alarm_time DESC; +``` + +### Acknowledge an alarm +```sql +UPDATE tracksolid.alarms +SET acknowledged_at = now(), + acknowledged_by = 'operator_name' +WHERE id = ; +``` + +--- + +## 7. Parking & Idle Time + +### Parking events today +```sql +SELECT + d.device_name, + pe.event_type, + pe.start_time AT TIME ZONE 'Africa/Nairobi' AS start_eat, + pe.end_time AT TIME ZONE 'Africa/Nairobi' AS end_eat, + ROUND(pe.duration_seconds / 60.0, 0) AS duration_mins, + pe.address +FROM tracksolid.parking_events pe +JOIN tracksolid.devices d ON d.imei = pe.imei +WHERE pe.start_time >= CURRENT_DATE AT TIME ZONE 'Africa/Nairobi' +ORDER BY pe.start_time DESC; +``` + +### Longest idle periods — last 7 days +```sql +SELECT + d.device_name, + d.driver_name, + pe.start_time AT TIME ZONE 'Africa/Nairobi' AS idle_start, + ROUND(pe.duration_seconds / 3600.0, 2) AS idle_hours, + pe.address +FROM tracksolid.parking_events pe +JOIN tracksolid.devices d ON d.imei = pe.imei +WHERE pe.start_time > now() - interval '7 days' +ORDER BY pe.duration_seconds DESC +LIMIT 20; +``` + +--- + +## 8. Position History & Route Replay + +### Position history by source — counts +```sql +SELECT + source, + COUNT(*) AS fixes, + MIN(gps_time AT TIME ZONE 'Africa/Nairobi') AS earliest, + MAX(gps_time AT TIME ZONE 'Africa/Nairobi') AS latest +FROM tracksolid.position_history +GROUP BY source; +``` + +### Route replay for a specific vehicle — last 24 hours +```sql +-- Replace 'FRED KMGW 538W HULETI' with the device_name you want +SELECT + ph.gps_time AT TIME ZONE 'Africa/Nairobi' AS gps_time_eat, + ROUND(ph.lat::numeric, 5) AS lat, + ROUND(ph.lng::numeric, 5) AS lng, + ph.speed, + ph.direction, + ph.acc_status, + ph.source +FROM tracksolid.position_history ph +JOIN tracksolid.devices d ON d.imei = ph.imei +WHERE d.device_name = 'FRED KMGW 538W HULETI' + AND ph.gps_time > now() - interval '24 hours' +ORDER BY ph.gps_time ASC; +``` + +### Fix density per device — last 24 hours +```sql +SELECT + d.device_name, + COUNT(*) AS total_fixes, + COUNT(*) FILTER (WHERE ph.source = 'poll') AS poll_fixes, + COUNT(*) FILTER (WHERE ph.source = 'track_list') AS track_list_fixes, + MIN(ph.gps_time AT TIME ZONE 'Africa/Nairobi') AS first_fix, + MAX(ph.gps_time AT TIME ZONE 'Africa/Nairobi') AS last_fix +FROM tracksolid.position_history ph +JOIN tracksolid.devices d ON d.imei = ph.imei +WHERE ph.gps_time > now() - interval '24 hours' +GROUP BY d.device_name +ORDER BY total_fixes DESC; +``` + +--- + +## 9. Ingestion Pipeline Health + +### Pipeline health — last hour (key check) +```sql +SELECT + endpoint, + COUNT(*) AS calls, + SUM(rows_upserted) AS upserted, + SUM(rows_inserted) AS inserted, + ROUND(AVG(duration_ms)::numeric, 0) AS avg_ms, + COUNT(*) FILTER (WHERE success = false) AS failures, + MAX(run_at AT TIME ZONE 'Africa/Nairobi') AS last_call_eat +FROM tracksolid.ingestion_log +WHERE run_at > now() - interval '1 hour' +GROUP BY endpoint +ORDER BY calls DESC; +``` + +### All-time ingestion summary +```sql +SELECT + endpoint, + COUNT(*) AS total_calls, + SUM(rows_upserted) AS total_upserted, + SUM(rows_inserted) AS total_inserted, + ROUND(AVG(duration_ms)::numeric, 0) AS avg_ms, + COUNT(*) FILTER (WHERE success = false) AS failures, + MIN(run_at AT TIME ZONE 'Africa/Nairobi') AS first_call, + MAX(run_at AT TIME ZONE 'Africa/Nairobi') AS last_call +FROM tracksolid.ingestion_log +GROUP BY endpoint +ORDER BY total_calls DESC; +``` + +### Recent calls — last 20 entries +```sql +SELECT + run_at AT TIME ZONE 'Africa/Nairobi' AS run_eat, + endpoint, + imei_count, + rows_upserted, + rows_inserted, + duration_ms, + success, + error_message +FROM tracksolid.ingestion_log +ORDER BY run_at DESC +LIMIT 20; +``` + +### Failed calls — all time +```sql +SELECT + run_at AT TIME ZONE 'Africa/Nairobi' AS run_eat, + endpoint, + error_code, + error_message +FROM tracksolid.ingestion_log +WHERE success = false +ORDER BY run_at DESC; +``` + +--- + +## 10. Device & Fleet Registry + +### Full fleet — all 63 devices +```sql +SELECT + device_name, + mc_type, + vehicle_number, + driver_name, + sim, + ROUND(current_mileage_km::numeric, 0) AS odometer_km, + expiration::date AS expires, + enabled_flag +FROM tracksolid.devices +ORDER BY mc_type, device_name; +``` + +### Fleet by model +```sql +SELECT mc_type, COUNT(*) AS devices +FROM tracksolid.devices +GROUP BY mc_type ORDER BY devices DESC; +``` + +### Odometer leaders — top 15 +```sql +SELECT + device_name, + mc_type, + sim, + ROUND(current_mileage_km::numeric, 0) AS odometer_km, + expiration::date AS expires +FROM tracksolid.devices +WHERE current_mileage_km IS NOT NULL AND current_mileage_km > 0 +ORDER BY current_mileage_km DESC +LIMIT 15; +``` + +### Devices needing metadata (blank vehicle_number or driver_name) +```sql +SELECT device_name, mc_type, sim +FROM tracksolid.devices +WHERE vehicle_number IS NULL OR vehicle_number = '' + OR driver_name IS NULL OR driver_name = '' +ORDER BY mc_type, device_name; +``` + +### Subscription status breakdown +```sql +SELECT + COUNT(*) FILTER (WHERE expiration IS NULL) AS no_expiry_set, + COUNT(*) FILTER (WHERE expiration < now()) AS already_expired, + COUNT(*) FILTER (WHERE expiration BETWEEN now() AND now() + interval '90 days') AS expiring_90days, + COUNT(*) FILTER (WHERE expiration > now() + interval '90 days') AS valid_long_term +FROM tracksolid.devices; +``` + +### Update vehicle metadata for a device +```sql +UPDATE tracksolid.devices +SET vehicle_number = 'KDA 123B', + driver_name = 'John Kamau', + vehicle_category = 'van' +WHERE device_name = 'FRED KMGW 538W HULETI'; +``` + +--- + +## 11. Schema & Migrations + +### Check applied migrations +```sql +SELECT filename, applied_at AT TIME ZONE 'Africa/Nairobi' AS applied_eat +FROM tracksolid.schema_migrations +ORDER BY applied_at; +``` + +### List all tables in tracksolid schema +```sql +SELECT table_name FROM information_schema.tables +WHERE table_schema = 'tracksolid' +ORDER BY table_name; +``` + +### Row counts across all key tables +```sql +SELECT + (SELECT COUNT(*) FROM tracksolid.devices) AS devices, + (SELECT COUNT(*) FROM tracksolid.live_positions) AS live_positions, + (SELECT COUNT(*) FROM tracksolid.position_history) AS position_history, + (SELECT COUNT(*) FROM tracksolid.trips) AS trips, + (SELECT COUNT(*) FROM tracksolid.alarms) AS alarms, + (SELECT COUNT(*) FROM tracksolid.parking_events) AS parking_events, + (SELECT COUNT(*) FROM tracksolid.obd_readings) AS obd_readings, + (SELECT COUNT(*) FROM tracksolid.device_events) AS device_events, + (SELECT COUNT(*) FROM tracksolid.fuel_readings) AS fuel_readings, + (SELECT COUNT(*) FROM tracksolid.temperature_readings) AS temperature_readings, + (SELECT COUNT(*) FROM tracksolid.lbs_readings) AS lbs_readings, + (SELECT COUNT(*) FROM tracksolid.ingestion_log) AS ingestion_log; +``` + +### Describe a table +```bash +# On the server +tsdb -c "\d tracksolid.trips" +tsdb -c "\d tracksolid.live_positions" +``` + +### Run DWH gold ETL for yesterday +```sql +SELECT dwh_gold.refresh_daily_metrics(CURRENT_DATE - 1); +``` + +### Apply a migration manually (from your Mac) +```bash +ssh kianiadee@rahamafresh.com \ + 'TS=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) && + docker exec -i "$TS" psql -U postgres -d tracksolid_db' \ + < 05_enhancement_migration.sql +``` + +--- + +## 12. Container & Service Operations + +### Find the current TimescaleDB container name +```bash +ssh kianiadee@rahamafresh.com \ + 'docker ps --filter "name=timescale_db" --format "{{.Names}}"' +``` + +### List all running containers +```bash +ssh kianiadee@rahamafresh.com 'docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"' +``` + +### Check container logs (last 100 lines) +```bash +# TimescaleDB +ssh kianiadee@rahamafresh.com \ + 'TS=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) && + docker logs "$TS" --tail 100' + +# Ingest movement service +ssh kianiadee@rahamafresh.com \ + 'SVC=$(docker ps --filter "name=ingest_movement" --format "{{.Names}}" | head -1) && + docker logs "$SVC" --tail 100' + +# Ingest events service +ssh kianiadee@rahamafresh.com \ + 'SVC=$(docker ps --filter "name=ingest_events" --format "{{.Names}}" | head -1) && + docker logs "$SVC" --tail 100' + +# Webhook receiver +ssh kianiadee@rahamafresh.com \ + 'SVC=$(docker ps --filter "name=webhook_receiver" --format "{{.Names}}" | head -1) && + docker logs "$SVC" --tail 100' +``` + +### Follow logs live (stream) +```bash +ssh kianiadee@rahamafresh.com \ + 'SVC=$(docker ps --filter "name=ingest_movement" --format "{{.Names}}" | head -1) && + docker logs "$SVC" --follow --tail 50' +``` + +### Restart a service +```bash +# Replace with: ingest_movement, ingest_events, webhook_receiver +ssh kianiadee@rahamafresh.com \ + 'SVC=$(docker ps --filter "name=ingest_movement" --format "{{.Names}}" | head -1) && + docker restart "$SVC"' +``` + +### Check disk space on the server +```bash +ssh kianiadee@rahamafresh.com 'df -h /' +``` + +### Check TimescaleDB chunk sizes (storage usage) +```sql +SELECT + hypertable_name, + pg_size_pretty(SUM(total_bytes)) AS total_size +FROM timescaledb_information.chunks +GROUP BY hypertable_name +ORDER BY SUM(total_bytes) DESC; +``` + +### Vacuum / analyse a table (maintenance) +```bash +ssh kianiadee@rahamafresh.com \ + 'TS=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) && + docker exec -i "$TS" psql -U postgres -d tracksolid_db -c " + VACUUM ANALYSE tracksolid.position_history;"' +``` + +--- + +## 13. Quick Reference — Flag Meanings + +| Flag | Context | Meaning | +|---|---|---| +| `--filter "name=X"` | `docker ps` | Match containers whose name contains `X` | +| `--format "{{.Names}}"` | `docker ps` | Output only the container name column | +| `head -1` | shell | Take only the first result (guard against duplicates) | +| `exec -i` | `docker exec` | Keep stdin open — required for piping SQL or running non-interactively | +| `exec -it` | `docker exec` | Add a TTY — use only for interactive sessions, not when piping | +| `-U postgres` | `psql` | Connect as the `postgres` superuser | +| `-d tracksolid_db` | `psql` | Target this database | +| `-c "..."` | `psql` | Run a single SQL statement and exit | +| `-f file.sql` | `psql` | Execute all SQL in a file | +| `AT TIME ZONE 'Africa/Nairobi'` | SQL | Convert UTC timestamp to EAT (UTC+3) for display | + +--- + +*Last updated: 2026-04-11*