tracksolid_timescale_grafan.../02_tracksolid_docker_commands.md
David Kiania 1f11a65b0b Add 02_tracksolid_docker_commands.md — remote DB command reference
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 <noreply@anthropic.com>
2026-04-11 08:23:01 +03:00

733 lines
22 KiB
Markdown

# 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 = <alarm_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 <service_name_prefix> 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*