From pypicloud
pypicloud was the closest thing to peryx in Python: a
Pyramid application offering private hosting on S3/GCS/Azure/local
storage with a fallback = cache mode that downloaded misses from PyPI, stored them, and served them. Its repository
was archived on August 27, 2023 ("Pypicloud has transitioned to maintenance mode"), with the last release in December
2022. It runs today only under Python 3.10 with SQLAlchemy pinned below 2.
Comparison against peryx
Overlap
- Read-through cache-on-miss. pypicloud's
fallback = cacheis peryx's default cached-index behavior: fetch a miss, store it, serve it. - Private hosting of your own packages, private names taking precedence over public ones.
- Token or user-authenticated uploads.
Extra: what pypicloud does that peryx does not
- Cloud storage backends. pypicloud stores artifacts on S3, GCS, Azure Blob, or local disk. peryx stores on local disk only.
- Pluggable cache and access backends. pypicloud keeps its package index in SQLAlchemy, Redis, or DynamoDB, and drives access through config, SQL, or LDAP user/group systems. peryx embeds its metadata store (redb, nothing to provision) and authenticates uploads with one token per index.
- Horizontal scale-out. Several stateless pypicloud web servers can share one storage backend and cache DB. peryx is one process per data directory.
Missing: what peryx adds
- It is maintained. pypicloud is archived and pinned to a pre-2.0 SQLAlchemy stack.
- A streaming cold path. pypicloud buffers a missed wheel fully into a
TemporaryFile, writes it to storage and a cache row, and only then serves it, so the client waits for the whole download plus the disk write plus the DB commit. peryx streams the bytes to the client and into the store at once. - Concurrency correctness. A cold burst of clients asking pypicloud for the same wheel each download it and race to
insert the same primary key into single-writer SQLite; the losers surface as
HTTP 500. peryx single-flights the fetch, so all waiters tail one download. - Content-addressed dedup and PEP 658 metadata, neither of which pypicloud
offers (it stores files by
name/version/filenameand serves no.metadatasibling).
Performance vs peryx
The benchmark suite runs both from their published packages. Cold and warm installs through uv:
| peryx | pypicloud | |
|---|---|---|
| cold cache net | 33.4 s ±29% (9.26x) | 9.8 s ±5% (2.72x) |
| warm cache | 8.4 s ±30% (2.37x) | 4.7 s ±3% (1.32x) |
| server CPU | 40.0 s ±16% (1.00x) | 3.4 s ±3% (0.09x) |
| server peak memory | 570 MB ±11% (1.00x) | 367 MB ±8% (0.64x) |
The throughput workload includes the cold burst that pypicloud answers with HTTP 500: four clients ask for one large
wheel the instant it lands.
| peryx | pypicloud | |
|---|---|---|
| cold cache: 4 clients, one wheel net | 819 ms ±1% (0.27x) | error (n/a) |
| hot cache: single download | 4,306 MB/s ±6% (36.97x) | error (n/a) |
| hot cache: 8 parallel downloads | 7,653 MB/s ±4% (65.51x) | error (n/a) |
| server CPU | 294 ms ±13% (1.00x) | 565 ms ±5% (1.92x) |
| server peak memory | 44 MB ±4% (1.00x) | 260 MB ±22% (5.85x) |
How to migrate
Feature-wise this is the most direct migration: peryx's read-through cached index is pypicloud's fallback = cache made
the default. Your cached-index state refills on first use; only hosted uploads need to move. Map the config across:
| pypicloud | peryx |
|---|---|
ppc-make-config + pserve config.ini | a TOML file + peryx serve |
pypi.fallback = cache | the default cached-index behavior |
pypi.fallback = redirect / none | not offered; misses serve through the cache or 404 on hosted-only indexes |
storage = s3 / gcs / azure | local data_dir only |
db = sqlalchemy / redis / dynamo cache | embedded (redb), nothing to provision |
| access backends (config / SQL / LDAP) | one upload_token per hosted index |
/simple/ and /pypi/ routes | /{route}/simple/ |
Gotchas
- No object-storage backend. If your deployment depended on S3 durability, put
data_diron a durable volume and back it up (plain files;rsyncworks), or wait for a storage backend seam. - No per-user permissions. peryx authenticates uploads with a token per index and leaves reads open; see the devpi page for the same caveat.
- One process per data directory. Multiple stateless web servers sharing one cache was a valid pypicloud shape; it is not a peryx shape. Run one peryx per site.