Skip to main content

scalo

PyPI Python Version License

There's plenty of sage advice about running services in production at scale -- config cascades, structured logging, secret masking, Prometheus, OpenTelemetry, health probes, backpressure, graceful shutdown -- but almost none of it as code you can just install and use.

This is that code.

scalo is an integrated runtime for hyperscale-grade control-plane services. Config, logging and metrics come as one pre-wired trinity -- global singletons you just use, no plumbing, no init dance. Everything else leans on that same integration: the config cascade flows straight into the CLI so run/version/config-check just work; the metrics and health wiring feed the K8s probe trinity; and the deployment contract generates your Helm, Dockerfile and Argo manifests from the config the app already declares.

Attach scalo to your service and a whole class of production pain -- the kind done wrong a hundred times elsewhere -- just goes away. Battle-tested, and almost no code on your side to do it properly. It's not a bag of utility functions you wire up yourself; it's the wiring, done right, for free.

scalo comes in two halves that share one set of conventions, idiomatic in each language. scalo-py (this package) is the control plane -- orchestration, APIs and integration glue (pip install scalo). scalo-rs is the data plane -- the Rust hot path where every microsecond and byte counts (cargo add scalo).

What this is (and isn't) for

For: control-plane APIs, UI backends, orchestrators, CLI tools, integration glue, batch workloads, configuration management.

Not for: the hot path. If you're processing millions of messages per second and shaving microseconds matters, that code belongs in Rust -- see scalo-rs. scalo-py is "fast enough for control plane and integration"; scalo-rs is "fast enough for the hot path".

We optimise scalo-py sensibly -- no gratuitously slow choices, no obvious algorithmic mistakes -- but the lean is toward stability, expressiveness, and integration rather than microseconds. Readable abstractions beat inlined ones; clean composition beats hand-rolled loops; heavier deps are acceptable when they earn their keep. This design decision is why scalo-py allows substantial dependency trees and doesn't agonise over async dispatch overhead. We don't hard-iterate the hot path the way scalo-rs does, because that's scalo-rs's job.

This module exists because of this -- but for the backend: https://www.youtube.com/watch?v=xE9W9Ghe4Jk

What you get

Core modules - always installed (uv add scalo):

Module Description Third-party deps
logger Structured JSON logging with automatic PII masking and secrets filtering, container-aware output loguru
config 7-layer cascade (CLI -> ENV -> .env -> YAML -> defaults), container-aware path resolution dynaconf, pyyaml, python-dotenv, mergedeep, tomli-w, dulwich
runtime Auto-detects K8s / Docker / local, resolves config and data paths accordingly stdlib only
cli ServiceApp base class -- subclass to get run / version / config-check for free typer
version-check Optional startup check for new releases (no-op if httpx not installed) httpx (lazy)

Optional modules - install via extras:

Module Extra Third-party deps
http http httpx, stamina (retry with jitter)
metrics metrics prometheus-client, psutil (auto-collects process/container metrics)
expression expression common-expression-language (CEL via Rust/PyO3)
kafka kafka confluent-kafka, genson
opentelemetry opentelemetry OpenTelemetry SDK + OTLP + Prometheus exporters
secrets secrets All backends (Vault/OpenBao + AWS + GCP + Azure)
deployment deployment pydantic (Dockerfile / Helm / Argo / compose generators)

Installation

# Core only (logger, config, runtime, cli, version-check)
uv add scalo

# With common extras
uv add "scalo[http,metrics,kafka]"

# Full stack
uv add "scalo[http,metrics,expression,kafka,opentelemetry,secrets,deployment]"

Package naming: scalo on PyPI, scalo for Python imports.

Optional Extras Sizes

Extra Packages Approx size
http httpx + stamina ~1 MB
metrics prometheus-client + psutil ~1 MB
expression CEL via Rust/PyO3 ~6 MB
kafka confluent-kafka + genson ~11 MB (C libs)
opentelemetry OpenTelemetry SDK + exporters ~4 MB
deployment pydantic ~2 MB
secrets All secrets backends -
secrets-vault OpenBao / HashiCorp Vault (uses http extra) convenience marker
secrets-aws AWS Secrets Manager via boto3 ~100 MB
secrets-gcp GCP Secret Manager ~80-100 MB
secrets-azure Azure Key Vault ~50 MB

Quick Start

Logging

from scalo.logger import logger

logger.info("Service starting", version="1.0.0")
logger.error("DB connection failed", host="postgres", retry=3)

Auto-detects console vs container - structured JSON in containers, human-readable locally. Sensitive fields (passwords, tokens, API keys, etc.) are masked automatically.

Configuration

from scalo.config import settings

# Cascade: CLI args -> ENV -> .env -> settings.yaml -> defaults
host = settings.database.host
port = settings.api.port

ENV key mapping: settings.database.host -> MYAPP_DATABASE_HOST (prefix is configurable per app).

Runtime Paths (container-aware)

from scalo import get_runtime_paths

runtime = get_runtime_paths("myapp")
config = runtime.config_dir / "app.yaml"   # /config in K8s, ~/.config locally
data   = runtime.data_dir  / "state.db"    # /data in K8s, ~/.local/share locally

Metrics

from scalo import create_metrics

metrics = create_metrics("myapp")
requests = metrics.counter("http_requests", "Total HTTP requests")
active   = metrics.gauge("active_users", "Signed-in users")
duration = metrics.histogram("request_duration", "Request duration (s)")

requests.inc()
active.set(42)
duration.observe(0.123)

Automatic process and container metrics (CPU, memory, FDs, uptime) come for free - no extra wiring.

Kafka

from scalo.kafka import KafkaClient, KafkaConsumer, KafkaProducer

Uses confluent-kafka-python (librdkafka) under the hood. Schema-registry integration, health checks, and admin operations included.

Secrets (multi-backend)

from scalo.secrets import SecretsManager

# Picks the configured backend: file, OpenBao/Vault, AWS, GCP, Azure
manager = SecretsManager.from_config(config)   # config dict per docs/api/SECRETS.md
api_key = await manager.get("stripe/api_key")

Two-tier caching (memory + disk), stale-cache fallback for backend outages.

CLI Framework (ServiceApp)

Subclass ServiceApp to get a standard service-CLI lifecycle (run, version, config-check) with no boilerplate. Config flows through the 7-layer cascade automatically.

from scalo.cli import ServiceApp, VersionInfo

class MyService(ServiceApp):
    name = "my-service"
    env_prefix = "MY_SVC"

    def version_info(self) -> VersionInfo:
        return VersionInfo(self.name, "1.0.0")

    async def run_service_async(self, config) -> None:
        # your service code
        ...

if __name__ == "__main__":
    MyService().cli()

DfeApp remains as a deprecated alias for ServiceApp to ease migration from hyperi-pylib; prefer ServiceApp in new code.

Observability port - health and metrics

ServiceApp binds a dedicated observability listener on --metrics-addr (default 0.0.0.0:9090, env METRICS_ADDR) for the whole time the service runs, and serves:

Path Purpose On failure
/metrics Prometheus scrape -
/healthz Liveness - process not deadlocked Restart pod
/readyz Readiness - deps healthy + ready flag set Stop routing traffic

/health/live and /health/ready are kept as aliases, plus /health/startup; point new manifests at the *z names. Startup deliberately has no path of its own - the standard aims startupProbe at the liveness path so the two cannot drift apart.

This is a separate port from your application's, on purpose: exposing user traffic must never expose the operator surface. Probe the observability port in your manifests, not the traffic port.

Register checks and flip readiness on the manager scalo serves, or /readyz will report something your service does not mean:

class MyService(ServiceApp):
    name = "my-service"
    env_prefix = "MYAPP"

    def run_service(self, config):
        self.health().register_ready_check("db", db.is_connected)
        self.health().set_ready()

Liveness MUST NEVER check downstream dependencies (a DB outage shouldn't restart your replicas). Readiness checks dependencies AND requires an explicit set_ready() call - cleared during graceful shutdown.

The listener is stdlib-only, so it works without the FastAPI extra. Set serve_observability = False on your ServiceApp for a service that has no business binding a port (a one-shot CLI, say). A bind failure is fatal by design - a service reporting healthy on a port nobody is listening to is the failure this exists to prevent.

Development

make quality   # lint, type-check, security audit
make test      # run test suite
make build     # build wheel

License

Apache-2.0. Third-party attributions are recorded in NOTICE.

Related

  • scalo-rs -- sister library for Rust services. Same opinions, same patterns, native Rust performance for hot-path workloads.
  • Migrating from hyperi-pylib -- scalo is the renamed, Apache-2.0 continuation of hyperi-pylib; this guide covers the mechanical changes.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

scalo-2.29.10.tar.gz (310.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

scalo-2.29.10-py3-none-any.whl (382.8 kB view details)

Uploaded Python 3

File details

Details for the file scalo-2.29.10.tar.gz.

File metadata

  • Download URL: scalo-2.29.10.tar.gz
  • Upload date:
  • Size: 310.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for scalo-2.29.10.tar.gz
Algorithm Hash digest
SHA256 56cee718b46cc1c45aeacf03f0ac795d94717b753ce9e1ca2e98bd97dce61e8e
MD5 2cac442f335b9d9f7e23388cab7811ac
BLAKE2b-256 b56edddbd7abdeef5b56c08b4b31d174aabf7ac306ef7702db796dd5d8d76f70

See more details on using hashes here.

File details

Details for the file scalo-2.29.10-py3-none-any.whl.

File metadata

  • Download URL: scalo-2.29.10-py3-none-any.whl
  • Upload date:
  • Size: 382.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for scalo-2.29.10-py3-none-any.whl
Algorithm Hash digest
SHA256 4c996b10a2feef4ad230729a8f9941cfb46551f3f5d2cfee2ca4e56d8bedf874
MD5 b1d84a1fdb6c5e56bd094c9c3fc13060
BLAKE2b-256 e2356fe6b18acbc1608ace69fd27d82e58dea8622dcd6d62adb3e325061388bf

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page