Skip to main content

PyPI Server Chart Usage

PyPI Server is a minimal PyPI compatible server for hosting your own Python packages. This chart helps you deploy a private PyPI server on Kubernetes.

Why use this chart?

  • Host your own private Python package repository
  • Community-maintained and up-to-date
  • Simple, lightweight, and easy to use

Installation

helm repo add community-charts https://community-charts.github.io/helm-charts
helm repo update
helm install my-pypiserver community-charts/pypiserver -n <your-namespace>

Replace <your-namespace> with your target namespace. For advanced configuration, see values.yaml and ArtifactHub page.

Basic Usage

Quick Start

Deploy PyPI Server with default settings:

# Create namespace
kubectl create namespace pypi

# Install chart
helm install pypi community-charts/pypiserver -n pypi

# Check deployment
kubectl get pods -n pypi
kubectl get svc -n pypi

Access the Server

# Port forward to access locally
kubectl port-forward svc/pypi 8080:8080 -n pypi

# Access in browser: http://localhost:8080

Upload Packages

# Upload a package
pip install twine
twine upload --repository-url http://localhost:8080 your-package.whl

# Or use pip to install from your server
pip install --index-url http://localhost:8080/simple/ your-package

Configuration Examples

Basic Configuration

# values-basic.yaml
replicaCount: 1

service:
type: ClusterIP
port: 8080

ingress:
enabled: true
className: nginx
hosts:
- host: pypi.yourdomain.com
paths:
- path: /
pathType: Prefix

Production Configuration

# values-production.yaml
replicaCount: 3

service:
type: LoadBalancer
port: 8080

ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: pypi.yourdomain.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: pypi-tls
hosts:
- pypi.yourdomain.com

resources:
limits:
cpu: 1000m
memory: 4Gi
requests:
cpu: 500m
memory: 2Gi

volumes:
- name: packages
persistentVolumeClaim:
claimName: pypi-packages-pvc

volumeMounts:
- name: packages
mountPath: "/data/packages"

Package Management

Uploading Packages

# Using twine
twine upload --repository-url https://pypi.yourdomain.com your-package.whl

# Using pip
pip install --index-url https://pypi.yourdomain.com/simple/ your-package

# Using curl
curl -X POST -F "content=@your-package.whl" https://pypi.yourdomain.com/upload/

Authentication

For protected packages, create a password file:

# Create password file
htpasswd -c .htpasswd username

# Create Kubernetes secret
kubectl create secret generic pypi-auth --from-file=.htpasswd -n pypi

# Update values.yaml
volumes:
- name: auth
secret:
secretName: pypi-auth

volumeMounts:
- name: auth
mountPath: "/data/.htpasswd"
subPath: ".htpasswd"

podArgs:
- "run"
- "-a"
- "."
- "-P"
- "/data/.htpasswd"
- "--overwrite"

Package Organization

Organize packages by project or team:

# Create package directories
mkdir -p packages/{team-a,team-b,public}

# Upload to specific directories
twine upload --repository-url https://pypi.yourdomain.com/team-a/ package-a.whl
twine upload --repository-url https://pypi.yourdomain.com/team-b/ package-b.whl

Integration Examples

CI/CD Integration

# GitHub Actions example
name: Deploy Package
on:
push:
tags:
- 'v*'

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Build package
run: |
python setup.py sdist bdist_wheel

- name: Upload to PyPI Server
run: |
pip install twine
twine upload --repository-url ${{ secrets.PYPI_URL }} \
--username ${{ secrets.PYPI_USERNAME }} \
--password ${{ secrets.PYPI_PASSWORD }} \
dist/*

Docker Integration

# Dockerfile example
FROM python:3.9-slim

# Install from private PyPI server
RUN pip install --index-url https://pypi.yourdomain.com/simple/ \
--trusted-host pypi.yourdomain.com \
your-private-package

# Continue with your application
COPY . /app
WORKDIR /app
CMD ["python", "app.py"]

Kubernetes Job Example

# job-example.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: package-upload-job
spec:
template:
spec:
containers:
- name: upload
image: python:3.9-slim
command:
- /bin/sh
- -c
- |
pip install twine
twine upload --repository-url https://pypi.yourdomain.com \
--username $PYPI_USERNAME \
--password $PYPI_PASSWORD \
/packages/*.whl
env:
- name: PYPI_USERNAME
valueFrom:
secretKeyRef:
name: pypi-credentials
key: username
- name: PYPI_PASSWORD
valueFrom:
secretKeyRef:
name: pypi-credentials
key: password
volumeMounts:
- name: packages
mountPath: /packages
volumes:
- name: packages
persistentVolumeClaim:
claimName: packages-pvc
restartPolicy: Never

Monitoring and Health Checks

Health Endpoint

PyPI Server provides a health endpoint:

# Check health
curl http://localhost:8080/health

# Expected response: {"status": "healthy"}

Metrics

Enable Prometheus metrics:

podAnnotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/metrics"

Logs

# View logs
kubectl logs -f deployment/pypi -n pypi

# Check specific pod logs
kubectl logs -f pod/pypi-xxxxx -n pypi

Scaling and Performance

Horizontal Scaling

# Scale to multiple replicas
replicaCount: 3

# Use shared storage
volumes:
- name: packages
persistentVolumeClaim:
claimName: pypi-packages-pvc
# Use ReadWriteMany storage class

volumeMounts:
- name: packages
mountPath: "/data/packages"

Resource Optimization

# Optimize for performance
resources:
limits:
cpu: 2000m
memory: 8Gi
requests:
cpu: 1000m
memory: 4Gi

podArgs:
- "run"
- "--server"
- "gunicorn"
- "--workers"
- "8"
- "--worker-connections"
- "2000"

Security Considerations

Network Security

# Restrict network access
service:
type: ClusterIP # Internal access only

# Use ingress for external access
ingress:
enabled: true
annotations:
nginx.ingress.kubernetes.io/whitelist-source-range: "10.0.0.0/8,172.16.0.0/12"

Authentication

# Require authentication for all packages
podArgs:
- "run"
- "-a"
- "."
- "-P"
- "/data/.htpasswd"
- "--overwrite"

TLS/SSL

# Enable HTTPS
ingress:
enabled: true
tls:
- secretName: pypi-tls
hosts:
- pypi.yourdomain.com

Next Steps