postgresqldockershellunixpg-restore

Trouble with restoring Postgres db running in Docker - getting "zsh: command not found: pg_restore"


I run Postgres from a local container, and do not have a local install of it. On macOS Ventura using Docker Desktop.

I can get the backup side to work just fine w/ the following command:

docker exec postgres-local-15 pg_dump -U postgres -F t dvdrental | gzip > /Users/me/Downloads/dvdrental.tar.gz

But I get:

"zsh: command not found: pg_restore"

...when trying either of these methods to restore:

docker exec postgres-local-15 gunzip < /Users/me/Downloads/dvdrental.tar | pg_restore -U postgres -F t -d dvdrental

-or- via network

gunzip < /Users/me/Downloads/dvdrental.tar | pg_restore -U postgres -F t -d dvdrental -h 127.0.0.1 -p 5432

Here's how I'm running pg locally, in Docker (macOS):

docker run --rm -v /Users/me/docker/postgres/15.3:/var/lib/postgresql/data -p 5432:5432 --name postgres-local-15 -e POSTGRES_PASSWORD=mypassword -d postgres:15.3

Could the "zsh" mean that it's looking on my local for pg_restore? I'm not the most sophisticated Docker guy - just utilizing what I found while searching.


Solution

  • What's Happening

    Shells split compound commands into simple commands before running them. Thus, your command line ends up being equivalent to:

    { docker exec postgres-local-15 gunzip < /Users/me/Downloads/dvdrental.tar; } |
      { pg_restore -U postgres -F t -d dvdrental; }
    

    Notice how docker exec is grouped into the simple command with the gunzip, while pg_restore is in a separate group. As this implies, the docker exec only applies to the gzip (which doesn't need to be run inside Docker in the first place), instead of applying to pg_restore.


    How To Fix It (Option 1: Moving Only pg_restore)

    You need to run pg_restore in Docker. You don't need to run gunzip in Docker.

    Thus, you want the docker exec to come before the pg_restore, not before the gunzip.

    gunzip </Users/me/Downloads/dvdrental.tar |
      docker exec postgres-local-15 pg_restore -U postgres -F t -d dvdrental
    

    How To Fix It (Option 2: Moving The Whole Pipeline)

    You can also run the whole pipeline in Docker, by starting a shell inside Docker that runs that pipeline.

    docker exec postgres-local-15 \
      sh -c 'gunzip | pg_restore -U postgres -F t -d dvdrental' \
      </Users/me/Downloads/dvdrental.tar
    

    Notice how the redirection is outside the quotes; this causes it to be run by your local shell before Docker is run, so it gets the file from your regular filesystem rather than looking for it inside the container.