Racker Hacker: Create a Local PyPi Repository Using Only mod_rewrite

The post originally appeared on Major’s blog, Racker Hacker, and with his permission we have reposted it here.

Regular users of Python’s package tools like pip or easy_install are probably familiar with the PyPi repository. It’s a one-stop-shop to learn more about available Python packages and get them installed on your server.

However, certain folks may find the need to host a local PyPi repository for their own packages. You may need it to store Python code which you don’t plan to release publicly or you may need to add proprietary patches to upstream Python packages. Regardless of the reason to have it, a local PyPi repository is relatively easy to configure.

You’ll need to start with a base directory for your PyPi repository. For this example, I chose /var/pypi. The directory structure should look something like this:


For a package like pip, you’d make a structure like this:


Once you have at least one package stored locally, it’s time to configure apache. Here’s a snippet from the virtual host I configured:

DocumentRoot /var/pypi/
ServerName pypi.example.com

Options +Indexes

RewriteEngine On
RewriteRule ^/robots.txt - [L]
RewriteRule ^/icons/.* - [L]
RewriteRule ^/index..* - [L]

RewriteCond /var/pypi/$1 !-f
RewriteCond /var/pypi/$1 !-d
RewriteRule ^/(.*)/?$  [R,L]

The last set of rewrite directives check to see if the request refers to an existing file or directory under your document root. If it does, your server will reply with a directory listing or with the actual file to download. If the directory or file doesn’t exist, apache will send the client a redirection to the main PyPi site.

Reload your apache configuration to bring in your new changes. Let’s try to download the pip tarball from our local server in the example I mentioned above:

$ curl -I 
HTTP/1.1 200 OK

$ curl -I 
HTTP/1.1 200 OK

I’ve obviously snipped a bit of the response above, but you can see that apache is responding with 200’s since it has the directories and files that I was trying to retrieve via curl. Let’s try to get something we don’t have locally, like kombu:

$ curl -I 
HTTP/1.1 302 Found
Location: http://pypi.python.org/simple/kombu/

Our local PyPi repository doesn’t have kombu so it will refer our Python tools over to the official PyPi repository to get the listing of available package versions for kombu.

Now we need to tell pip to use our local repository. Edit ~/.pip/pip.conf and add:

index-url = 

If you’d rather use easy_install, edit ~/.pydistutils.cfg and add:

index_url = 

Once your tools are configured, try installing a package you have locally and try to install one that you know you won’t have locally. You can add -v to pip install to watch it retrieve different URL’s to get the packages it needs. If you spot any peculiar behavior or unexpected redirections, double-check your mod_rewrite rules in your apache configuration and check the spelling of your directories under your document root.

Major Hayden builds OpenStack clouds as a principal architect at Rackspace. Major is a core developer in the OpenStack-Ansible project with a focus on improving information security in OpenStack deployments. He holds multiple Red Hat and Global Information Assurance Certification (GIAC) certifications and has written extensively about securing virtualized Linux environments. Outside of OpenStack, Major has contributed to several open source projects including dracut, systemd, and Ansible. Within the Fedora Linux community, he serves on the Fedora Security Team and Fedora Server Working Group. He enjoys writing on his personal blog, major.io, and he talks about technical topics on Twitter as @majorhayden.



Please enter your comment!
Please enter your name here