PyPI Server Setup
This skill provides guidance for creating local PyPI servers to host and distribute Python packages.
When to Use This Skill
- Setting up a local PyPI repository or package index
- Building Python packages for distribution (wheel/sdist)
- Serving packages via HTTP for pip installation
- Testing package installation from custom index URLs
Environment Reconnaissance (First Step)
Before planning the approach, gather critical environment information:
-
Check Python version: Run
to determine compatibility constraints
- Python 3.13+ removed the module, breaking many older tools like
- Plan fallback strategies based on available Python version
-
Check available system tools: Verify what utilities exist
- Process management:
- Network utilities:
- Package tools: to see pre-installed packages
-
Identify port availability: Check if target ports are free before starting servers
Approach Selection
Decision Framework
| Condition | Recommended Approach |
|---|
| Python 3.13+ | Use with proper directory structure |
| Python 3.12 or earlier | Either or works |
| Simple single-package hosting | is sufficient |
| Full PyPI mirroring needed | Consider or |
Recommended Default: Python's Built-in HTTP Server
For most local PyPI hosting tasks, use
with the correct directory structure. This approach:
- Has no external dependencies
- Works across all Python versions
- Is simpler to configure and debug
Directory Structure Requirements
Pip expects a specific directory structure when using
:
server_root/
└── simple/
└── <package-name>/
└── <package-name>-<version>-py3-none-any.whl
Critical Details:
- The directory must be at the root of the served directory
- Package names in the directory should be normalized (lowercase, hyphens to underscores for some cases)
- The server must be started from the directory containing , not from within it
Building Python Packages
Standard Package Structure
package_name/
├── setup.py
├── pyproject.toml (optional but recommended)
└── package_name/
├── __init__.py
└── module.py
Minimal setup.py Example
python
from setuptools import setup, find_packages
setup(
name="package-name",
version="0.1.0",
packages=find_packages(),
)
Build Commands
bash
# Build wheel and source distribution
python3 -m pip install build
python3 -m build
# Output appears in dist/
Server Setup Steps
Step 1: Create Directory Structure
bash
mkdir -p pypi-server/simple/packagename/
cp dist/*.whl pypi-server/simple/packagename/
Step 2: Start HTTP Server
bash
cd pypi-server
python3 -m http.server 8080
Important: Start the server from the directory that contains
, not from within
.
Step 3: Verify Server
Test that the structure is correct:
bash
curl http://localhost:8080/simple/
curl http://localhost:8080/simple/packagename/
Verification Strategy
Pre-Installation Checks
- Verify server is running:
curl -I http://localhost:PORT/
- Verify simple index exists:
curl http://localhost:PORT/simple/
- Verify package directory exists:
curl http://localhost:PORT/simple/packagename/
- Verify wheel file is accessible:
curl -I http://localhost:PORT/simple/packagename/file.whl
Installation Test
bash
pip install --index-url http://localhost:PORT/simple packagename==version
Post-Installation Verification
python
import packagename
# Test core functionality
Common Pitfalls and Solutions
1. Port Already in Use
Symptom: OSError: [Errno 98] Address already in use
Solutions:
- Use a different port
- Find and kill the existing process:
python
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- Create a custom server script with option
2. Wrong Directory Structure
Symptom: when pip tries to access
Cause: Server started from wrong directory or missing
prefix
Solution: Verify the URL
http://localhost:PORT/simple/
returns a directory listing
3. Python 3.13+ Compatibility
Symptom: ModuleNotFoundError: No module named 'cgi'
when using pypiserver
Cause: Python 3.13 removed the deprecated
module
Solution: Use
instead of pypiserver
4. Process Management Without Standard Tools
Symptom: Cannot find or kill background processes (ps/pkill not available)
Solutions:
- Use Python's process management:
python
import subprocess
proc = subprocess.Popen(['python3', '-m', 'http.server', '8080'])
# Save proc.pid for later termination
- Use filesystem on Linux:
- Track PIDs explicitly when starting background processes
5. Shell Compatibility
Symptom: when sourcing files
Cause: Using
in sh shell (source is a bash feature)
Solution: Use
instead of
for POSIX compatibility:
6. Package Name Normalization
Symptom: Package not found despite correct structure
Cause: Pip normalizes package names (underscores to hyphens, lowercase)
Solution: Use lowercase names and be consistent with hyphens/underscores
Custom Server Script (Robust Alternative)
For better process control and port reuse, consider a custom server script:
python
#!/usr/bin/env python3
import http.server
import socketserver
import os
PORT = 8080
DIRECTORY = "/path/to/pypi-server"
os.chdir(DIRECTORY)
class ReuseAddrServer(socketserver.TCPServer):
allow_reuse_address = True
handler = http.server.SimpleHTTPRequestHandler
with ReuseAddrServer(("", PORT), handler) as httpd:
print(f"Serving at port {PORT}")
httpd.serve_forever()
Checklist Before Starting