I have asked this question on SO but perhaps that was the wrong location. Apologies for double posting but the question may have a better audience here.

The original question is: https://stackoverflow.com/questions/25619290/change-permissions-of-postgres-copy-to-exported-file

And the text is below. Thanks.

When running the postgresql [9.3] "COPY TO" functionality, the postgres user creates a file. I have told it to put the file in the tmp directory (although this can be changed if needed).

This is run as part of a cron job, the user is an "export" user set up especially for this task. The end goal is for the exported file to end up in /home/export/created_file.csv.

1) postgres can't stash the exported file straight into the home directory of another user without setting up the folder to be overly permissive.

2) the export user can copy the file from /tmp to it's home directory, but then can not clean up (delete) the postgres owned file in /tmp.

This is a daily process and so old files will need to be deleted in some way. I can add a cron job to clean up the files afterwards but I was hoping for a cleaner solution - either a way to change the permissions on the file that postgres creates, or some way to give the export user the additional permission of being able to delete that file in advance of it's creation.

Giving the export user the same permissions as postgres in general however isn't an option, as it would then have free reign over the database.

Any advice, be it unix based or postgres based would be very welcome, thanks.

  • Please delete the old question. Sep 3 '14 at 9:24
  • Original SO question deleted.
    – Mitch Kent
    Sep 3 '14 at 11:55
  • ...and link to old question on SO too? Jan 27 at 0:00

You'll want to create a directory without the t (sticky) permissions bit set, and have PostgreSQL copy the file into there. You should also make the directory setgid (g+s), as that makes the group inherited from the directory, not the creating process; see setgid directories.

You could put this directory in /tmp but personally I'd put it some neutral location like /var/lib, or mounted volume somewhere, as /tmp gets cleaned out automatically so you'll need to have the directory re-created if it's missing.

Assuming you do use /tmp:

mkdir -p /tmp/pgcsv
chgrp users /tmp/pgcsv
chmod u=rwX,g=rwsX,o= /tmp/pgcsv

then create the csv in there. It'll be automatically created with group-owner users, which will make it readable by your users (unless PostgreSQL's umask is too restrictive). Because the directory is group-writable by users (or whatever group you use) and does not have the t (sticky) bit set your users can delete files they do not own from it.

The reason this doesn't work when using /tmp/ directly is that the sticky bit is set in /tmp for security, so that user1 can't delete a file created by user2 and replace it with a symlink that user2 then writes to, allowing user1 to steal their data / trick them into overwriting things.

Other options:

  • Run the cron job as user export, using psql's \copy command, so it writes the data over the client/server socket and the file is created as the export user in the first place. This is by far the simplest option, and only slightly slower than a direct COPY.
  • Have a cron job running as root invoke the PostgreSQL copy then move the data file and change its ownership once the COPY finishes.
  • Give the export user the ability to run a very restricted command as root via sudo to move/delete.
  • Use POSIX ACLs to grant the postgres user special permission to write to /home/export (if POSIX ACLs are enabled on your file system)
  • Set group-owner of /home/export to postgres and make its permissions g=wx. PostgreSQL can add new entries, and overwrite anything within that it has write permission to, but not list entries. I don't like this option much.
  • A very well documented answer, thank you. There are at least some options that I would never have known about, and at least a couple I'm kicking myself for not having thought of. Many thanks.
    – Mitch Kent
    Sep 3 '14 at 11:54

I'm probably super late for this, but I just want to add this in case someone else gets stuck:

Since the program you're running in COPY TO PROGRAM is just a regular bash command that receives the result of the select as its input, you could just append a chmod and change file permissions before being done with the file.

For example, you could change:

COPY (SELECT * from ...) TO PROGRAM 'gzip > my_new_file'


COPY (SELECT * from ...) TO PROGRAM 'gzip > my_new_file_path && chmod 755 my_new_file_path'

and that would set permissions to the file before finishing the copy.

  • That would have been mighty useful answer, thank you! But you're right in that you're a little late for me :-)
    – Mitch Kent
    Mar 17 '16 at 21:13
  • Great answer. I would also add that, if you don't want your output file to be zipped, you can just replace gzip with cat.
    – Russ
    Jun 21 '17 at 14:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.