Skip to content

Operator Migration Guide — Python → Rust PhyCloud

This guide describes the cutover from the historical Python PhyCloud MVP to the canonical Rust PhyCloud service (PhyCloud/rust/). The cutover was completed in PhyWare issue #218, the final step of the Python → Rust migration epic #201, and the legacy Python source tree was removed in #269 one release cycle later.

If you are still running the Python service, the steps below describe a drain → switch → verify rollout to the Rust binary.

Compatibility at a glance

Surface Compatibility
Public HTTP API (paths, headers, response shapes) Byte-compatible — clients (PhyTrace SDK, phyware CLI, admin dashboard, PhyComp) need no changes
Provenance hash chain (SHA-256 + canonical JSON) Bit-identical — verified by replay-style compat tests in PhyCloud/rust/tests/provenance_compat.rs
phyc_… API key format Identical
PHYCLOUD_* environment variables Mostly identical (see variable mapping below)
Database schema Compatible — Rust ships sqlx migrate files that are equivalent to the historical Alembic head; you can either point the Rust service at the existing database or rebuild from scratch
Database URL scheme postgres://… (no +asyncpg driver suffix) for the Rust service
Migration tool sqlx migrate (auto-run at startup when PHYCLOUD_AUTO_MIGRATE=true) replaces alembic upgrade head

Cutover plan

1. Pre-flight

  • Snapshot your PhyCloud database (PostgreSQL: pg_dump).
  • Note any custom values for PHYCLOUD_* environment variables in your deployment so they can be replayed against the Rust service.
  • Pin the Rust container image tag (or the phyware repo commit SHA) you intend to roll forward to.

2. Verify against the Rust service in staging

The repo-root docker-compose.yml builds the canonical phycloud service from the Rust Dockerfile:

docker compose up -d phycloud

Verify your existing PhyTrace SDK clients, phyware CLI scripts, and (if deployed) PhyComp instances can ingest and query against the Rust service unchanged. The built-in integration tests already run against the Rust service — see tests/integration/test_phytrace_sdk_rust_integration.py and tests/integration/test_phycomp_rust_phycloud_rust_smoke.py.

3. Switch production traffic

For a Kubernetes / Compose deployment the change is a one-line image swap — the Rust service listens on the same port (8000) and serves the same API surface:

- image: ghcr.io/phyware/phycloud-python:<old-tag>
+ image: ghcr.io/phyware/phycloud-rust:<new-tag>

For bespoke deployments built from source: change the dockerfile: field in your Compose / Helm chart to ./PhyCloud/rust/Dockerfile (built from the repo root) and rebuild.

4. Run database migrations

The Rust service can run sqlx::migrate!() at startup:

environment:
  PHYCLOUD_AUTO_MIGRATE: "true"

This replaces the standalone phycloud-migrate (Alembic) job that the historical Python service required. If you prefer to apply migrations out-of-band, run sqlx migrate run --source PhyCloud/rust/migrations against your database URL before starting the service.

The schema is forward-compatible with the historical Alembic head — no data re-keying is required.

5. Verify

After cutover, exercise the standard smoke checks against the new service:

# Health
curl -sf http://phycloud.example.com/health

# Bootstrap status (should report `bootstrapped: true` after first call)
curl -sf http://phycloud.example.com/bootstrap/status

# Ingest + query round-trip via the phyware CLI
phyware cloud bootstrap --endpoint https://phycloud.example.com
phyware cloud events list --limit 5

The PhyComp service (whether on Python or Rust) requires no configuration change — its PHYCOMP_PHYCLOUD_URL continues to point at the same hostname.

6. Decommission the Python service

Once you are satisfied the Rust service is stable:

  • Remove any explicit phycloud-python deployments from your Compose / Helm manifests.
  • Stop building from the historical Python Dockerfile in your downstream pipelines (the upstream source tree was removed in #269).

Environment variable mapping

Most PHYCLOUD_* variables are accepted unchanged. Exceptions worth calling out:

Variable Python (historical) Rust (canonical) Notes
PHYCLOUD_DATABASE_URL postgresql+asyncpg://… postgres://… Drop the +asyncpg driver suffix; sqlx uses the bare scheme.
PHYCLOUD_AUTO_MIGRATE n/a true / false New flag; tells the Rust binary to run sqlx::migrate!() at startup.
PHYCLOUD_CORS_ORIGINS JSON list (e.g. '["http://…"]') currently a no-op The Rust service does not yet honour this variable; CORS is open in dev mode (PHYWARE_DEV_MODE=1) and disabled in production. Track future work in the migration epic.
PHYCLOUD_LOG_JSON derived from structlog config true / false Default true; emits structured JSON identical in shape to the historical structlog output.
PHYCLOUD_JWT_SECRET / PHYCLOUD_LICENSE_SECRET required in prod required in prod Unchanged.
PHYCLOUD_RATE_LIMIT_* required for slowapi required for tower-governor Unchanged grammar (<count>/<period>).

A full list of supported variables is documented in PhyCloud/rust/src/settings.rs and the crate README.md.

Getting help