-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
Bug Report: PostgreSQL 18 - Initialization scripts in /docker-entrypoint-initdb.d/ never execute
Description
Scripts placed in /docker-entrypoint-initdb.d/
are never executed when using PostgreSQL 18 Docker image because the data directory is never considered "empty" due to automatic creation of version subdirectory.
Environment
- PostgreSQL Version: 18 (postgres:18)
- Docker Version: 24.x+
- Docker Compose Version: 2.40.0
- Host OS: Linux (Ubuntu/Debian based)
Steps to Reproduce
- Create a docker-compose.yml:
services:
postgres:
image: postgres:18
container_name: test-postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: test123
POSTGRES_DB: postgres
volumes:
- postgres-data:/var/lib/postgresql/data
- ./init:/docker-entrypoint-initdb.d
ports:
- "5432:5432"
volumes:
postgres-data:
- Create init script at
./init/01-init.sql
:
CREATE DATABASE testdb;
- Start container with empty volume:
docker compose down -v
docker compose up -d
- Check if database was created:
docker exec test-postgres psql -U postgres -c '\l'
Expected Behavior
The testdb
database should be created as per the init script in /docker-entrypoint-initdb.d/
.
Actual Behavior
The init script is never executed. Only default databases (postgres, template0, template1) exist.
The script exists in the container:
$ docker exec test-postgres ls -la /docker-entrypoint-initdb.d/
-rw-r--r--. 1 1050 993 1563 Oct 16 07:27 01-init-databases.sql
But it was never executed.
Root Cause Analysis
Upon investigation, we found that:
- PostgreSQL 18 creates a version subdirectory automatically on startup:
$ docker exec test-postgres ls -la /var/lib/postgresql/data/
total 12
drwxrwxrwt. 3 postgres postgres 4096 Oct 16 07:36 .
drwxr-xr-x. 1 root root 4096 Sep 30 00:06 ..
drwxr-xr-x. 3 root root 4096 Oct 16 07:36 18 # <-- Created automatically
lrwxrwxrwx. 1 root root 1 Sep 30 00:06 data -> .
- The postgres:18 image has a symlink in
/var/lib/postgresql/
:
$ docker run --rm postgres:18 ls -la /var/lib/postgresql/
lrwxrwxrwx. 1 root root 1 Sep 30 00:06 data -> . # <-- Symlink in base image
- The docker-entrypoint.sh only runs init scripts if the data directory is empty (per documentation)
- Because the
18/
subdirectory is created during initialization, the directory is no longer "empty" - Therefore, initialization scripts are skipped
Version Comparison
This issue does NOT occur with PostgreSQL 17 or earlier versions:
PostgreSQL 17:
$ docker run --rm postgres:17 ls -la /var/lib/postgresql/data/
# Directory starts empty, init scripts execute correctly ✅
PostgreSQL 18:
$ docker run --rm postgres:18 ls -la /var/lib/postgresql/data/
# Directory contains '18/' subdirectory immediately
# Init scripts never execute ❌
Impact
- Severity: Critical
- All initialization scripts are silently ignored
- Users cannot pre-seed databases on first container startup
- Breaking change from PostgreSQL 17 behavior
- No error messages or warnings are shown to indicate scripts were skipped
- Breaks existing docker-compose configurations that worked perfectly with PostgreSQL ≤17
Workaround
Manually execute init scripts after container starts:
docker exec -i test-postgres psql -U postgres < init/01-init.sql
Or add a custom entrypoint wrapper to run scripts manually.
Proposed Fix
Option 1: Modify docker-entrypoint.sh
to check if the data directory contains only the version subdirectory (e.g., 18/
) and the data
symlink, and treat it as "empty" for initialization purposes.
Option 2: Delay the creation of the version subdirectory until after init scripts have been executed.
Option 3: Add a flag or environment variable to force execution of init scripts even when directory is not empty.
Additional Context
- The symlink
data -> .
has existed in previous PostgreSQL versions without causing this issue - The automatic creation of the
18/
subdirectory appears to be new behavior in PostgreSQL 18 - This breaks existing production workflows and CI/CD pipelines
- Spent 3+ hours debugging before identifying root cause
- Multiple users are likely affected but may not realize their init scripts are being silently ignored
Related Issues
- Using volumes for persistent data:'initdb: directory "/var/lib/postgresql/data" exists but is not empty' #263 - Similar "directory not empty" issues
- Initialization script not being executed #693 - Initialization scripts not being executed
Environment Details
PostgreSQL: postgres:18 (latest as of October 2025)
Docker: 24.0+
Docker Compose: 2.40.0