9 Python Library Habits That Cut Your Cloud Bill

Updated 2026-03-27 · SPUNK13 · Back to spunk.bet

Nine changes to how you use Python libraries that cut real infrastructure and CI spend. No exact savings claimed, because they depend on your bill — but each one attacks a line item you are definitely paying for.

1. Replace pip with uv in CI

CI is billed by the minute and a large chunk of every job is dependency installation. uv resolves in parallel and hardlinks from a global cache instead of re-downloading wheels, turning a minute of installing into seconds. Multiply by every push, every branch, every developer.

2. Read Parquet, not CSV

Convert data files to Parquet with pyarrow. Columnar storage with compression typically produces files several times smaller than the equivalent CSV, and because it is columnar you read only the columns you select. That cuts storage, egress and query time at once — and object storage bills all three.

3. Use DuckDB or Polars instead of pandas on large files

pandas materialises whole frames in memory, which is how a data job ends up on an instance sized for its peak rather than its average. duckdb.sql("SELECT ... FROM 'data/*.parquet'") streams and pushes predicates down; Polars does the same with a lazy API. Downsizing one always-on instance class is a permanent monthly saving.

4. Cache expensive calls to disk

functools.lru_cache for in-process repeats, joblib.Memory for results that should survive a restart. Every cached call is an API request or a query you do not pay for twice. This is the single cheapest optimisation on the list — one decorator.

5. Pool your HTTP connections

Creating a new TLS connection per request is latency you pay for in compute time. Use a single httpx.Client or requests.Session for the process lifetime rather than module-level requests.get. On a job making thousands of calls, handshake elimination alone shortens runtime measurably.

6. Swap the JSON library on hot paths

orjson serialises and deserialises substantially faster than the standard library. On an API that returns large JSON payloads, that shows up directly as reduced CPU per request, which is what your container sizing is based on.

7. Cut Docker images with multi-stage builds

Build wheels in one stage, copy only the installed packages into a slim runtime stage, and add --no-cache-dir to pip. Smaller images pull faster on every deploy and every autoscale event, and registry storage is billed too. Dropping build toolchains from the final layer is usually the biggest single cut.

8. Profile import time before blaming the code

python -X importtime script.py lists every import and how long it took. On serverless functions, cold start is dominated by imports, and it is common to find a heavyweight library pulled in at module level for one rarely-used function. Move it inside the function and the cold start drops.

9. Size your workers to your actual cores

Gunicorn's rule of thumb is roughly two workers per core plus one, and defaults are frequently far above or below what the instance provides. Too many workers means memory pressure and swap; too few means paying for idle CPU. Check --workers against the real core count of the instance you deployed to — it is a one-line change on the container you are already renting.

More from spunk.bet

Explore everything we offer — free, fast, no signup.

Visit spunk.bet
400+ Free ToolsFree CasinoMemesBirth ChartsScam DB