Skip to content

Cost & resource controls

Where TeslaSync's spend and disk growth are bounded, and what happens when a bound is removed. Referenced by the cost dimension of the production readiness scorecard (ops/scorecard/dimensions.yaml).

Self-hosting shifts cost from a subscription to compute, storage, and — for the AI features — per-token API spend. Compute and request spend ship with active ceilings. Destructive telemetry retention requires an explicit operator acknowledgement after backup verification.

Compute

ControlWhereDefault
CPU/memory requests + limitshelm/teslasync/values.yaml resources, web.resources, per-worker resourcesSet for every workload
Compose memory limitsdocker-compose.yml deploy.resourcesSet for every service
Autoscaling boundshelm/teslasync/values.yaml autoscaling.minReplicas / maxReplicasDisabled; explicit min/max when enabled
Canary replicasrollout.<component>.canary.replicaCount1, and canary is disabled by default

Autoscaling is off by default. When it is enabled, maxReplicas is the only thing between a traffic anomaly and an unbounded bill — treat raising it as a cost decision, not a capacity decision.

Storage

Fleet telemetry is the dominant cost driver: signal_log is a hypertable receiving every changed field for every vehicle, forever, unless bounded.

ControlEnvironment variableShipped defaultPurpose
Signal history windowSIGNAL_HISTORY_RETENTION_DAYS365 daysSelects the raw-history window for signal_log and transport evidence
Signal history acknowledgementSIGNAL_HISTORY_RETENTION_ACKNOWLEDGEDfalseActivates destructive chunk removal only after backup verification
Position retentionPOSITION_RETENTION_DAYSDisabledTrims the legacy positions table, which receives no canonical telemetry writes
General data retentionDATA_RETENTION_DAYSDisabledCompatibility setting for the removed vehicle-state history; it does not bound active telemetry
MongoDB TTLMONGODB_TTL_DAYS30 daysBounds the optional document store

Go, Docker Compose, and Helm all ship the same 365-day history window and the same safe false acknowledgement default. This prevents an upgrade from silently deleting telemetry from an existing installation. After an operator sets SIGNAL_HISTORY_RETENTION_ACKNOWLEDGED=true, TeslaSync drops complete TimescaleDB chunks on a daily schedule for both signal_log and signal_transport_evidence. Initial convergence advances through no more than eight seven-day chunk windows per run.

ops/retention/policy.yaml is the machine-readable contract; the retention ops gate checks both defaults on every configuration surface, the bounded chunk cleanup, the acknowledgement guard, and the recurring scheduler:

bash
go run ./cmd/ops-gate -check retention

Before enabling cleanup:

  1. Create a database backup inside the declared RPO window and complete its restore validation.
  2. Confirm the selected history window preserves the evidence needed by operations, exports, and analytics.
  3. Set SIGNAL_HISTORY_RETENTION_ACKNOWLEDGED=true and observe chunks_dropped and backlog_remaining in the cleanup logs.

Setting SIGNAL_HISTORY_RETENTION_DAYS=0 disables cleanup even when it was previously acknowledged. Leaving acknowledgement false preserves all history and emits a startup warning so the unbounded storage posture is visible rather than silently destructive.

Two things to know before changing them:

  1. Continuous aggregates keep their own copies. cagg_fleet_stats and cagg_battery_daily survive raw-row trimming, so shortening raw retention loses point-in-time reconstruction but not long-range trends.
  2. Retention is destructive. Trimmed telemetry cannot be backfilled — Tesla does not re-deliver history. Shorten a retention window only after confirming what depends on it.

Tesla Fleet API spend

Fleet API polling, wake-ups, commands, and selected partner endpoints are metered by Tesla. TeslaSync reserves an estimated price before each call against one atomic UTC-day PostgreSQL row shared by the API server, automation worker, resubscribe tool, and replicas.

The shipped $0.30/day ceiling protects $0.05 for commands so background polling cannot consume the entire allowance. The System Budgets page exposes both rows. The guard fails closed when its database evidence is unavailable. See docs/operations/fleet-api-budget.md for estimates, configuration, and response procedure.

bash
go run ./cmd/ops-gate -check fleet-api-budget

AI provider spend

AI inference is the other direct per-request monetary cost and can run away quickly when a remote provider is enabled.

ControlWhere
Per-account token/cost quotainternal/ai/limit/quota.go
Cost accounting per callinternal/ai/limit/cost.go, internal/ai/provider/cost_decorator.go
Rate limitinginternal/ai/provider/ratelimit_decorator.go
Staged enablementops/rollout/stages.yamlai-provider-live-calls is enabled at the canary stage, not at image promotion

The quota fails closed: when it is exhausted, AI features report unavailable rather than continuing to spend. See docs/runbooks/degraded-mode-ai-provider.md — raising a quota to "unblock" a feature is a deliberate spend decision and should be recorded as one.

Running a local Ollama instance moves this cost from per-token API spend to compute, which is bounded by the resource limits above.

Load generation

Capacity tests are the one part of this system that can generate cost on purpose. ops/capacity/profiles.yaml bounds them:

  • safety.max_duration: 30m — a hard ceiling the gate enforces against every profile's declared duration.
  • safety.allowed_environments — the gate rejects any entry containing "prod", so a capacity profile structurally cannot target production.
  • safety.require_confirmationCONFIRM=RUN must be typed; the k6 scripts refuse to start without it.
  • .github/workflows/capacity-test.yml boots and destroys its own ephemeral stack, and has no input for aiming at a long-lived environment.

Observability retention

The self-hosted observability stack has its own footprint: Prometheus TSDB, Tempo trace blocks, and Pyroscope profiles. Trace sampling is controlled by OTEL_TRACES_SAMPLER_ARG (default 1.0 — every trace). On a busy fleet that is the second-largest storage consumer after signal_log; lowering it is the first lever to pull if trace storage grows faster than expected.

Reviewing cost

The final cloud invoices live outside the repository, but the controls and their shipped defaults are machine-gated:

bash
go run ./cmd/ops-gate -check retention    # signal_log default and enforcement
go run ./cmd/ops-gate -check fleet-api-budget # Fleet API spend + command reserve
go run ./cmd/ops-gate -check capacity     # load-generation ceilings
helm template test helm/teslasync         # resource limits + HPA bounds render
go test ./internal/ai/limit/...           # spend quotas behave

The readiness scorecard reports these as the cost dimension:

bash
go run ./cmd/readiness-scorecard

MIT License · Self-hosted Tesla intelligence