feat: read-only Fleet Analytics MCP server
Standalone, hosted MCP server that lets the decision & analytics team query
the fleet database (reporting.* / tracksolid.*) from Claude — read-only, for
reporting and decisions, never edit/delete.
- analytics_mcp.py: FastMCP streamable-HTTP server. Tools: query (guarded
single SELECT/WITH, auto-LIMIT, write/DDL blocked), list_schemas,
list_tables, describe_table, list_functions, sample_table. Per-analyst
Bearer auth; /healthz exempt. No ts_shared_rev import (carries no ingestion
secrets).
- Read-only enforced at four layers: analytics_ro GRANTs,
default_transaction_read_only=on, rolled-back txn, SQL keyword guard.
- scripts/: analytics_ro_role.sql + bootstrap_analytics_ro.sh (dedicated
least-privilege role, password in host-only ~/.analytics_ro.pw).
- Dockerfile + pyproject (uv, package=false) for Coolify build; deploy.sh
manual host fallback (standalone Traefik bridge on the tracksolid_db host).
- docs/ANALYTICS_MCP.{md,html} + README: architecture, deploy runbook,
add-to-Claude, verification, security notes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 20:43:24 +00:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# deploy.sh — manual host deploy for the read-only Fleet Analytics MCP server.
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
# Use this if you are NOT letting Coolify build the Dockerfile (see README §Deploy
|
|
|
|
|
# for the Coolify-managed path, which is the recommended default). This script
|
|
|
|
|
# builds the image from this repo ON THE HOST and runs it as a standalone
|
|
|
|
|
# Traefik-labelled bridge — the same proven pattern as the dashboard_api bridges:
|
|
|
|
|
# it joins the network that can reach timescale_db, derives a READ-ONLY DATABASE_URL
|
|
|
|
|
# for the analytics_ro role, and exposes the MCP over HTTPS with Bearer auth.
|
|
|
|
|
#
|
|
|
|
|
# Prereqs ON THE HOST:
|
|
|
|
|
# * the analytics_ro role exists -> scripts/bootstrap_analytics_ro.sh (writes ~/.analytics_ro.pw)
|
|
|
|
|
# * this repo is checked out (e.g. ~/fleetanalytics_mcp) — run this script from inside it
|
|
|
|
|
#
|
|
|
|
|
# Run:
|
|
|
|
|
# cd ~/fleetanalytics_mcp && git pull
|
|
|
|
|
# MCP_AUTH_TOKENS="alice:$(openssl rand -hex 16)" bash deploy.sh
|
|
|
|
|
#
|
|
|
|
|
# A token/env change needs a container RECREATE — this script does that. Record
|
|
|
|
|
# each analyst's token securely; it is only shown once (when you generate it).
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
NAME=analytics_mcp
|
|
|
|
|
PORT=8892
|
|
|
|
|
HOST_DOMAIN="${HOST_DOMAIN:-fleetmcp.fivetitude.com}" # prod: fleetmcp.rahamafresh.com
|
2026-06-23 18:35:31 +00:00
|
|
|
# Comma-separated list of every domain this service answers on (defaults to
|
|
|
|
|
# HOST_DOMAIN). All are folded into ONE Traefik router rule so a single cert
|
|
|
|
|
# covers them and connectors on either domain keep working.
|
|
|
|
|
HOST_DOMAINS="${HOST_DOMAINS:-$HOST_DOMAIN}"
|
|
|
|
|
BT='`'
|
|
|
|
|
RULE=""
|
|
|
|
|
IFS=',' read -ra _DOMS <<< "$HOST_DOMAINS"
|
|
|
|
|
for _d in "${_DOMS[@]}"; do
|
|
|
|
|
_d="${_d// /}"
|
|
|
|
|
if [ -n "$_d" ]; then
|
|
|
|
|
seg="Host(${BT}${_d}${BT})"
|
|
|
|
|
if [ -z "$RULE" ]; then RULE="$seg"; else RULE="$RULE || $seg"; fi
|
|
|
|
|
fi
|
|
|
|
|
done
|
feat: read-only Fleet Analytics MCP server
Standalone, hosted MCP server that lets the decision & analytics team query
the fleet database (reporting.* / tracksolid.*) from Claude — read-only, for
reporting and decisions, never edit/delete.
- analytics_mcp.py: FastMCP streamable-HTTP server. Tools: query (guarded
single SELECT/WITH, auto-LIMIT, write/DDL blocked), list_schemas,
list_tables, describe_table, list_functions, sample_table. Per-analyst
Bearer auth; /healthz exempt. No ts_shared_rev import (carries no ingestion
secrets).
- Read-only enforced at four layers: analytics_ro GRANTs,
default_transaction_read_only=on, rolled-back txn, SQL keyword guard.
- scripts/: analytics_ro_role.sql + bootstrap_analytics_ro.sh (dedicated
least-privilege role, password in host-only ~/.analytics_ro.pw).
- Dockerfile + pyproject (uv, package=false) for Coolify build; deploy.sh
manual host fallback (standalone Traefik bridge on the tracksolid_db host).
- docs/ANALYTICS_MCP.{md,html} + README: architecture, deploy runbook,
add-to-Claude, verification, security notes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 20:43:24 +00:00
|
|
|
IMAGE="fleetanalytics-mcp:latest"
|
|
|
|
|
ENV_FILE="$(pwd)/.deploy.env"
|
2026-06-17 08:37:25 +00:00
|
|
|
|
|
|
|
|
# Per-analyst Bearer tokens. For a CODE-ONLY redeploy you can omit MCP_AUTH_TOKENS:
|
|
|
|
|
# we reuse the tokens from the currently running container so existing analysts keep
|
|
|
|
|
# working and no secret has to be re-typed or printed. Only set MCP_AUTH_TOKENS when
|
|
|
|
|
# you are adding/rotating/revoking a token.
|
|
|
|
|
if [ -z "${MCP_AUTH_TOKENS:-}" ]; then
|
|
|
|
|
MCP_AUTH_TOKENS="$(docker inspect "$NAME" --format '{{range .Config.Env}}{{println .}}{{end}}' 2>/dev/null | sed -n 's/^MCP_AUTH_TOKENS=//p' | head -1)"
|
|
|
|
|
[ -n "$MCP_AUTH_TOKENS" ] && echo "Reusing existing analyst tokens from running $NAME container."
|
|
|
|
|
fi
|
feat: read-only Fleet Analytics MCP server
Standalone, hosted MCP server that lets the decision & analytics team query
the fleet database (reporting.* / tracksolid.*) from Claude — read-only, for
reporting and decisions, never edit/delete.
- analytics_mcp.py: FastMCP streamable-HTTP server. Tools: query (guarded
single SELECT/WITH, auto-LIMIT, write/DDL blocked), list_schemas,
list_tables, describe_table, list_functions, sample_table. Per-analyst
Bearer auth; /healthz exempt. No ts_shared_rev import (carries no ingestion
secrets).
- Read-only enforced at four layers: analytics_ro GRANTs,
default_transaction_read_only=on, rolled-back txn, SQL keyword guard.
- scripts/: analytics_ro_role.sql + bootstrap_analytics_ro.sh (dedicated
least-privilege role, password in host-only ~/.analytics_ro.pw).
- Dockerfile + pyproject (uv, package=false) for Coolify build; deploy.sh
manual host fallback (standalone Traefik bridge on the tracksolid_db host).
- docs/ANALYTICS_MCP.{md,html} + README: architecture, deploy runbook,
add-to-Claude, verification, security notes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 20:43:24 +00:00
|
|
|
: "${MCP_AUTH_TOKENS:?set MCP_AUTH_TOKENS=name:token[,name:token...] before running (per-analyst Bearer tokens)}"
|
|
|
|
|
|
|
|
|
|
# Resolve the network + DB DSN from the running webhook_receiver (it sits on the
|
|
|
|
|
# same internal network as timescale_db and holds a DATABASE_URL we can reuse the
|
|
|
|
|
# host:port/dbname from). This avoids hardcoding the internal DB hostname.
|
|
|
|
|
WH=$(docker ps --filter name=webhook_receiver --format "{{.Names}}" | head -1)
|
|
|
|
|
[ -n "$WH" ] || { echo "ERROR: webhook_receiver container not found (need the tracksolid stack running)"; exit 1; }
|
|
|
|
|
APPNET=$(docker inspect "$WH" --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}}{{end}}')
|
|
|
|
|
SRC_DB_URL=$(docker inspect "$WH" --format '{{range .Config.Env}}{{println .}}{{end}}' | sed -n 's/^DATABASE_URL=//p' | head -1)
|
|
|
|
|
[ -n "$SRC_DB_URL" ] || { echo "ERROR: DATABASE_URL not found in $WH env"; exit 1; }
|
|
|
|
|
echo "Reusing network $APPNET (from $WH)"
|
|
|
|
|
|
|
|
|
|
# Build a READ-ONLY DATABASE_URL: app DB host:port/dbname + analytics_ro creds.
|
|
|
|
|
RO_PW=$(cat "${ANALYTICS_RO_PW_FILE:-$HOME/.analytics_ro.pw}" 2>/dev/null || true)
|
|
|
|
|
[ -n "$RO_PW" ] || { echo "ERROR: ~/.analytics_ro.pw missing — run scripts/bootstrap_analytics_ro.sh first"; exit 1; }
|
|
|
|
|
HOSTPART="${SRC_DB_URL#*@}" # host:port/dbname[?params]
|
|
|
|
|
RO_DB_URL="postgresql://analytics_ro:${RO_PW}@${HOSTPART}"
|
|
|
|
|
|
2026-06-23 18:35:31 +00:00
|
|
|
# Build the image from this repo (SKIP_BUILD=1 reuses the existing image for a
|
|
|
|
|
# labels/env-only change — no new code is pulled in).
|
|
|
|
|
if [ "${SKIP_BUILD:-0}" = "1" ]; then
|
|
|
|
|
echo "SKIP_BUILD=1 — reusing existing $IMAGE (no rebuild)."
|
|
|
|
|
docker image inspect "$IMAGE" >/dev/null 2>&1 || { echo "ERROR: $IMAGE not present"; exit 1; }
|
|
|
|
|
else
|
|
|
|
|
echo "Building $IMAGE ..."
|
|
|
|
|
docker build -t "$IMAGE" .
|
|
|
|
|
fi
|
feat: read-only Fleet Analytics MCP server
Standalone, hosted MCP server that lets the decision & analytics team query
the fleet database (reporting.* / tracksolid.*) from Claude — read-only, for
reporting and decisions, never edit/delete.
- analytics_mcp.py: FastMCP streamable-HTTP server. Tools: query (guarded
single SELECT/WITH, auto-LIMIT, write/DDL blocked), list_schemas,
list_tables, describe_table, list_functions, sample_table. Per-analyst
Bearer auth; /healthz exempt. No ts_shared_rev import (carries no ingestion
secrets).
- Read-only enforced at four layers: analytics_ro GRANTs,
default_transaction_read_only=on, rolled-back txn, SQL keyword guard.
- scripts/: analytics_ro_role.sql + bootstrap_analytics_ro.sh (dedicated
least-privilege role, password in host-only ~/.analytics_ro.pw).
- Dockerfile + pyproject (uv, package=false) for Coolify build; deploy.sh
manual host fallback (standalone Traefik bridge on the tracksolid_db host).
- docs/ANALYTICS_MCP.{md,html} + README: architecture, deploy runbook,
add-to-Claude, verification, security notes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 20:43:24 +00:00
|
|
|
|
|
|
|
|
# Minimal env (read-only DSN + auth only — no Tracksolid ingestion secrets).
|
|
|
|
|
{ echo "DATABASE_URL=${RO_DB_URL}"; echo "MCP_AUTH_TOKENS=${MCP_AUTH_TOKENS}"; } > "$ENV_FILE"
|
|
|
|
|
chmod 600 "$ENV_FILE"
|
|
|
|
|
|
|
|
|
|
docker rm -f "$NAME" 2>/dev/null || true
|
|
|
|
|
docker run -d --name "$NAME" --restart unless-stopped \
|
|
|
|
|
--network "$APPNET" \
|
|
|
|
|
--env-file "$ENV_FILE" \
|
fix: harden MCP server reliability, build reproducibility, and auth
Addresses intermittent query failures on the live instance (container itself
is healthy — failures are application/query-level) plus security hardening.
Reliability (analytics_mcp.py):
- Discard dead pooled connections instead of recycling them. A broken socket
(DB restart, network blip, crash) previously poisoned the pool and every
later query handed that connection failed until container recreation. New
_is_disconnect() classifies real connection loss (class-08 / 57P0x SQLSTATE,
or socket-level OperationalError with pgcode=None) vs. an in-session query
error like statement_timeout (QueryCanceled / 57014), which is NOT a
disconnect and leaves the connection usable.
- query() retries ONCE, only on a genuine disconnect, so a recycled-but-stale
connection is invisible to the analyst (a real query error still surfaces).
- Bound concurrent checkouts with a semaphore (POOL_MAX) so >POOL_MAX
concurrent queries QUEUE instead of overflowing the pool and raising
PoolError (a 500 to the analyst).
- Lazy pool (minconn=0) + retry on init, so a brief DB outage at deploy time
no longer crash-loops the worker.
Build reproducibility:
- Commit uv.lock (was gitignored) and build with `uv sync --frozen` so
redeploys can't silently pull a newer, behaviour-changed mcp/starlette.
Security:
- Constant-time Bearer-token comparison (hmac.compare_digest).
- /healthz no longer leaks the analyst/token count.
- Dockerfile runs as a non-root user.
- deploy.sh: Docker log rotation (bound disk) + Traefik rate-limit middleware.
Also: relax the SQL guard so a forbidden keyword inside a string literal (e.g.
WHERE summary ILIKE '%please delete%') no longer rejects a valid read; the
blocklist still rejects data-modifying CTEs (and writes are impossible anyway
via the analytics_ro GRANTs + read-only rolled-back txn). Fix stale docstrings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 20:28:58 +00:00
|
|
|
--log-opt max-size=10m --log-opt max-file=5 \
|
feat: read-only Fleet Analytics MCP server
Standalone, hosted MCP server that lets the decision & analytics team query
the fleet database (reporting.* / tracksolid.*) from Claude — read-only, for
reporting and decisions, never edit/delete.
- analytics_mcp.py: FastMCP streamable-HTTP server. Tools: query (guarded
single SELECT/WITH, auto-LIMIT, write/DDL blocked), list_schemas,
list_tables, describe_table, list_functions, sample_table. Per-analyst
Bearer auth; /healthz exempt. No ts_shared_rev import (carries no ingestion
secrets).
- Read-only enforced at four layers: analytics_ro GRANTs,
default_transaction_read_only=on, rolled-back txn, SQL keyword guard.
- scripts/: analytics_ro_role.sql + bootstrap_analytics_ro.sh (dedicated
least-privilege role, password in host-only ~/.analytics_ro.pw).
- Dockerfile + pyproject (uv, package=false) for Coolify build; deploy.sh
manual host fallback (standalone Traefik bridge on the tracksolid_db host).
- docs/ANALYTICS_MCP.{md,html} + README: architecture, deploy runbook,
add-to-Claude, verification, security notes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 20:43:24 +00:00
|
|
|
--label 'traefik.enable=true' \
|
|
|
|
|
--label 'traefik.docker.network=coolify' \
|
|
|
|
|
--label 'traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https' \
|
fix: harden MCP server reliability, build reproducibility, and auth
Addresses intermittent query failures on the live instance (container itself
is healthy — failures are application/query-level) plus security hardening.
Reliability (analytics_mcp.py):
- Discard dead pooled connections instead of recycling them. A broken socket
(DB restart, network blip, crash) previously poisoned the pool and every
later query handed that connection failed until container recreation. New
_is_disconnect() classifies real connection loss (class-08 / 57P0x SQLSTATE,
or socket-level OperationalError with pgcode=None) vs. an in-session query
error like statement_timeout (QueryCanceled / 57014), which is NOT a
disconnect and leaves the connection usable.
- query() retries ONCE, only on a genuine disconnect, so a recycled-but-stale
connection is invisible to the analyst (a real query error still surfaces).
- Bound concurrent checkouts with a semaphore (POOL_MAX) so >POOL_MAX
concurrent queries QUEUE instead of overflowing the pool and raising
PoolError (a 500 to the analyst).
- Lazy pool (minconn=0) + retry on init, so a brief DB outage at deploy time
no longer crash-loops the worker.
Build reproducibility:
- Commit uv.lock (was gitignored) and build with `uv sync --frozen` so
redeploys can't silently pull a newer, behaviour-changed mcp/starlette.
Security:
- Constant-time Bearer-token comparison (hmac.compare_digest).
- /healthz no longer leaks the analyst/token count.
- Dockerfile runs as a non-root user.
- deploy.sh: Docker log rotation (bound disk) + Traefik rate-limit middleware.
Also: relax the SQL guard so a forbidden keyword inside a string literal (e.g.
WHERE summary ILIKE '%please delete%') no longer rejects a valid read; the
blocklist still rejects data-modifying CTEs (and writes are impossible anyway
via the analytics_ro GRANTs + read-only rolled-back txn). Fix stale docstrings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 20:28:58 +00:00
|
|
|
--label 'traefik.http.middlewares.fleetmcp-ratelimit.ratelimit.average=30' \
|
|
|
|
|
--label 'traefik.http.middlewares.fleetmcp-ratelimit.ratelimit.burst=60' \
|
feat: read-only Fleet Analytics MCP server
Standalone, hosted MCP server that lets the decision & analytics team query
the fleet database (reporting.* / tracksolid.*) from Claude — read-only, for
reporting and decisions, never edit/delete.
- analytics_mcp.py: FastMCP streamable-HTTP server. Tools: query (guarded
single SELECT/WITH, auto-LIMIT, write/DDL blocked), list_schemas,
list_tables, describe_table, list_functions, sample_table. Per-analyst
Bearer auth; /healthz exempt. No ts_shared_rev import (carries no ingestion
secrets).
- Read-only enforced at four layers: analytics_ro GRANTs,
default_transaction_read_only=on, rolled-back txn, SQL keyword guard.
- scripts/: analytics_ro_role.sql + bootstrap_analytics_ro.sh (dedicated
least-privilege role, password in host-only ~/.analytics_ro.pw).
- Dockerfile + pyproject (uv, package=false) for Coolify build; deploy.sh
manual host fallback (standalone Traefik bridge on the tracksolid_db host).
- docs/ANALYTICS_MCP.{md,html} + README: architecture, deploy runbook,
add-to-Claude, verification, security notes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 20:43:24 +00:00
|
|
|
--label "traefik.http.routers.http-0-fleetmcp.entryPoints=http" \
|
|
|
|
|
--label "traefik.http.routers.http-0-fleetmcp.middlewares=redirect-to-https" \
|
2026-06-23 18:35:31 +00:00
|
|
|
--label "traefik.http.routers.http-0-fleetmcp.rule=${RULE}" \
|
feat: read-only Fleet Analytics MCP server
Standalone, hosted MCP server that lets the decision & analytics team query
the fleet database (reporting.* / tracksolid.*) from Claude — read-only, for
reporting and decisions, never edit/delete.
- analytics_mcp.py: FastMCP streamable-HTTP server. Tools: query (guarded
single SELECT/WITH, auto-LIMIT, write/DDL blocked), list_schemas,
list_tables, describe_table, list_functions, sample_table. Per-analyst
Bearer auth; /healthz exempt. No ts_shared_rev import (carries no ingestion
secrets).
- Read-only enforced at four layers: analytics_ro GRANTs,
default_transaction_read_only=on, rolled-back txn, SQL keyword guard.
- scripts/: analytics_ro_role.sql + bootstrap_analytics_ro.sh (dedicated
least-privilege role, password in host-only ~/.analytics_ro.pw).
- Dockerfile + pyproject (uv, package=false) for Coolify build; deploy.sh
manual host fallback (standalone Traefik bridge on the tracksolid_db host).
- docs/ANALYTICS_MCP.{md,html} + README: architecture, deploy runbook,
add-to-Claude, verification, security notes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 20:43:24 +00:00
|
|
|
--label "traefik.http.routers.https-0-fleetmcp.entryPoints=https" \
|
2026-06-23 18:35:31 +00:00
|
|
|
--label "traefik.http.routers.https-0-fleetmcp.rule=${RULE}" \
|
fix: harden MCP server reliability, build reproducibility, and auth
Addresses intermittent query failures on the live instance (container itself
is healthy — failures are application/query-level) plus security hardening.
Reliability (analytics_mcp.py):
- Discard dead pooled connections instead of recycling them. A broken socket
(DB restart, network blip, crash) previously poisoned the pool and every
later query handed that connection failed until container recreation. New
_is_disconnect() classifies real connection loss (class-08 / 57P0x SQLSTATE,
or socket-level OperationalError with pgcode=None) vs. an in-session query
error like statement_timeout (QueryCanceled / 57014), which is NOT a
disconnect and leaves the connection usable.
- query() retries ONCE, only on a genuine disconnect, so a recycled-but-stale
connection is invisible to the analyst (a real query error still surfaces).
- Bound concurrent checkouts with a semaphore (POOL_MAX) so >POOL_MAX
concurrent queries QUEUE instead of overflowing the pool and raising
PoolError (a 500 to the analyst).
- Lazy pool (minconn=0) + retry on init, so a brief DB outage at deploy time
no longer crash-loops the worker.
Build reproducibility:
- Commit uv.lock (was gitignored) and build with `uv sync --frozen` so
redeploys can't silently pull a newer, behaviour-changed mcp/starlette.
Security:
- Constant-time Bearer-token comparison (hmac.compare_digest).
- /healthz no longer leaks the analyst/token count.
- Dockerfile runs as a non-root user.
- deploy.sh: Docker log rotation (bound disk) + Traefik rate-limit middleware.
Also: relax the SQL guard so a forbidden keyword inside a string literal (e.g.
WHERE summary ILIKE '%please delete%') no longer rejects a valid read; the
blocklist still rejects data-modifying CTEs (and writes are impossible anyway
via the analytics_ro GRANTs + read-only rolled-back txn). Fix stale docstrings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 20:28:58 +00:00
|
|
|
--label "traefik.http.routers.https-0-fleetmcp.middlewares=fleetmcp-ratelimit" \
|
feat: read-only Fleet Analytics MCP server
Standalone, hosted MCP server that lets the decision & analytics team query
the fleet database (reporting.* / tracksolid.*) from Claude — read-only, for
reporting and decisions, never edit/delete.
- analytics_mcp.py: FastMCP streamable-HTTP server. Tools: query (guarded
single SELECT/WITH, auto-LIMIT, write/DDL blocked), list_schemas,
list_tables, describe_table, list_functions, sample_table. Per-analyst
Bearer auth; /healthz exempt. No ts_shared_rev import (carries no ingestion
secrets).
- Read-only enforced at four layers: analytics_ro GRANTs,
default_transaction_read_only=on, rolled-back txn, SQL keyword guard.
- scripts/: analytics_ro_role.sql + bootstrap_analytics_ro.sh (dedicated
least-privilege role, password in host-only ~/.analytics_ro.pw).
- Dockerfile + pyproject (uv, package=false) for Coolify build; deploy.sh
manual host fallback (standalone Traefik bridge on the tracksolid_db host).
- docs/ANALYTICS_MCP.{md,html} + README: architecture, deploy runbook,
add-to-Claude, verification, security notes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 20:43:24 +00:00
|
|
|
--label "traefik.http.routers.https-0-fleetmcp.tls=true" \
|
|
|
|
|
--label "traefik.http.routers.https-0-fleetmcp.tls.certresolver=letsencrypt" \
|
|
|
|
|
--label "traefik.http.services.fleetmcp.loadbalancer.server.port=${PORT}" \
|
|
|
|
|
"$IMAGE"
|
|
|
|
|
|
|
|
|
|
docker network connect coolify "$NAME" 2>/dev/null || true
|
|
|
|
|
rm -f "$ENV_FILE"
|
|
|
|
|
sleep 5
|
|
|
|
|
echo "== container =="; docker ps --filter name="$NAME" --format "{{.Names}} | {{.Status}}"
|
|
|
|
|
echo "== DB role (expect analytics_ro) =="; docker exec "$NAME" sh -lc 'printenv DATABASE_URL | sed -E "s#://([^:]+):[^@]+@#://\1:<pw>@#"'
|
2026-06-16 21:00:40 +00:00
|
|
|
echo "== health =="; docker exec "$NAME" python -c "import urllib.request; print(urllib.request.urlopen('http://localhost:${PORT}/healthz').read().decode())" 2>&1 | head
|