15

I get the error message...

could not change directory to "/home/corey/scripts": Permission denied 

... when I run the following script ...

#!/bin/bash sudo -u postgres psql < setup_dev_db.sql read -rsp $'Press any key to continue...\n' -n 1 key 

... the contents of setup_dev_db.sql are executed without any issues, but the error is annoying.

Can I get rid of it?

3
  • 3
    Let's try to localize where the error is. If you type sudo -u postgres, do you get the same error? Commented Sep 11, 2015 at 14:40
  • 1
    @MarkPlotnick is on the right track. There are too many things going on with your script to easily identify WHICH is causing the problem. However, the quick check would be to review permissions for user postgres on the folder /home/corey/scripts Commented Sep 11, 2015 at 14:48
  • 3
    I suspect it's simply because the OP ran the script from that directory, which the postgres user can't read, and the error would go away if the script was run from, say, /tmp. Commented Sep 11, 2015 at 14:58

6 Answers 6

11

Postgres wants to create a $HOME/.psql_history file, where it will store all your queries and commands from the psql client. It may well want to do something else at $HOME, but I don't see any evidence in the form of other hidden files. And it won't actually create the history file unless you're using psql interactively, which you're not.

I had this exact same problem and found this question, but the accepted answer wasn't acceptable to me -- I shouldn't have to grant postgres permission to leave a trail of my queries in whatever directory I happen to be in when I run a script!

@Corey, the solution you mentioned in your comment (cd /tmp before calling sudo...) is probably the best. psql won't create this file in /tmp (I'm sure that's deliberate, because it could allow unprivileged users to read the file).

There are two other solutions I can think of :

  1. Run psql in a login shell by adding -i to your command

    sudo -i -u postgres psql < setup_dev_db.sql 

    This will set $HOME to postgres's HOME directory, listed in /etc/passwd. For Ubuntu, that's /var/lib/postgres. But since you're piping in commands, it won't create a .psql_history file. However, if you use interactive psql, anyone else with sudo privileges on the machine will have access to your command history.

    I'm not sure if there are any other negative consequences to running a login shell in this situation.

  2. Run psql as a less-privileged user, e.g.

    $ psql dev_db -hlocalhost corey_dev -W < setup_dev_db.sql 

    If this is a problem because you leave postgres user creation to your setup_dev_db.sql script, and you don't have any users yet, just add a createuser command in your script first, something like this:

    $ sudo -u postgres createuser corey_dev -P 

    and perhaps ...

    $ sudo -u postgres createdb dev_db "Dev database" 

NOTE: When using the psql client interactively (which you're not, here), if you see a message like could not change directory to "/home/corey/scripts": Permission denied message ****, psql is going to write to /var/lib/postgres/.psql_history (or wherever its $HOME is)! If you've ever seen that warning when using interactive psql, go look--you'll probably find a hidden history file.

3
  • I had to do sudo -u postgres -i psql -U .... because sudo -u -i postgres psql -U and sudo -ui postgres psql -U ... didn't work. Commented Jan 12, 2022 at 15:35
  • I don't think that psql wanting to write to $HOME/.psql_history is the cause of this issue. In support of this suspicion I would like to offer the output of sudo -u postgres bash -c 'echo $HOME', which is /var/lib/postgresql; so writing to .psql_history should just work. No, for some reason,psql seems to insist on doing a chdir first to whatever the current working directory is, which for a non-interactive sudo will be the working directory of the invoker; hence the "could not change directory to /home/corey/scripts" output, which is unlikely to be someone's homedir. Commented Dec 15, 2023 at 14:46
  • However, if you do see your own homedirectory printed, then adding a --set-home flag to sudo will definitely print the homedir of the postgres user; but I will bet that, all other things considered equal, sudo --set-home -u postgres psql will still print the "could not change directory to ..." error. Commented Dec 15, 2023 at 14:54
7

To change to a directory, a user must have the 'x' permission for that directory.

I assume you are running the script from '/home/corey/scripts'. When 'sudo -u postgres' changes the current user to 'postgres' it attempts to set the working directory for 'postgres' to the working directory it was called from generating the error you're seeing.

Make sure that the user 'postgres' has permission 'x' for '/home/corey/scripts'.

1
  • 1
    I added "cd /tmp" to the top of my file and prefixed "setup_dev_db.sql" with "~/Dropbox/scripts/" and that fixed it. Commented Sep 15, 2015 at 15:24
3

TL;DR: There is discussion about this issue in Postgres mailing list and it has been fixed in PostgreSQL 16.

Reason for the error:
While @lambart's solutions are correct, the reason for the error is not since it has nothing to do with .psql_history file. The

could not change directory to "/home/corey/scripts": Permission denied 

error is coming from resolve_symlinks() function which resolves symlinks using chdir and finally tries to chdir back to the original directory which fails if the user executing psql does not have permissions to do so. The resolve_symlinks() call comes from find_my_exec() which in turn is called by set_pglocale_pgservice() (which sets PGSYSCONFDIR and PGLOCALEDIR).

Solutions for older versions:

For versions prior 16, the error is fixed by giving the user executing psql command x permission to the working directory where the command is issued. This can be done in several ways:

  1. Add -i to sudo command to use login shell which changes the working directory to the home directory of the postgres user before executing the psql command (postgres user naturally has the x permission to its own home directory):

    sudo -i -u postgres psql < setup_dev_db.sql 
  2. Give postgres user x permission to the current working directory by adding it to the group owning the working directory:

    # Get group owning the current directory stat -c "%G" . # Add postgres user to that group sudo usermod -aG <group above here> postgres 
  3. Give postgres user x permission to the current working directory by giving x permission to "others" to the working directory. This can be handy e.g., in single user VM-based systems meant for development, such as WSL2 where simple chmod o+x /home/$USER command will fix the error.

  4. Run the psql using current user which of course have permissions to the current working directory.

  5. Before executing the command, change working directory to a directory where postgres user has x permission, e.g., cd /tmp.

1

You may use sudo -u postgres -c your_command.bash or su postgres -c your_command.bash. You could also use,

su - postgres your_command.bash 

This will ensure that you acquired all the postgres account enviroment.

1
  • won't work if you don't want everything in the script to run as root... Commented Oct 6, 2017 at 7:17
0

For me this did the trick, pay attention to quotes (')

sudo -Hiu postgres 'pg_dump --column-inserts --data-only --table=someTable entities_db > /var/backups/anywhere/$(date +%Y%m%d_%H%M%S)_someTable.sql' 

Note the -Hiufor sudo, or use su - postgres

you can also put that in a cronjob for root with crontab -e

0

You can save the current dir to the stack with pushd then move to the directory with permissions, change the user to postgres, and when return, move to the last directory using popd.

pushd /tmp; sudo runuser -u postgres -- psql -d custom_db; popd 
  • pushd: will save the current directory and move to /tmp to avoid the error message with postgres user
  • sudo run user -u postgress -- psql -d custom_db: will change to postgres user and execute psql
  • popd: after returning from psql, move back to the last directory in use, before changing to /tmp

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.