Heroku's Surprising psycopg Installation: The Role of Pipfile.lock and Buildpacks
Heroku's Surprising psycopg Installation: The Role of Pipfile.lock and Buildpacks

Heroku Installing Unexpected psycopg Package in Production but Not Testing

Discover why Heroku unexpectedly installs psycopg in deployed Python apps and how Pipfile.lock and buildpacks influence this.6 min


If you’ve deployed your Python app on Heroku, you might sometimes face a confusing issue: Heroku installing the unexpected psycopg package in production, even though it’s not installed locally or in testing. This quirky behavior is often puzzling because your testing environment doesn’t reflect this additional installation, yet production somehow pulls it in. So why does this happen?

If you manage package dependencies using Pipfile and Pipfile.lock, combined with Heroku’s classic buildpacks, you expect the production installation to mirror your local and testing environments. Yet, psycopg mysteriously appears in your production server, creating an unexpected discrepancy. Let’s figure out why this occurs and how you can resolve it.

Understanding Pipfile vs. requirements.txt

Traditionally, Python developers used requirements.txt to manage dependencies. But Pipenv introduced Pipfile and Pipfile.lock, aiming to solve dependency conflicts better and manage environments efficiently. While requirements.txt lists specific package versions explicitly, Pipfile.lock ensures reproducible builds by capturing exact versions and hashes of packages, helping your production environment stay consistent with testing.

Maintaining consistency between your testing and production environments is crucial, preventing nasty surprises in production, like the psycopg mystery we’re discussing here.

Psycopg2 is a popular PostgreSQL adapter for Python while the psycopg package, meanwhile, is slightly different—essentially a lighter or simplified version. Confusingly similar names can lead to unintended installations if explicit dependencies aren’t managed clearly.

Examining Your Testing Environment

Ideally, your testing setup closely resembles your production environment. Using pip freeze helps you check exactly which packages are installed locally or in testing:

pip freeze

Running this should output something like:

Django==3.2.8
gunicorn==20.1.0
psycopg2==2.9.3
requests==2.26.0

As you can clearly see, only psycopg2 is installed locally, not psycopg. So, your testing environment appears clean and controlled.

Investigating Heroku’s Production Differences

When you run:

heroku run pip freeze -a your-app-name

you surprisingly might see:

Django==3.2.8
gunicorn==20.1.0
psycopg==3.1.7
psycopg2==2.9.3
requests==2.26.0

Notice anything unusual here? Yep, somehow the psycopg package showed up, uninvited, alongside psycopg2. But why?

Heroku doesn’t explicitly say why this happens directly, but it relates to certain default tools and add-ons, particularly Heroku’s built-in tools like Pgbouncer.

The Pgbouncer Twist: Why Heroku Installs psycopg

If your Heroku app uses a Pgbouncer buildpack, this is precisely where the psycopg package sneaks in. Pgbouncer is a PostgreSQL connection pooler that sits between your app and database, managing connections efficiently. Heroku’s docs don’t explicitly mention psycopg’s automatic installation, but if you inspect the internals, you find that the connection pooler buildpack relies on the lightweight psycopg to handle database interactions smoothly.

This clearly explains why this extra package, psycopg, appears only in the production deployment and nowhere else.

What About Local Installation With Docker?

Docker setups usually mimic production environments closely. If you build your container locally using Dockerfiles based on Pipfile.lock, you’d typically find no unexpected packages:

FROM python:3.9-slim

RUN pip install pipenv
COPY Pipfile Pipfile.lock ./
RUN pipenv install --system --deploy

Your local Docker build runs smoothly, and checking the packages inside your container would show no psycopg:

docker exec -it your-container pip freeze
Django==3.2.8
gunicorn==20.1.0
psycopg2==2.9.3
requests==2.26.0

Again, no sign of psycopg here. So, why does Heroku insist on psycopg?

Digging Further: Alternative Sources

Since Pipfile.lock explicitly manages your packages, psycopg isn’t appearing due to your own setup. Let’s consider Heroku’s built-in packages and dependencies as the likely culprits instead.

Heroku automatically injects some dependencies when using specific add-ons or services. Pgbouncer buildpack directly depends on psycopg package due to its internal functionality. Thus, Heroku injects psycopg independently into the build environment to ensure compatibility with its PostgreSQL systems.

Your app doesn’t explicitly depend on it, but it ends up in your production environment due to Heroku’s internal procedures and dependency handling.

Resolving the Unexpected psycopg Installation:

To address this issue, consider the following approaches:

  1. Review Heroku Buildpacks: Identify precisely which buildpack introduces psycopg. Verify using commands like:
    heroku buildpacks
  2. Explicitly Handling Dependencies: Ensure your app clearly specifies all required packages in Pipfile and confirms through Pipfile.lock.
  3. Accept psycopg as a harmless addition: If the extra package doesn’t disrupt your application, you may choose to ignore it. However, always verify compatibility.
  4. Docker-based Deployments: Using Docker on Heroku clarifies your package management. Docker offers complete control over installed packages, eliminating unexpected additions.

Keeping Testing and Production Aligned:

This psycopg incident underscores a bigger principle: keeping your development, testing, and production environments aligned. Always ensure your Pipfile.lock file reflects precisely what’s required. Production variations, especially from third-party services like Heroku, occasionally introduce quirks you should understand to maintain consistency.

Regularly audit your environments using pip freeze and compare output across environments (local, testing, staging and production). This can help quickly spot unexpected differences and tackle them before major issues surface in your application performance.

For detailed information about different Python projects or other related troubleshooting, check out this helpful page on Python articles.

Have you encountered unexpected packages like this psycopg drama in your deployments? How did you deal with it? Feel free to share your experience below.


Like it? Share with your friends!

Shivateja Keerthi
Hey there! I'm Shivateja Keerthi, a full-stack developer who loves diving deep into code, fixing tricky bugs, and figuring out why things break. I mainly work with JavaScript and Python, and I enjoy sharing everything I learn - especially about debugging, troubleshooting errors, and making development smoother. If you've ever struggled with weird bugs or just want to get better at coding, you're in the right place. Through my blog, I share tips, solutions, and insights to help you code smarter and debug faster. Let’s make coding less frustrating and more fun! My LinkedIn Follow Me on X

0 Comments

Your email address will not be published. Required fields are marked *