A fresh volume holds an empty cluster, and something must create BookNest's table and six books. Two mechanisms can, and they behave differently.
The postgres entrypoint initializes an empty data directory: it runs initdb, creates POSTGRES_USER and POSTGRES_DB, then executes every *.sql, *.sql.gz and *.sh file in /docker-entrypoint-initdb.d/ in alphabetical order, inside the temporary socket-only server of Healthchecks. It does so only when the data directory is empty; later starts ignore the scripts, so editing one changes nothing until the volume is deleted. It suits database-level setup such as an extra read-only role, mounted into that directory as a file or a Compose 514 config (Secrets and Configs in Compose).
The application owns its schema. BookNest's db.init() in db/index.js runs on every API start: CREATE TABLE IF NOT EXISTS, then an insert of db/seed.json only if the table is empty. It is idempotent, so restarts never duplicate rows, and BookNest needs no init scripts. A larger project replaces it with a migration tool (node-pg-migrate 1,487 , Knex 922,339 , Prisma 30,571 Migrate, Flyway 14,820 or Liquibase 51,363 ) run as a one-shot service that api waits for with condition: service_completed_successfully, so two API replicas never race to migrate one schema. The next subsection shows both mechanisms at work.