PostgreSQL Docker 컨테이너가 환경 변수대로 초기화되지 않을 때

@givemethatsewon· May 21, 2026 · 4 min read

요약

  • 문제: Docker Compose에서 PostgreSQL 환경 변수를 바꿨는데 Spring Boot 서버의 DB 인증이 계속 실패했다.
  • 원인: 기존 named volume이 재사용되면서 PostgreSQL이 데이터베이스 초기화를 건너뛰었다.
  • 해결: 개발 환경에서는 docker compose down -v로 volume까지 삭제한 뒤 다시 올렸고, 운영 환경에서는 SQL로 사용자/비밀번호를 변경해야 한다.

상황

Docker Compose로 nginx, PostgreSQL, Spring Boot 서버를 한 번에 올리고 있었다. PostgreSQL 컨테이너에는 POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB를 환경 변수로 넘겼고, Spring Boot 서버도 같은 .env 값을 읽도록 구성했다.

version: "3.8"

services:
  nginx:
    container_name: app-proxy
    build:
      context: ./nginx
      dockerfile: Dockerfile
    image: app-proxy:1.0.0-compose
    ports:
      - "80:80"
    networks:
      - app-network

  postgresdb:
    container_name: app-db
    build:
      context: ./postgresDB
      dockerfile: Dockerfile
    image: app-db:1.0.0-compose
    environment:
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: ${DB_NAME}
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - app-network

  web-application-server:
    container_name: app-was
    build:
      context: ./web-application-server
      dockerfile: Dockerfile
    image: app-was:1.0.0-compose
    env_file:
      - .env
    depends_on:
      - postgresdb
    networks:
      - app-network

volumes:
  postgres_data:

networks:
  app-network:
    driver: bridge

겉으로 보기에는 문제가 없어 보였다. PostgreSQL 컨테이너 안에서 env를 확인하면 환경 변수도 들어와 있었다. 그래서 처음에는 Spring Boot 쪽 설정이나 네트워크 문제를 의심했다.

문제

Spring Boot 서버는 계속 PostgreSQL 인증에 실패했다.

app-was | org.hibernate.exception.GenericJDBCException:
app-was |   unable to obtain isolated JDBC connection
app-was |   [FATAL: password authentication failed for user "app_user"]

PostgreSQL 컨테이너에 직접 들어가 사용자 정보와 데이터베이스를 확인해보니, 기대했던 값으로 초기화되어 있지 않았다. 컨테이너에는 환경 변수가 들어와 있는데, 실제 데이터베이스는 그 값을 반영하지 않은 상태였다.

처음에는 이상하게 보였다. 환경 변수가 있으니 PostgreSQL도 그 값으로 생성됐어야 한다고 생각했기 때문이다.

확인한 단서

로그를 자세히 보니 핵심 메시지가 있었다.

app-db | PostgreSQL Database directory appears to contain a database; Skipping initialization

즉, PostgreSQL 컨테이너는 환경 변수를 무시한 것이 아니라 초기화 과정을 아예 다시 실행하지 않은 것이다.

왜 이런 일이 생겼나

PostgreSQL Docker 공식 이미지에서 POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB 같은 Docker 전용 환경 변수는 데이터 디렉터리가 비어 있을 때 초기 데이터베이스를 만들기 위해 사용된다.

처음 컨테이너가 시작될 때 데이터 디렉터리가 비어 있으면 initdb가 실행되고, 이때 기본 사용자와 데이터베이스가 만들어진다. 추가로 /docker-entrypoint-initdb.d/ 아래의 .sql, .sql.gz, .sh 스크립트도 이 초기화 단계에서 실행된다.

반대로 데이터 디렉터리에 이미 PostgreSQL 데이터가 있으면 초기화는 건너뛴다. 이 경우 나중에 .envdocker-compose.yml의 환경 변수를 바꿔도 기존 데이터베이스 사용자나 비밀번호가 자동으로 바뀌지 않는다.

공식 이미지 문서도 같은 점을 명시한다. Docker 전용 환경 변수는 빈 데이터 디렉터리로 컨테이너를 시작할 때만 영향을 주고, 이미 존재하는 데이터베이스는 시작 시 그대로 둔다.

원인

디버깅 과정에서 docker compose up을 여러 번 반복했다. 그 사이 처음 만들어진 named volume인 postgres_data가 계속 재사용되고 있었다.

volumes:
  - postgres_data:/var/lib/postgresql/data

컨테이너를 지웠더라도 named volume은 자동으로 지워지지 않는다. 그래서 새 컨테이너를 띄워도 PostgreSQL 입장에서는 이미 데이터가 있는 디렉터리를 다시 마운트한 상태였다.

결과적으로 PostgreSQL은 다음처럼 판단했다.

  1. /var/lib/postgresql/data에 기존 데이터가 있다.
  2. 이미 초기화된 데이터베이스라고 본다.
  3. POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB 기반 초기화를 생략한다.
  4. 기존 사용자/비밀번호가 남아 있으므로 Spring Boot의 접속 정보와 맞지 않는다.

해결

개발 환경이라 기존 데이터를 보존할 필요가 없었다. 그래서 Compose를 내리면서 volume까지 삭제한 뒤 다시 올렸다.

docker compose down -v
docker compose up --build

-v 옵션은 Compose가 만든 named volume도 함께 제거한다. 이후 새 volume이 만들어지면서 PostgreSQL 데이터 디렉터리가 비어 있는 상태가 되었고, 환경 변수 기반 초기화가 다시 실행됐다.

그 뒤 Spring Boot JPA도 정상적으로 PostgreSQL에 접속했다.

운영할 때 주의할 점

개발 환경에서는 docker compose down -v가 빠른 해결책이지만, 운영이나 공유 개발 DB에서는 매우 위험하다. volume 안의 데이터가 삭제되기 때문이다.

운영 환경에서 사용자나 비밀번호를 바꿔야 한다면 volume을 지우는 방식이 아니라 SQL로 명시적으로 변경해야 한다.

ALTER USER app_user WITH PASSWORD 'new-password';

또는 필요한 사용자를 새로 만들고 권한을 부여해야 한다.

CREATE USER app_user WITH PASSWORD 'new-password';
GRANT ALL PRIVILEGES ON DATABASE app_db TO app_user;

배운 점

PostgreSQL Docker 컨테이너의 환경 변수는 매번 적용되는 설정값이 아니다. 첫 초기화 시점에만 데이터베이스를 생성하기 위한 입력값이다.

그래서 Docker Compose로 DB를 디버깅할 때는 컨테이너만 보는 것이 아니라 volume 상태까지 같이 봐야 한다. docker compose down만으로는 데이터가 초기화되지 않고, named volume이 남아 있으면 PostgreSQL도 기존 데이터베이스를 계속 사용한다.

정리하면 다음 기준을 기억하면 된다.

  • 컨테이너 환경 변수는 현재 컨테이너에 들어온 값이다.
  • PostgreSQL 사용자와 비밀번호는 데이터 디렉터리에 저장된 DB 상태다.
  • POSTGRES_* 환경 변수는 빈 데이터 디렉터리를 처음 초기화할 때만 반영된다.
  • 개발 중 DB를 완전히 새로 만들려면 volume까지 삭제해야 한다.
  • 운영 DB에서는 volume 삭제가 아니라 SQL 마이그레이션/권한 변경으로 처리해야 한다.

참고

givemethatsewon profile
@givemethatsewon
프로젝트를 만들고 운영하면서 배운 개발, 제품, 디버깅 기록을 남깁니다.