Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chown: changing ownership of ‘/var/lib/postgresql/data’: Operation not permitted, when running in kubernetes with mounted "/var/lib/postgres/data" volume #361

Closed
arnold0617 opened this issue Oct 25, 2017 · 46 comments

Comments

@arnold0617
Copy link

I am running docker.io/postgres:9.6.5 in kubernetes, pod crash loop back off all the time.

I found this message in log, "chown: changing ownership of ‘/var/lib/postgresql/data’: Operation not permitted"

@yosifkit
Copy link
Member

If the error is coming from the following line, it is just a red herring and thus not really the problem. (I think it was added for when the directory was group writable to fix the ownership since postgres can be particular about that).

chown -R "$(id -u)" "$PGDATA" 2>/dev/null || :

Since this seems like a general debugging question and not really a problem with the image, it'd be better to post questions like this in the Docker Community Forums, the Docker Community Slack, or Stack Overflow.

@mikygit
Copy link

mikygit commented Nov 23, 2017

Same problem here. It happens on a docker mounted dir. I suspect a file system weirdy. Still investigating.
If anybody can help ...

@RomanGalochkin
Copy link

RomanGalochkin commented Nov 23, 2017

The same situation. What the problem? Maybe NFS?
I have tried everything.

@mikygit
Copy link

mikygit commented Nov 23, 2017

Same in a sense that I do have this "chown: changing ownership of ‘/var/lib/postgresql/data’: Operation not permitted" error.
It's probably NFS (the same docker image was perfectly running on another host) but I'm not an expert in that field and I'm struggling to explain our IT team what to do and why ...

Any ideas?

@RomanGalochkin
Copy link

RomanGalochkin commented Nov 23, 2017

To solve this problem do it:

  1. Set NFS export dir:
    "nfs/main *(rw,sync,no_subtree_check,no_root_squash)"
    And then restart NFS server:
    sudo /etc/init.d/nfs-kernel-server restart
    or
    sudo exportfs -arv
  2. Set NFS volume like this:
  nfs:
    server: ***
    path: "nfs/main/data"
  1. Update your entity configuration:
         volumeMounts:
            - name: postgres-storage
              mountPath: /var/lib/postgresql/data
              readOnly: false

Very important to export nfs/main and create volume inside of the folder nfs/main/data.

@mikygit
Copy link

mikygit commented Nov 23, 2017

Thx!
This looks like a special case for postgre. Would I have to do the same for every mounted volumes?

@RomanGalochkin
Copy link

RomanGalochkin commented Nov 23, 2017

No, you don't need it. Cuz not every container tries to change owner and permissions of files.

@mikygit
Copy link

mikygit commented Nov 23, 2017

You're right.
Is your third point still a 'NFS" configuration? Because in that case, shouldn't the mountPath point to the host folder that I want to share with the docker container (which in my case is different)?

@RomanGalochkin
Copy link

Yes, it is. NFS exported path is a just entry point. And nobody cannot change owner of the path. But, inside of the folder you can do everything you want. It is the main idea of the solution.

@mikygit
Copy link

mikygit commented Nov 23, 2017

... not sure in the end if you are saying that mountPath should be /xxx/xxx/xxx/xxx if I plan to mount that specific folder ;-) Can you clarify?
nb: could stick to /var/lib/postgresql/data sure, but just to understand properly

@RomanGalochkin
Copy link

RomanGalochkin commented Nov 23, 2017

  1. You create sharing folder: "/nfs/shares".
  2. You point volume to "/nfs/shares/data".
  3. You set volume of a container "/var/lib/postgresql/data".
  4. You give to clients users of NFS server rights "no_root_squash" on the shared folder "/nfs/shares".

We have created volume inside of shared folder.

@mikygit
Copy link

mikygit commented Nov 23, 2017

I deserved that one. My question did sound stupid ;-)
Thanx a lot for your help!

@mikygit
Copy link

mikygit commented Nov 23, 2017

Oops, another question sorry:
What about 'nfs/main/data' you specified in 2)? Should it be related to the shared folder "/nfs/shares"?

@RomanGalochkin
Copy link

RomanGalochkin commented Nov 23, 2017

Yes, should. I showed another steps with different names to understand better.

@mikygit
Copy link

mikygit commented Nov 23, 2017

cheers!

@tianon
Copy link
Member

tianon commented Apr 19, 2018

Closing since this issue is environmental, not something we can really fix in the image.

In the future (as @yosifkit mentioned above), these sorts of questions/requests would be more appropriately posted to the Docker Community Forums, the Docker Community Slack, or Stack Overflow. Thanks!

@lingping525
Copy link

@mikygit Can you please share your configuration snippets. I tried to following the instructions here but still getting stuck for hours. I put one line /Users/me/db -alldirs *(rw,sync,no_subtree_check,no_root_squash) in /etc/exports and restart nfsd. then in my docker-compose.yml, I declare the nfsmount point as

