tracksolid_timescale_grafan.../**02_tracksolid_docker_commands.md
David Kiania 34f5fa1b9c feat(dwh): bronze pipeline migrations, runbook, and execution manual
DWH pipeline (new):
  - dwh/261001_dwh_control.sql — watermarks + per-run audit log schema
  - dwh/261002_bronze_constraints_audit.sql — ON CONFLICT key assertion
  - dwh/261003_dwh_roles.sql — dwh_owner / grafana_ro contract assertion
  - dwh/261004_dwh_observability_views.sql — v_table_freshness,
    v_recent_failures, v_watermark_lag (readable by grafana_ro)
  - docs/DWH_PIPELINE.md — operations runbook (setup, troubleshooting,
    manual re-run, back-fill, rotation)
  - DWH_Execution_Manual.md — reusable playbook for future data
    projects (extract → blob → load pattern, 7 design principles,
    snapshot-vs-incremental matrix, verification gates)
  - docs/superpowers/{specs,plans}/2026-04-24-n8n-dwh-bronze-pipeline-*
    — design spec + 27-task implementation plan

Security:
  - dwh/260423_dwh_ddl_v1.sql — redacted plaintext role passwords to
    'CHANGE_ME_BEFORE_APPLY' placeholders; added SECURITY header
    documenting generation + rotation flow

Docs:
  - CLAUDE.md — §3 adds tracksolid_dwh@31.97.44.246:5888 target,
    §4 adds dwh/ + docs/DWH_PIPELINE.md to codebase map, §5 adds
    bronze + dwh_control schema roll-up, §10 adds deploy task +
    password rotation follow-up

Also includes miscellaneous in-progress files accumulated on this
branch (workspace, analytics notes, vehicle CSVs, extract helpers,
renamed markdown archives).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 01:07:53 +03:00

22 KiB

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
  2. Setup — Shell Function on the Server
  3. Connecting From Your Mac
  4. Fleet & Live Positions
  5. Trips & Movement
  6. Alarms
  7. Parking & Idle Time
  8. Position History & Route Replay
  9. Ingestion Pipeline Health
  10. Device & Fleet Registry
  11. Schema & Migrations
  12. Container & Service Operations
  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:

TS_DB=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1)

Layer 2 — run psql inside it.

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.

# 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:

source ~/.zshrc

Usage on the server:

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

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

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

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

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

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

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)

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)

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

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

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

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

-- 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)

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)

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

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

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

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

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

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

UPDATE tracksolid.alarms
SET acknowledged_at = now(),
    acknowledged_by = 'operator_name'
WHERE id = <alarm_id>;

7. Parking & Idle Time

Parking events today

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

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

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

-- 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;
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)

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

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

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

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

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

SELECT mc_type, COUNT(*) AS devices
FROM tracksolid.devices
GROUP BY mc_type ORDER BY devices DESC;

Odometer leaders — top 15

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)

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

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

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

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

SELECT table_name FROM information_schema.tables
WHERE table_schema = 'tracksolid'
ORDER BY table_name;

Row counts across all key tables

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

# On the server
tsdb -c "\d tracksolid.trips"
tsdb -c "\d tracksolid.live_positions"

Run DWH gold ETL for yesterday

SELECT dwh_gold.refresh_daily_metrics(CURRENT_DATE - 1);

Apply a migration manually (from your Mac)

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

ssh kianiadee@rahamafresh.com \
  'docker ps --filter "name=timescale_db" --format "{{.Names}}"'

List all running containers

ssh kianiadee@rahamafresh.com 'docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"'

Check container logs (last 100 lines)

# 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)

ssh kianiadee@rahamafresh.com \
  'SVC=$(docker ps --filter "name=ingest_movement" --format "{{.Names}}" | head -1) &&
   docker logs "$SVC" --follow --tail 50'

Restart a service

# 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

ssh kianiadee@rahamafresh.com 'df -h /'

Check TimescaleDB chunk sizes (storage usage)

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)

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