How to Use psql in Docker Compose

As a Django newbie, I’ve found it easy to consider the database stuff “magic” and just leave it alone.

I’m starting to realize, however reluctantly, that it’s super important to know how databases work!

After seeing a software engineer YouTuber talk through a few of his database tables with some simple commands, I decided that would be a helpful skill.

So I ran psql and then… obviously it didn’t work because that would be too easy.

In this post, I’ll talk about how to use psql to view your PostgreSQL database from your Django project.

The Docker Problem

Docker has been great to me, but it’s also been a huge headache. There’s just so much going on behind the scenes; it makes reality difficult to comprehend. Layer that on top of learning Django and you’ve got a clear path to overwhelm.

For context, I can attribute my knowledge of Docker and Docker Compose to Will Vincent from his book “Django for Professionals”. That book really helped getting going, but deviating from that path is definitely challenging.

How to Run the Django Server Locally

Here’s a quick review of my local setup.

Dockerfile

# pull base image
FROM python:3.8

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# set work directory
WORKDIR /code

# install dependenceies
COPY Pipfile Pipfile.lock /code/
RUN pip install pipenv && pipenv install --system

# copy project
COPY . /code/

docker-compose.yml

version: "3.7"

services:
  web:
    build: .
    command: python /code/manage.py runserver 0.0.0.0:8000
    env_file:
      - .env
    volumes:
      - .:/code
    ports:
      - 8000:8000
    depends_on:
      - db
  db:
    image: postgres:12
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      - "POSTGRES_HOST_AUTH_METHOD=trust"
    env_file:
      - .env
    ports:
      - 5432:5432

volumes:
  postgres_data:

Then to run the local Django server and local PostgreSQL database:

sudo docker-compose up -d --build

Accessing the Python Container

When running management commands with Docker Compose, we have to specify which service we want to run the command.

I have two services: web and db.

When I need to use Django’s manage.py, I have to prepend this command:

sudo docker-compose exec web...

So, for example, when I want to migrate my users app migrations, it looks like this:

sudo docker-compose exec web python manage.py migrate users

Python Container or Database Container?

So there are two ways (that I know) to access the psql command.

  1. python manage.py dbshell
  2. psql

python manage.py dbshell

As specified in the Django docs:

dbshell

Runs the command-line client for the database engine specified in your ENGINE setting, with the connection parameters specified in your USERPASSWORD, etc., settings.

  • For PostgreSQL, this runs the psql command-line client.
  • For MySQL, this runs the mysql command-line client.
  • For SQLite, this runs the sqlite3 command-line client.
  • For Oracle, this runs the sqlplus command-line client.

This sounds like the way to go, so let’s run it.

sudo docker-compose exec web python manage.py dbshell

And then we get an error:

CommandError: You appear not to have the 'psql' program installed or on your path.

WHAT!? How is that possible?

Our PostgreSQL service “db” surely has psql installed, but that’s a different container. Instead of trying to install psql inside our “web” container, let’s try running psql directly from the “db” service.

psql

To run, we’ll have to specify the “db” service:

sudo docker-compose exec db psql

Annnnnnd we get another error:

psql: error: could not connect to server: FATAL:  role "root" does not exist

This error is telling us that psql is, in fact, available. Confirm by checking the version.

sudo docker-compose exec db psql --version

Which returns:

psql (PostgreSQL) 12.2 (Debian 12.2-2.pgdg100+1)

Now the error told us that “role ‘root’ does not exist”. The default command with no arguments is trying to log in as root. But if we check out the .env file, we can see we specified different values for the PostgreSQL database name, username, and password:

POSTGRES_DB=postgres
POSTGRES_USER=postgres
POSTGRES_PASSWORD=supersecretpassword
POSTGRES_HOST=db
POSTGRES_PORT=5432

Well hey then, we don’t NEED to connect from “root”. What if we specify the username with the -U flag?

sudo docker-compose exec db psql -U postgres

Then we get a new prompt:

postgres=#

This is the psql prompt.

Next Steps

Hey congrats! Now you can use psql!

Or should I say, “Now you have to learn psql commands!”

Here’s a few to get your started:

  • List all databases: \l
  • View all tables in the current database: \d
  • Describe a table: \d table_name
  • List all available users: \du
  • Quit psql: \q

2 Comments

Since you’re sharing PostgreSQL’s network access with your host, via the “ports” stanza, you can also talk to it with a native client on your host. DBeaver and PSequel are what I’ve been using on macOS. I’m a CLI guy, but sometimes a GUI helps visualize; DBeaver will even draw an ER Diagram of you schema.

Leave a Reply