version: '2'
volumes:
  nfsmountdbdata:
    driver: local
    driver_opts:
      type: nfs
      o: addr=host.docker.internal,rw,nolock,hard,nointr,nfsvers=3
      device: ":/Users/me/db/data"
  nfsmountdbinit:
    driver: local
    driver_opts:
      type: nfs
      o: addr=host.docker.internal,rw,nolock,hard,nointr,nfsvers=3
      device: ":/Users/me/db/initdb"
services:

  ## POSTGRES DATABASE
  db:
    image: postgres:9.6
    privileged: true
    volumes:
      #- ./services/db/initdb:/docker-entrypoint-initdb.d
      #- ./services/db/app:/var/lib/postgresql/data
      - nfsmountdbinit:/docker-entrypoint-initdb.d
      - nfsmountdbdata:/var/lib/postgresql/data
    ports:
      - 5432:5432

But when the container db starts, it complains a lot about chown: changing ownership of '/var/lib/postgresql/data/base/**/**': Operation not permitted

@kinkou
Copy link

kinkou commented Feb 28, 2019

@lingping525 (and others struggling with this problem, which I guess is trying get Docker for Mac's NFS sharing to work): the init script will chown the db folder and its contents, unless the container is started with --user. Read this (section "Arbitrary --user Notes").

@shewless
Copy link

shewless commented May 7, 2019

I want to update this issue because I think people are still hitting it via google. I don't think you should add "no_root_squash" to your exports. The answer is in https://hub.docker.com/_/postgres.

PGDATA: This optional variable can be used to define another location - like a subdirectory - for the database files. The default is /var/lib/postgresql/data, but if the data volume you're using is a filesystem mountpoint (like with GCE persistent disks), Postgres initdb recommends a subdirectory (for example /var/lib/postgresql/data/pgdata ) be created to contain the data.

So if you set the environment variable PGDATA to /var/lib/postgresql/data/pgdata and your mountPath stays as /var/lib/postgresql/data then all is well.

Your container will mount /var/lib/postgresql/data and create the pgdata directory. All of the chown operations will work because it's not trying to chown the base directory (which is the nfs mount).

@dempile
Copy link

dempile commented May 13, 2019

thanks @shewless it worked for me

@Asgoret
Copy link

Asgoret commented May 24, 2019

@shewless don't work in OKD 3.11 with psql 11

@eddieparker
Copy link

Apologies for the necro, but Google led me here as the first result. The above didn't fix things for me, but what did was to make a docker file with the following:

FROM postgres:11.2

# Make us the same gid/id as the nfs mount.
RUN sed -i 's/:999:/:5081:/g' /etc/group
RUN sed -i 's/:999:999:/:5081:5081:/g' /etc/passwd

CMD [ "postgres", "-c", "max_connections=10000"]

Specifically I modified the group/passwd gid/uid respectively to match what was the NFS uid/gid and then things started working. Hopefully this helps any other future time travellers.

@aksakalli
Copy link

@eddieparker you don't need to extend the postgres image, just run postgres as the host user instead to tackle this problem:

version: '3.2'

services:
  postgres:
    image: postgres:11
    environment:
      - PGDATA=/var/lib/postgresql/data/pgdata
    user: "${UID:?You must do 'export UID' to launch}:${GID:?You must do 'export GID' to launch}"
    volumes:
      - nfsmountdbdata:/var/lib/postgresql/data
    ports:
      - 5432:5432

volumes:
  nfsmountdbdata:
    driver: local
    driver_opts:
      type: nfs
      o: addr=host.docker.internal,rw
      device: ":/Users/me/db/data"

@arkonsolutions
Copy link

Hello. Wokring for me:

HOST:
sudo useradd -u 999 postgres
cd /path/to/postgres/data/on/host
sudo chown 999 .

@jeremybusk
Copy link

jeremybusk commented Aug 13, 2021

You may want to try changing security context. Note securityContext:, change to your wants. Example code.

blah blah ...
 spec:
      securityContext:
        runAsUser: 1000
        runAsGroup: 3000
        fsGroup: 2000
      containers:
        - name: postgresql-db
          image: postgres:latest
          volumeMounts:
            - name: postgresql-db-disk
              mountPath: /var/lib/postgresql/data
blah blah ...

See https://kubernetes.io/docs/tasks/configure-pod-container/security-context/

@BinitaBharati
Copy link

I want to update this issue because I think people are still hitting it via google. I don't think you should add "no_root_squash" to your exports. The answer is in https://hub.docker.com/_/postgres.

PGDATA: This optional variable can be used to define another location - like a subdirectory - for the database files. The default is /var/lib/postgresql/data, but if the data volume you're using is a filesystem mountpoint (like with GCE persistent disks), Postgres initdb recommends a subdirectory (for example /var/lib/postgresql/data/pgdata ) be created to contain the data.

So if you set the environment variable PGDATA to /var/lib/postgresql/data/pgdata and your mountPath stays as /var/lib/postgresql/data then all is well.

Your container will mount /var/lib/postgresql/data and create the pgdata directory. All of the chown operations will work because it's not trying to chown the base directory (which is the nfs mount).

Thanks for the input. But, unfortunately does not work on postgres 14.1 image.

@tianon
Copy link
Member

tianon commented Feb 4, 2022

@BinitaBharati can you provide a bit more detail for reproducing? I can't seem to: 😕

$ docker pull postgres:14.1
14.1: Pulling from library/postgres
Digest: sha256:3162a6ead070474b27289f09eac4c865e75f93847a2d7098f718ee5a721637c4
Status: Image is up to date for postgres:14.1
docker.io/library/postgres:14.1

$ docker run -d --name pg --env PGDATA=/var/lib/postgresql/data/pgdata --env POSTGRES_PASSWORD=password postgres:14.1
fbfeb257794ff6716265a5625332fdb8f911708b3991ba47f90bb4d4b5bf4a5b

$ docker logs --tail=1 pg
2022-02-04 23:41:42.717 UTC [1] LOG:  database system is ready to accept connections

$ docker exec pg ls -l /var/lib/postgresql/data
total 4
drwx------ 19 postgres root 4096 Feb  4 23:41 pgdata
$ docker exec pg ls -l /var/lib/postgresql/data/pgdata
total 128
drwx------ 5 postgres postgres  4096 Feb  4 23:41 base
drwx------ 2 postgres postgres  4096 Feb  4 23:41 global
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_commit_ts
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_dynshmem
-rw------- 1 postgres postgres  4821 Feb  4 23:41 pg_hba.conf
-rw------- 1 postgres postgres  1636 Feb  4 23:41 pg_ident.conf
drwx------ 4 postgres postgres  4096 Feb  4 23:41 pg_logical
drwx------ 4 postgres postgres  4096 Feb  4 23:41 pg_multixact
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_notify
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_replslot
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_serial
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_snapshots
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_stat
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_stat_tmp
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_subtrans
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_tblspc
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_twophase
-rw------- 1 postgres postgres     3 Feb  4 23:41 PG_VERSION
drwx------ 3 postgres postgres  4096 Feb  4 23:41 pg_wal
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_xact
-rw------- 1 postgres postgres    88 Feb  4 23:41 postgresql.auto.conf
-rw------- 1 postgres postgres 28835 Feb  4 23:41 postgresql.conf
-rw------- 1 postgres postgres    36 Feb  4 23:41 postmaster.opts
-rw------- 1 postgres postgres   101 Feb  4 23:41 postmaster.pid

@BinitaBharati
Copy link

@BinitaBharati can you provide a bit more detail for reproducing? I can't seem to: 😕

$ docker pull postgres:14.1
14.1: Pulling from library/postgres
Digest: sha256:3162a6ead070474b27289f09eac4c865e75f93847a2d7098f718ee5a721637c4
Status: Image is up to date for postgres:14.1
docker.io/library/postgres:14.1

$ docker run -d --name pg --env PGDATA=/var/lib/postgresql/data/pgdata --env POSTGRES_PASSWORD=password postgres:14.1
fbfeb257794ff6716265a5625332fdb8f911708b3991ba47f90bb4d4b5bf4a5b

$ docker logs --tail=1 pg
2022-02-04 23:41:42.717 UTC [1] LOG:  database system is ready to accept connections

$ docker exec pg ls -l /var/lib/postgresql/data
total 4
drwx------ 19 postgres root 4096 Feb  4 23:41 pgdata
$ docker exec pg ls -l /var/lib/postgresql/data/pgdata
total 128
drwx------ 5 postgres postgres  4096 Feb  4 23:41 base
drwx------ 2 postgres postgres  4096 Feb  4 23:41 global
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_commit_ts
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_dynshmem
-rw------- 1 postgres postgres  4821 Feb  4 23:41 pg_hba.conf
-rw------- 1 postgres postgres  1636 Feb  4 23:41 pg_ident.conf
drwx------ 4 postgres postgres  4096 Feb  4 23:41 pg_logical
drwx------ 4 postgres postgres  4096 Feb  4 23:41 pg_multixact
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_notify
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_replslot
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_serial
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_snapshots
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_stat
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_stat_tmp
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_subtrans
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_tblspc
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_twophase
-rw------- 1 postgres postgres     3 Feb  4 23:41 PG_VERSION
drwx------ 3 postgres postgres  4096 Feb  4 23:41 pg_wal
drwx------ 2 postgres postgres  4096 Feb  4 23:41 pg_xact
-rw------- 1 postgres postgres    88 Feb  4 23:41 postgresql.auto.conf
-rw------- 1 postgres postgres 28835 Feb  4 23:41 postgresql.conf
-rw------- 1 postgres postgres    36 Feb  4 23:41 postmaster.opts
-rw------- 1 postgres postgres   101 Feb  4 23:41 postmaster.pid

@tianon - I think there is some misunderstanding 🙂 . What I meant to do is this
Make the postgres container's data folder lie on an external NFS path.
So, as evident from your output, setting the --env PGDATA... makes the data directory to change within the container and consequently the host too. But, the absolute path within the host is controlled by docker. But, in my case, I would like to use my own absolute host path that maps to my NFS path; and not the one made by docker within the host.
When I run this:

docker run --network=host --name postgres  -e POSTGRES_PASSWORD=root -e  PGDATA=/var/lib/postgresql/data/pgdata -v /root/MY_NFS_PATH:/var/lib/postgresql/data -t postgres:14.1

I get this :

chown: changing ownership of ‘/var/lib/postgresql/data’: Operation not permitted error. 

I got it working without using PGDATA like this:
docker run --network=host --name postgres --user 2030 -e POSTGRES_PASSWORD=root -v /root/MY_NFS_PATH:/var/lib/postgresql/data -t postgres:14.1
So, as we can see from the above command, the container is run with my host UID=2030. The NFS path (/root/MY_NFS_PATH) is also owned by the user with UID=2030.

@LePeti
Copy link

LePeti commented Jun 20, 2023

This still doesn't work for me either. This is my setup:

  • docker v24.0.2
  • docker-compose v2.16.0
  • running on macOS Ventura 13.3.1 (M2 chip mbp)

docker-compose.yml:

services:
  db:
      image: postgres:15.3
      container_name: pg-db
      volumes:
        - ./data:/var/lib/postgresql/data
      environment:
        - POSTGRES_PASSWORD=secret
        - PGDATA=/var/lib/postgresql/data/pgdata
      ports:
        - 5432:5432

in terminal, I'm running: docker-compose up db

output and error:

Attaching to project-watertemp-pg-db
pg-db  | chown: changing ownership of '/var/lib/postgresql/data': Permission denied
pg-db exited with code 1

On the other hand, using docker only works:

docker run --name pg --env PGDATA=/var/lib/postgresql/data/pgdata --env POSTGRES_PASSWORD=secret --volume ./data:/var/lib/postgresql/data postgres:15.3

But this is not really a good option for me, as I need multiple services working together.

Any help would be appreciated 🙏

@lanegoolsby
Copy link

If you get here from Google and aren't particularly concerned about using a mounted volume you use a non-mounted volume like this:

services:
  postgres:
    image: postgres:14.2
    restart: always
    environment:
      - POSTGRES_USER=master
      - POSTGRES_PASSWORD=dev
      - POSTGRES_DB=postgres
    logging:
      options:
        max-size: 10m
        max-file: "3"
    ports:
      - "5432:5432"
    expose:
      - 5432
    volumes:
      - postgres-data:/var/lib/postgresql/data

volumes:
  postgres-data:

@robertcnix
Copy link

@BinitaBharati can you provide a bit more detail for reproducing? I can't seem to: 😕

$ docker run --rm -it --user 999 --name pg -v /tmp/pgdata:/var/lib/postgresql/data/pgdata --env PGDATA=/var/lib/postgresql/data/pgdata --env POSTGRES_PASSWORD=password postgres:14

The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locale "en_US.utf8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".

Data page checksums are disabled.

fixing permissions on existing directory /var/lib/postgresql/data/pgdata ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default max_connections ... 20
selecting default shared_buffers ... 400kB
selecting default time zone ... Etc/UTC
creating configuration files ... ok
running bootstrap script ... 2023-12-27 08:39:07.059 UTC [69] FATAL:  data directory "/var/lib/postgresql/data/pgdata" has wrong ownership
2023-12-27 08:39:07.059 UTC [69] HINT:  The server must be started by the user that owns the data directory.
child process exited with exit code 1
initdb: removing contents of data directory "/var/lib/postgresql/data/pgdata"

@kirvedx
Copy link

kirvedx commented Dec 30, 2023

I switched from a mounted host volume to a named volume, and that did the trick.

I had multiple issues going on, namely that I was also using version 14 database files with the version 16 "latest" image, but the solution I came up with still worked. Mind you, it was working with a mounted host volume 6 months to a year ago, with no issues. I'm not sure what changed.

Here's what I did:

In docker-compose.yml:

volumes: # ⇦ Add this, you're specifying named volumes outside of services (when inside,
  pgdata: # ⇦  its volume or bind mounts). The name-only specification is the 'named' volume.
               # ⇦ We could spec an object here and set params, including device path, but - forget all that...
               
services:

  postgres:  # https://hub.docker.com/_/postgres
    container_name: 'postgres-environment'
    image: postgres:latest
    environment:
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypassword
      POSTGRES_DB: my_db
      PGDATA: /var/lib/postgresql/data/pgdata # ⇦ The recommended place to feed persisted database files/data
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data # ⇦ Here, we volume mount our named volume.
    networks:
      - development
    deploy:
      # Not important

  # Adminer below, removed for this purpose...

I tried everything, let me tell you - it was such a pain in the butt. The changes, explained further:

  • Change from using a direct volume or bind mount (i.e. /local/host/path/:/path/on/container), to using a named volume (as above). This is hardly specifically confirmed for you how this works, wordings around this leave you wondering whether you have a named volume, are mounting a volume, or are simply using a bind mount, regardless of what you do.
  • Rather than trying to specify a host path for my named volume, I let docker compose automatically create the volume where it wants it to live, and I manually copied my data into it (again, the former is another very confusing endeavor where even the binary responses you'll get leave you wondering what exactly you're doing.)

Next, I ran docker compose up -d:

[+] Running 4/4
 ✔ Network docker-compose_development  Created                                                                                                                                                         0.0s 
 ✔ Volume "docker-compose_pgdata"      Created                                                                                                                                                         0.0s 
 ✔ Container postgres-environment      Started                                                                                                                                                         0.4s 
 ✔ Container adminer-environment       Started  

Now, to see where your named volume technically lives, you can docker volume inspect <volume name>. But that won't do you any good. To copy files into it the volume needs to be mounted to a container.

So I went ahead and copied my data over to it. Currently, at this stage I have created a container, but it' s restarting endlessly due to other issues that I tackle later. All that matters at this point, however, is that a container is created - not whether it is running. There are several ways to test this, but anyways - it's a convenience to this end. So with the volume mounted to a container:

x@y:~$ sudo chown -R rik:rik /docker-volumes/postgres-environment/datadir/pgdata
[sudo] password for rik:
x@y:~$ docker cp /docker-volumes/postgres-environment/datadir/pgdata postgres-environment:/var/lib/postgresql/data
Successfully copied 52.9MB to postgres-environment:/var/lib/postgresql/data

I chown the pgdata directory so I can cp without sudo. It really doesn't matter what the permissions on that directory are though.

Finally, I docker compose down and then docker compose up -d to see:

PostgreSQL Database directory appears to contain a database; Skipping initialization

2023-12-30 05:54:47.912 UTC [1] FATAL:  database files are incompatible with server
2023-12-30 05:54:47.912 UTC [1] DETAIL:  The data directory was initialized by PostgreSQL version 14, which is not compatible with this version 16.1 (Debian 16.1-1.pgdg120+1).

So if you happen to have the right version going on, you'll find that you can easily get up and running this way and all while preserving your existing data and configuration. It didn't matter what the permissions were set to initially on that data, I tried 5 different ways; It simply worked. If you run into trouble just chown -R it to be the 999:999 you know it needs to be first. You won't need to though.

In my case, I recreated an empty volume and found that my Docker Desktop VM was out of disk space. So after deleting ~60GB of GitLab docker containers and associated images, I found myself up and running and tests passing on my module with the above method - with persisted data kept on my local host, just not where I originally had it. I consider it a fix since using a named volume seems to get rid of all those permissions issues for the long haul. In my case I did hold onto my v14 database files, but I went ahead and let docker-compose recreate my datadir for v16. Using this method, I was able to use my old v14 data with the postgres:14 image - which did fall victim to the original issue as well in my original implementation.

Setting UID:GID in every fashion did nothing, and every other manner by which to handle this scenario required deriving a new image from a custom Dockerfile derivative. I was looking for a more streamlined and universal way to handle things - and this was it for me.

I hope this helps those of you out that were running into this issue similarly to me - who find your way here. I know its slightly diff in context than the issue here, but I feel confident I'll land here again if I run into the issue again in the future.

@robertcnix
Copy link

I solved it by mounting the folder above the pgdata folder so that initdb can own the creation of the pgdata folder instead of configuring it for initdb to install directly into the mounted folder.

In other words, instead of

$ docker ... -v /tmp/pgdata:/var/lib/postgresql/data/pgdata --env PGDATA=/var/lib/postgresql/data/pgdata ...

I did

$ docker ... -v /tmp/pgdata:/var/lib/postgresql/data --env PGDATA=/var/lib/postgresql/data/pgdata ...

@kirvedx
Copy link

kirvedx commented Dec 31, 2023

I solved it by mounting the folder above the pgdata folder so that initdb can own the creation of the pgdata folder instead of configuring it for initdb to install directly into the mounted folder.

In other words, instead of

$ docker ... -v /tmp/pgdata:/var/lib/postgresql/data/pgdata --env PGDATA=/var/lib/postgresql/data/pgdata ...

I did

$ docker ... -v /tmp/pgdata:/var/lib/postgresql/data --env PGDATA=/var/lib/postgresql/data/pgdata ...

So what you did, was take the data files and configuration files that you keep locally at /tmp/pgdata, and instead of mounting them into /var/lib/postgresql/data/pgdata you mounted them into /var/lib/postgresql/data. In doing so, you chose to not follow the recommendation for where to put persisted data that you maintain yourself within a docker container of postgres (which is /var/lib/postgresql/data/pgdata, and to instead put the persisted data directly into the /var/lib/postgresql/data directory instead.

However, then you still told postgresql to find the data in /var/lib/postgresql/data/pgdata (exactly where its recommended to mount host-managed persisted data), but again you did not provide it there.

So what happened when you started the postgres container, was that postgres did not find any data or configuration in the directory it was told to use for it, and so automatically generated it for you. It's no different than when simply not persisting data - except you're persisting the newly created data. Is that what you intended? Are subsequent startups into new instances of a container working without issue for you now with that newly persisted data?

Had you done:

$ docker ... -v /tmp/:/var/lib/postgresql/data --env PGDATA=/var/lib/postgresql/data/pgdata ...

You'd have been closer to doing what I think you were hoping you were doing. However, initdb wouldn't then become responsible for creating pgdata. You would still find that the mount point still presents pgdata with the permissions it has locally to the container instance. Even if the uid:gid is correct - there is still the same permissions issue (at least there was for me).

Here's how I was doing it previously in my docker-compose.yml implementation:

version: '3.9'

services:

  postgres:  # https://hub.docker.com/_/postgres
    container_name: 'postgres-environment'
    image: postgres:latest
    environment:
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypassword
      POSTGRES_DB: my_db
      PGDATA: /var/lib/postgresql/data/pgdata
    #user: "999:999"
    ports:
      - "5432:5432"
    volumes:
      - /docker-volumes/postgres-environment/datadir/:/var/lib/postgresql/data
    networks:
      - development
    deploy:
      # Not important

  # Adminer below, removed for this purpose...

It's the docker compose equivalent to actually doing what I think you were attempting to accomplish, where the mount point focus becomes the data directory itself, one level higher than the directory actually being used for the data - with the locally 'preserved' ./pgdata directory actually a subdirectory to the locally preserved data directory, and so subsequently still a subdirectory of /var/lib/postgresql/data/ within the container.

That's where I specifically was facing the permissions issue this time around. It's how I had it resolved previously, however - but it was not working at this point. The permissions that existed on the locally preserved pgdata directory and its contents were the permissions that the previous postgres container instances had set onto it/them, having originally created the directory and data in the instance in the past on first run. It was then that I modified the configuration within it and simply held on to it. It worked fine for a long time but suddenly the permission issue came back in new force. When checking the permissions it's still set to 999:999 (which is the uid/gid of postgres within the container). However on my system 999 is systemd-coredump.

The only other recommended fix that I found that supposedly works was making a dockerfile that starts with FROM postgres:latest and which then modifies the postgres user to use the same uid:gid that is used locally. I'm worried that it will ultimately end up requiring that the local user will need to be a locally maintained postgres user as well. I don't want to have to do that - and moreover, I don't want to have to maintain a customisation of the latest postgres image that changes the uid:gid - nor to have to change the uid:gid of my local user either (999 is already taken anyways).

Using a named volume really was the magic solution that I was looking for. I took the original persisted data and configuration from my original v14 instance and copied it into the named volume for my new v14 configuration - and was able to fix/resolve/get around those permissions issue(s) while still continuing to use my historic data/configuration.

For v16 I went with a named volume as well, letting it generate new data by simply specifying a new empty named volume; It will persist and continue to use that newly generated data from now on though - without permissions issues.

Are you clear on the fact that your latest postgres container instance wasn't using your originally persisted data - but was generating new data? At least, according to what you provided, on the initial first run following that change? That while that newly created data should persist and it will be what you're using going forward if the problem is now gone and you keep that configuration - that it isn't the original persisted data that you were previously using?

@slaesh
Copy link

slaesh commented Jan 24, 2024

I am struggling too, still.. setup is a k8s with a mounted volume (SMB). I did the mentioned "trick" with the mounting PATH-A but setting PGDATA to PATH-A/something.. but this does not work either.
is there a way to disable this at all? if you (postgres) are able to read and write.. thats fine, keep doing! :)) at least with this one ENV-VARIABLE ignore-permissions-yes-i-know-what-i-do ..

