A service is one container definition, which Compose 514 can run as one or more containers. BookNest has two: api, built from the Dockerfile of Multi-Stage and BuildKit and Buildx and Multi-Platform, and db, the official PostgreSQL 18 1,289 image. Save this as compose.yaml in the project root:
# BookNest: the API and PostgreSQL 18. Settings come from the environment or a .env file.
name: booknest
services:
api:
build: .
ports:
- "${API_PORT:-3000}:3000"
environment:
PGHOST: db
PGUSER: ${POSTGRES_USER:-booknest}
PGPASSWORD: ${POSTGRES_PASSWORD:-booknest}
PGDATABASE: ${POSTGRES_DB:-booknest}
depends_on:
- db
restart: on-failure
db:
image: postgres:18
environment:
POSTGRES_USER: ${POSTGRES_USER:-booknest}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-booknest}
POSTGRES_DB: ${POSTGRES_DB:-booknest}
ports:
- "127.0.0.1:${DB_PORT:-5432}:5432"
volumes:
- pgdata:/var/lib/postgresql
volumes:
pgdata:image: runs an existing image; build: builds one, here from the current directory as the context. The long form, build: {context: ., dockerfile: Dockerfile, target: test, args: {...}}, selects a stage and passes build arguments, and when a service has both keys, image: names the built image. Without it Compose tags the result <project>-<service>, so this project's API image is booknest-api. The API reads the same PG* variables as before, with the service name db as its host, and PostgreSQL is published on the loopback interface only, so a host-side npm 2,036 test still reaches it. While the old docker-compose.yml exists too, every command warns "Found multiple config files" and uses compose.yaml; Env Files deletes the old file.