Documentation

From pypiserver

pypiserver is a Bottle app that serves a directory of your own packages over the simple API, with htpasswd-gated uploads. Its upstream story is a redirect: --fallback-url sends the client to pypi.org for anything the directory lacks, and nothing comes back into a cache. It serves under whichever WSGI server is importable (waitress if installed, otherwise the single-threaded stdlib server), and the project advertises that it is looking for new maintainers.

Comparison against velodex

Overlap

  • Hosting your own packages over the PEP 503 simple API.
  • twine uploads as the write path, authenticated against a credential file.
  • sha256 in file links so installers verify what they download.

Extra: what pypiserver does that velodex does not

  • Per-action authentication. pypiserver's -a download,list,update gates reads, listings, and uploads independently against an htpasswd file. velodex authenticates uploads only; reads are open to the network the port lives on.
  • A hand-editable package directory. You can drop files into pypiserver's directory and it lists them. velodex has no drop-in directory; uploads are the only write path.

Missing: what velodex adds

  • A real cached index. pypiserver's fallback is a 302 redirect to pypi.org; the file never enters its directory, so every machine still needs pypi.org access and every miss pays full upstream latency. velodex's cached layer serves misses through itself and keeps them: one egress point, cold installs at upstream speed, and a content-addressed store that dedupes.
  • Outage resilience. An upstream outage takes pypiserver's fallback installs down with it. velodex serves the last good page while the upstream is unreachable, so a pypi.org blip degrades to stale-but-working.
  • Shadowing. Your uploads shadow upstream names instead of coexisting with a redirect.
  • PEP 658 metadata. pypiserver serves none; velodex serves it by default.

Performance vs velodex

The benchmark suite runs both from their published packages. In the install rows, pypiserver's near-zero server CPU and flat cold-versus-warm columns are the redirect showing through: it does no work on a miss because it caches nothing.

uv: install the top 51 PyPI packages (ratios vs direct)
velodex pypiserver
cold cache 3.8 s (1.14x) 3.6 s (1.08x)
warm cache 2.7 s (0.83x) 3.7 s (1.14x)
server CPU 1.0 s (1.00x) 29 ms (0.03x)
server peak memory 537 MB (1.00x) 72 MB (0.13x)
simple-page requests against a warm cache (ratios vs direct)
velodex pypiserver
1 user: requests/s 681 req/s (2.95x) 216 req/s (0.93x)
1 user: p95 latency 6 ms (0.62x) 10 ms (1.08x)
32 users: requests/s 2,987 req/s (3.32x) 904 req/s (1.00x)
32 users: p95 latency 24 ms (0.20x) 115 ms (0.95x)
server CPU 2m 49.4s (1.00x) 3.5 s (0.02x)
server peak memory 324 MB (1.00x) 72 MB (0.22x)

How to migrate

Your package directory does not drop in: re-upload it once with twine, and velodex derives hashes and metadata server-side. Map the flags across:

pypiservervelodex
pypi-server run -p 8080 ~/packagesvelodex serve
http://host:8080/simple/http://host:4433/{route}/simple/
-P htpasswd.txt -a updateupload_token on the hosted index
--fallback-url https://pypi.org/simple/ (redirect)a cached layer under the virtual index (served and cached)
--disable-fallbacka hosted-only index, no cached layer
twine upload -r local dist/*the same command, pointed at the virtual route

Re-upload the directory in one pass:

for f in packages/*; do twine upload --repository-url http://host:4433/{route}/ "$f"; done

Gotchas

  • Reads are open. pypiserver's per-action auth (-a download,list,update) has no counterpart; velodex authenticates uploads only, and reads are open to the network the port lives on. Put velodex on a trusted network or behind your own gateway if reads must be restricted.
  • No hand-editing the directory. If you relied on editing files in the package directory by hand, that workflow is gone; uploads are the write path.
  • Clients stop talking to pypi.org. Under pypiserver's redirect every client still reached pypi.org directly; behind velodex they do not, which is the point, but check that nothing downstream assumed direct upstream access.
On this page