I cant and I dont want to create a "postgres" user on the SMB server? Oo
what am I supposed to do.. :(

@yosifkit
Copy link
Member

There are two ways to run the postgres image while providing a bind mount directory (i.e., -v /path/on/host/:/var/lib/postgresql/data/):

  1. allow the entrypoint script to chown it to the postgres user within the container (user 999)
    • it does the directory creation and chown before stepping down from root
      if [ "$user" = '0' ]; then
      find "$PGDATA" \! -user postgres -exec chown postgres '{}' +
  2. provide a path with already correct (non-root) ownership and appropriate permissions and run the container as that user (and thus the chown is skipped)
    • it will still attempt to chmod 00700 since initdb is also picky about that
    • test from WSL2 to dockerd running in Docker Desktop
      $ mkdir pg-data
      $ sudo chown 9000:9000 pg-data
      $ ls -ln
      ...
      drwxr-sr-x 2 9000 9000 4096 Jan 24 14:03 pg-data
      $ docker run -d --user 9000:9000 -v "$PWD/pg-data:/var/lib/postgresql/pgdata -e POSTGRES_PASSWORD=12345 postgres
      $ # success!!! (note the mode change on the directory)
      $ sudo ls -lan pg-data/
      total 132
      drwx------ 19 9000 9000  4096 Jan 24 14:09 .
      drwxrwsr-x  6 1000 1000  4096 Jan 24 14:03 ..
      drwx------  5 9000 9000  4096 Jan 24 14:08 base
      drwx------  2 9000 9000  4096 Jan 24 14:09 global
      drwx------  2 9000 9000  4096 Jan 24 14:08 pg_commit_ts
      drwx------  2 9000 9000  4096 Jan 24 14:08 pg_dynshmem
      -rw-------  1 9000 9000  5743 Jan 24 14:08 pg_hba.conf
      -rw-------  1 9000 9000  2640 Jan 24 14:08 pg_ident.conf
      drwx------  4 9000 9000  4096 Jan 24 14:09 pg_logical
      drwx------  4 9000 9000  4096 Jan 24 14:08 pg_multixact
      drwx------  2 9000 9000  4096 Jan 24 14:08 pg_notify
      drwx------  2 9000 9000  4096 Jan 24 14:08 pg_replslot
      drwx------  2 9000 9000  4096 Jan 24 14:08 pg_serial
      drwx------  2 9000 9000  4096 Jan 24 14:08 pg_snapshots
      drwx------  2 9000 9000  4096 Jan 24 14:09 pg_stat
      drwx------  2 9000 9000  4096 Jan 24 14:08 pg_stat_tmp
      drwx------  2 9000 9000  4096 Jan 24 14:08 pg_subtrans
      drwx------  2 9000 9000  4096 Jan 24 14:08 pg_tblspc
      drwx------  2 9000 9000  4096 Jan 24 14:08 pg_twophase
      -rw-------  1 9000 9000     3 Jan 24 14:08 PG_VERSION
      drwx------  3 9000 9000  4096 Jan 24 14:08 pg_wal
      drwx------  2 9000 9000  4096 Jan 24 14:08 pg_xact
      -rw-------  1 9000 9000    88 Jan 24 14:08 postgresql.auto.conf
      -rw-------  1 9000 9000 29770 Jan 24 14:08 postgresql.conf
      -rw-------  1 9000 9000    36 Jan 24 14:08 postmaster.opts

The second one might require setting PGDATA to a subpath of the mounted directory if the directory already has other content or cannot be presented as owned by the particular user. See also the Docker Hub doc page. (Note: do not mount to /var/lib/postgresql/ and use the default PGDATA of /var/lib/postgresql/data since there will still be an automatic anonymous volume created by docker so your files will be there and not in the host bind mount)

@slaesh
Copy link

slaesh commented Jan 25, 2024

thanks @yosifkit for helping out! I would be happy to follow along using docker or docker-compose.. but I am not sure how to do it properly using kubernetes.

here are some screenshots from my shared folders, both having now the 9000:9000 owner:

image

and this is my deployment.. tried to use the securityContext stuff, but no idea if this is the right thing to do..

apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
  namespace: paperless
  labels:
    app: postgres
spec:
  selector:
    matchLabels:
      app: postgres
  replicas: 1
  template:
    metadata:
      labels:
        app: postgres
    spec:
      securityContext:
        runAsUser: 9000
        runAsGroup: 9000
      containers:
        - name: postgres
          image: postgres:16.1-alpine3.19
#          command: [ "/bin/bash", "-c", "--" ]
#          args: [ "while true; do sleep 30; done;" ]
          ports:
            - containerPort: 5432
              name: 5432tcp5432
              protocol: TCP
          env:
            - name: PGDATA
              value: /var/lib/postgresql/data/data-wrapper-for-smb-to-work
            - name: POSTGRES_USER
              value: pgadmin
            - name: POSTGRES_PASSWORD
              value: what-the-heck!
            - name: POSTGRES_DB
              value: paperless
          volumeMounts:
            - mountPath: /var/lib/postgresql/data
              name: paperless
              subPath: ./postgres-data
      volumes:
        - name: paperless
          persistentVolumeClaim:
            claimName: paperless-pvc-smb

and here are the postgres logs

chmod: /var/run/postgresql: Operation not permitted
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locale "en_US.utf8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".

Data page checksums are disabled.

fixing permissions on existing directory /var/lib/postgresql/data/data-wrapper-for-smb-to-work ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default max_connections ... 20
selecting default shared_buffers ... 400kB
selecting default time zone ... UTC
creating configuration files ... ok
2024-01-25 06:41:27.211 UTC [46] FATAL:  data directory "/var/lib/postgresql/data/data-wrapper-for-smb-to-work" has wrong ownership
2024-01-25 06:41:27.211 UTC [46] HINT:  The server must be started by the user that owns the data directory.
child process exited with exit code 1
initdb: removing contents of data directory "/var/lib/postgresql/data/data-wrapper-for-smb-to-work"
running bootstrap script ...

@LaurentGoderre
Copy link
Member

LaurentGoderre commented Jan 29, 2024

For postgres it should be

securityContext:
  runAsUser: 70
  runAsGroup: 70

@slaesh
Copy link

slaesh commented Jan 29, 2024

For postgres it should be

securityContext:
  runAsUser: 70
  runAsGroup: 70

thanks @LaurentGoderre , tried that too. even building a custom image with 1000 instead of 70. even 9000, ... didnt work either. none of them.

in the 70's case: it is complaining then that the rights are missing to create the folder, even if it is created. guess it wants to create the nested stuff.. but it is not allowed to.. sure, cause the file-server has no user with 70:70, which has the rights to edit stuff :) one of the most used DBs and its unusable cause of awkward permission stuff :D

I gave up..

@LaurentGoderre
Copy link
Member

@slaesh I think it's a mismatch happening. When UID 70, postgres is happy but the mount volume might not have the right permission.

You could try the following line in your custom image replacing the UID with the one you need (postgres seems to care more about the name of the user than the id)

RUN echo 'postgres:x:70:70:Postgres:/var/lib/postgresql:/bin/false' > /passwd \
    && echo 'postgres:x:70:postgres' > /group

@slaesh
Copy link

slaesh commented Jan 29, 2024

thanks @LaurentGoderre , but sadly no success.

executing _main()
uid: 1000, gid: 1000
call initdb!!
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locale "en_US.utf8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".

Data page checksums are disabled.

fixing permissions on existing directory /var/lib/postgresql/data/data-wrapper-for-smb-to-work ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default max_connections ... 20
selecting default shared_buffers ... 400kB
selecting default time zone ... UTC
creating configuration files ... ok
2024-01-29 16:33:43.830 UTC [44] FATAL:  data directory "/var/lib/postgresql/data/data-wrapper-for-smb-to-work" has wrong ownership
2024-01-29 16:33:43.830 UTC [44] HINT:  The server must be started by the user that owns the data directory.
child process exited with exit code 1
initdb: removing contents of data directory "/var/lib/postgresql/data/data-wrapper-for-smb-to-work"
running bootstrap script ...

the folder:
image

the Dockerfile:

image

modified script with logs:

image

@LaurentGoderre
Copy link
Member

@slaesh and you're sure that the user paperless has a UID of 1000?

@slaesh
Copy link

slaesh commented Jan 29, 2024

yes. he has. I even created a postgres user on my dev system.. with the UID 1001:

executing _main()
uid: 1001, gid: 1001
call initdb!!
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locale "en_US.utf8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".

Data page checksums are disabled.

fixing permissions on existing directory /var/lib/postgresql/data/data-wrapper-for-smb-to-work ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default max_connections ... 20
selecting default shared_buffers ... 400kB
selecting default time zone ... UTC
creating configuration files ... ok
2024-01-29 17:25:41.525 UTC [44] FATAL:  data directory "/var/lib/postgresql/data/data-wrapper-for-smb-to-work" has wrong ownership
2024-01-29 17:25:41.525 UTC [44] HINT:  The server must be started by the user that owns the data directory.
child process exited with exit code 1
initdb: removing contents of data directory "/var/lib/postgresql/data/data-wrapper-for-smb-to-work"
running bootstrap script ...

image

image

I dont get it... :D

@LaurentGoderre
Copy link
Member

I just re-read, are you trying to mount a Windows folder via SMB? Also are you using Compose or K8S when trying these changes or both?

@slaesh
Copy link

slaesh commented Jan 29, 2024

no its not a windows folder. shared NAS (linxux) folder. k8s all the time :)

@oldshensheep
Copy link

I have similar issue, and I found it's because of rootless docker.
Rootless docker lacks permissions to modify GID/UID directly and uses uidmap to map UIDs.

If you are using rootless Docker, ensure that:

> cat /etc/subuid
mio:165536:65536
sheep:165536:65536

and then execute:

> chown <cuid>:<you_current_user_group>  <you_postgres_data_dir>

Here, <cuid> should fall within the range [165536, 165536+65536] as specified in /etc/subuid.

https://docs.docker.com/engine/security/rootless/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests