EDIT - since Jul 23, 2015
The official postgres docker image will run .sql scripts found in the /docker-entrypoint-initdb.d/ folder.
So all you need is to create the following sql script:
init.sql
CREATE USER docker;
CREATE DATABASE docker;
GRANT ALL PRIVILEGES ON DATABASE docker TO docker;
and add it in your Dockerfile:
Dockerfile
FROM library/postgres
COPY init.sql /docker-entrypoint-initdb.d/
But since July 8th, 2015, if all you need is to create a user and database, it is easier to just make use to the POSTGRES_USER, POSTGRES_PASSWORD and POSTGRES_DB environment variables:
docker run -e POSTGRES_USER=docker -e POSTGRES_PASSWORD=docker -e POSTGRES_DB=docker library/postgres
or with a Dockerfile:
FROM library/postgres
ENV POSTGRES_USER docker
ENV POSTGRES_PASSWORD docker
ENV POSTGRES_DB docker
for images older than Jul 23, 2015
From the documentation of the postgres Docker image, it is said that
[...] it will source any *.sh script found in that directory [
/docker-entrypoint-initdb.d] to do further initialization before starting the service
What's important here is "before starting the service". This means your script make_db.sh will be executed before the postgres service would be started, hence the error message "could not connect to database postgres".
After that there is another useful piece of information:
If you need to execute SQL commands as part of your initialization, the use of Postgres single user mode is highly recommended.
Agreed this can be a bit mysterious at the first look. What it says is that your initialization script should start the postgres service in single mode before doing its actions. So you could change your make_db.ksh script as follows and it should get you closer to what you want:
NOTE, this has changed recently in the following commit. This will work with the latest change:
export PGUSER=postgres
psql <<- EOSQL
CREATE USER docker;
CREATE DATABASE docker;
GRANT ALL PRIVILEGES ON DATABASE docker TO docker;
EOSQL
Previously, the use of --single mode was required:
gosu postgres postgres --single <<- EOSQL
CREATE USER docker;
CREATE DATABASE docker;
GRANT ALL PRIVILEGES ON DATABASE docker TO docker;
EOSQL
Answer from Thomasleveil on Stack OverflowEDIT - since Jul 23, 2015
The official postgres docker image will run .sql scripts found in the /docker-entrypoint-initdb.d/ folder.
So all you need is to create the following sql script:
init.sql
CREATE USER docker;
CREATE DATABASE docker;
GRANT ALL PRIVILEGES ON DATABASE docker TO docker;
and add it in your Dockerfile:
Dockerfile
FROM library/postgres
COPY init.sql /docker-entrypoint-initdb.d/
But since July 8th, 2015, if all you need is to create a user and database, it is easier to just make use to the POSTGRES_USER, POSTGRES_PASSWORD and POSTGRES_DB environment variables:
docker run -e POSTGRES_USER=docker -e POSTGRES_PASSWORD=docker -e POSTGRES_DB=docker library/postgres
or with a Dockerfile:
FROM library/postgres
ENV POSTGRES_USER docker
ENV POSTGRES_PASSWORD docker
ENV POSTGRES_DB docker
for images older than Jul 23, 2015
From the documentation of the postgres Docker image, it is said that
[...] it will source any *.sh script found in that directory [
/docker-entrypoint-initdb.d] to do further initialization before starting the service
What's important here is "before starting the service". This means your script make_db.sh will be executed before the postgres service would be started, hence the error message "could not connect to database postgres".
After that there is another useful piece of information:
If you need to execute SQL commands as part of your initialization, the use of Postgres single user mode is highly recommended.
Agreed this can be a bit mysterious at the first look. What it says is that your initialization script should start the postgres service in single mode before doing its actions. So you could change your make_db.ksh script as follows and it should get you closer to what you want:
NOTE, this has changed recently in the following commit. This will work with the latest change:
export PGUSER=postgres
psql <<- EOSQL
CREATE USER docker;
CREATE DATABASE docker;
GRANT ALL PRIVILEGES ON DATABASE docker TO docker;
EOSQL
Previously, the use of --single mode was required:
gosu postgres postgres --single <<- EOSQL
CREATE USER docker;
CREATE DATABASE docker;
GRANT ALL PRIVILEGES ON DATABASE docker TO docker;
EOSQL
By using docker-compose:
Assuming that you have following directory layout:
$MYAPP_ROOT/docker-compose.yml
/Docker/init.sql
/Docker/db.Dockerfile
File: docker-compose.yml
version: "3.3"
services:
db:
build:
context: ./Docker
dockerfile: db.Dockerfile
volumes:
- ./var/pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
File: Docker/init.sql
CREATE USER myUser;
CREATE DATABASE myApp_dev;
GRANT ALL PRIVILEGES ON DATABASE myApp_dev TO myUser;
CREATE DATABASE myApp_test;
GRANT ALL PRIVILEGES ON DATABASE myApp_test TO myUser;
File: Docker/db.Dockerfile
FROM postgres:11.5-alpine
COPY init.sql /docker-entrypoint-initdb.d/
Composing and starting services:
docker-compose -f docker-compose.yml up --no-start
docker-compose -f docker-compose.yml start
Containerised PostgreSQL and re-initializing databases
If 3&4 are designed to "reinit" the postgres from the bind mounted init data I think all you need to do is remove the service.
docker-compose stop docker-compose rm postgres # should remove the attached volumes docker-compose up -d # should recreate the volume and run the initdb script
EDIT:
Personally I also feel like "just blowing it away" seems a little hasty, if there are any database writes you will lose them. Before you stop you could easily have made a backup.
docker-compose exec {servicename} pg_dumpall -c -U postgres > dump_`date +%d-%m-%Y"_"%H_%M_%S`.sqlAnd then go ahead and then stop/rm the service since you have a backup.
EDIT2:
You may need docker-compose rm -v postgres if you didn't explicitly declare the volumes. The flag tells it to also remove the anonymous volumes associated with the image.
Creating a NEW DB in a running PostgreSQL container
Create docker container with postgres db and tables
How to run a script PostgreSQL with Docker Compose in the initialization ?
If, when you start your Docker Compose, you're getting:
PostgreSQL Database directory appears to contain a database; Skipping initialization
you need to proactively remove the volumes which were set up to store the database.
The command docker-compose down doesn't do this automatically.
You can request removal of volumes like this:
docker-compose down --volumes
Be warned that this will delete any data you had in any database before. You can't get this data back if you remove the volume which contained it!
According to the documentation of postgres docker image you did everything correct.
If you would like to do additional initialization in an image derived from this one, add one or more *.sql, *.sql.gz, or *.sh scripts under /docker-entrypoint-initdb.d (creating the directory if necessary). After the entrypoint calls initdb to create the default postgres user and database, it will run any *.sql files, run any executable *.sh scripts, and source any non-executable *.sh scripts found in that directory to do further initialization before starting the service.
But, there is a catch which I think you missed based on log that you posted above.
Warning: scripts in /docker-entrypoint-initdb.d are only run if you start the container with a data directory that is empty; any pre-existing database will be left untouched on container startup.
So, I would give it a try to empty database_data directory and run again docker-compose up.
I have an app that uses a small static database in PostgreSQL, using the postgres:10-alpine image and a volume mount for the data. This db needs to be refreshed every so often from an external source that the devs control.
At the moment, app and DB are managed with docker-compose. Devs can trigger a new deployment to prod (when tests pass), but they don't have access to prod themselves.
For deployment, we use a bash script that roughly does the following:
-
stops and removes any existing instance of the postgres container using
docker-compose stop && rm -f -
deletes the entire data directory with
docker --rm -t ... rm -rf /var/lib/postgresql/data/ -
initializes the db data again running a one-off
docker run -d postgres -v init.sh:/docker-entrypoint-initdb.d/init.sh -
stops and kills the one-off instance after sleeping for a few seconds (
docker stop && docker rm) -
brings up the docker-compose stack up again (
docker-compose up -d)
Steps 2 and 3 are needed because the new db might come with different roles/privileges, and it's easier to just blow everything up and start again with a clean slate than trying to figure out a diff to apply.
This process works but I find it a bit convoluted, and potentially prone to failure (if the sleep in step 4 is not enough, or needs to become too long when the db grows).
I considered baking the db data into our own images FROM postgres, but I don't really feel like that would be ideal, particularly if the db becomes too big.
Can anyone think of a better solution?
If 3&4 are designed to "reinit" the postgres from the bind mounted init data I think all you need to do is remove the service.
docker-compose stop
docker-compose rm postgres # should remove the attached volumes
docker-compose up -d # should recreate the volume and run the initdb script
EDIT:
Personally I also feel like "just blowing it away" seems a little hasty, if there are any database writes you will lose them. Before you stop you could easily have made a backup.
docker-compose exec {servicename} pg_dumpall -c -U postgres > dump_`date +%d-%m-%Y"_"%H_%M_%S`.sql
And then go ahead and then stop/rm the service since you have a backup.
EDIT2:
You may need docker-compose rm -v postgres if you didn't explicitly declare the volumes. The flag tells it to also remove the anonymous volumes associated with the image.
Why not using a build in the docker-compose.yml which can then rebuild the Postgres service with the new data? If you put this command near end of your Dockerfile you can make sweet usage of caching.
To be honest I don't really get what you question is / the scenario you are facing.