Docker compose stop vs down 차이점 완벽 정리
docker compose stop은 컨테이너를 유지하고 down은 컨테이너와 네트워크를 삭제합니다. 두 명령어 모두 명명된 볼륨은 삭제하지 않으며, 데이터까지 완전히 제거하려면 --volumes 플래그를 사용해야 합니다. 각 명령어의 동작 방식과 데이터 보존 여부를 상세히 설명합니다.
간단한 답변
docker compose stop은 컨테이너를 중지하고 디스크에 그대로 남겨둡니다. docker compose down은 컨테이너를 중지한 뒤, 해당 프로젝트를 위해 생성된 컨테이너와 네트워크를 삭제합니다. 두 명령어 모두 명명된 볼륨(named volume)에는 영향을 주지 않습니다. 데이터베이스가 삭제되는 경우는 -v를 추가했을 때뿐이며, docker compose down -v과 같이 사용하면 Compose 파일의 volumes 섹션에 선언된 명명된 볼륨까지 제거됩니다.
이것이 전체 차이점에 대한 한 문단 요약입니다. 이 가이드의 나머지 부분에서는 Postgres 볼륨을 사용하여 down 이후에도 데이터가 유지되는 모습과 down -v 실행 시 데이터가 사라지는 과정을 직접 확인하며, --force-recreate이 필요한 두 가지 경우를 설명합니다.
docker compose stop: 컨테이너는 유지됩니다
stop은 각 컨테이너의 메인 프로세스에 SIGTERM 신호를 보내고 대기한 뒤, 프로세스가 여전히 살아있으면 SIGKILL 신호를 보냅니다. 기본 대기 시간은 10초이며 -t를 통해 변경할 수 있습니다. 아무것도 삭제되지 않습니다. 컨테이너는 자신의 ID, 쓰기 가능한 레이어, IP 예약, 로그를 그대로 유지합니다.
docker compose stop
docker compose ps -adocker compose ps은 기본적으로 실행 중인 컨테이너만 보여주므로, stop을 실행한 후에는 빈 테이블이 출력되어 컨테이너가 사라졌다고 오해하기 쉽습니다. ps -a는 중지된 컨테이너까지 포함하며, 여기서 각 서비스 옆에 Exited (0)이 표시된 것을 확인할 수 있습니다. docker compose start를 사용하면 동일한 컨테이너를 재사용하여 다시 시작할 수 있습니다.
컨테이너가 여전히 존재하기 때문에 볼륨 외부의 컨테이너 내부에 작성된 모든 데이터는 그대로 남아 있습니다. 여기에는 docker compose exec를 사용하여 수동으로 설치한 패키지나 컨테이너 내부에서 수정한 설정 파일이 포함됩니다. 이것이 디버깅 중에 stop을 선호하는 실질적인 이유입니다. 동일한 상태로 다시 시작할 수 있기 때문입니다.
docker compose down: 컨테이너와 네트워크 제거
down은 컨테이너를 중지한 뒤, 해당 프로젝트를 위해 Compose가 생성한 기본 네트워크와 함께 컨테이너를 제거합니다. Docker 문서에서는 이를 컨테이너 중지 및 up로 생성된 컨테이너, 네트워크, 볼륨, 이미지를 제거하는 것으로 설명하지만, 볼륨과 이미지 제거는 -v 및 --rmi 옵션을 명시적으로 사용할 때만 수행됩니다.
docker compose down
docker compose ps -a
docker network lsdown을 실행한 후, ps -a는 해당 프로젝트에 대해 아무것도 출력하지 않으며 <project>_default 네트워크도 사라집니다. 프로젝트 이름은 Compose 파일에 name:를 설정하거나 -p를 전달하지 않는 한 디렉터리 이름에서 가져옵니다. 컨테이너의 쓰기 가능 레이어 내에서 수행한 모든 변경 사항은 이제 복구할 수 없으므로, down은 컨테이너를 폐기하고 볼륨에 저장된 데이터만 유지하는 명령어로 이해해야 합니다.
잘못된 디렉터리에서 실행하면 no configuration file provided: not found 오류가 발생합니다. Compose는 의도한 프로젝트가 무엇인지 알 수 없으므로 작업을 거부합니다. 프로젝트 폴더에 있지 않을 때는 docker compose -f /srv/myapp/compose.yaml down을 사용하십시오.
docker compose down 명령을 실행하면 볼륨이 삭제됩니까?
아니요. 최상위 volumes 키 아래에 선언된 명명된 볼륨(named volume)은 down 이후에도 유지되며, 해당 볼륨이 연결되었던 컨테이너가 삭제된 후에도 데이터는 남아 있습니다. 이는 해당 명령과 관련하여 가장 흔히 하는 걱정이며, 이 동작은 Compose v2 전반에서 동일하게 유지됩니다.
테스트할 스택을 구성해 봅니다. voltest라는 빈 디렉터리를 만들고 그 안에 다음 내용을 compose.yaml 파일로 저장합니다.
services:
db:
image: postgres:16.4
restart: unless-stopped
environment:
POSTGRES_PASSWORD: example
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:스택을 시작하고 나중에 확인할 수 있는 행을 기록합니다.
docker compose up -d
sleep 10
docker compose exec -T db psql -U postgres -c "create table marker (note text);"
docker compose exec -T db psql -U postgres -c "insert into marker values ('survived');"이제 컨테이너를 삭제하고 볼륨을 확인합니다.
docker compose down
docker volume ls출력 결과에 여전히 voltest_pgdata이 표시됩니다. 컨테이너는 사라졌지만 데이터는 남아 있습니다. 스택을 다시 시작하고 해당 행을 읽어 봅니다.
docker compose up -d
sleep 10
docker compose exec -T db psql -U postgres -c "select * from marker;"survived가 포함된 한 행이 출력됩니다. 새로 생성된 컨테이너는 이전과 다른 ID를 가진 별개의 컨테이너이지만, 동일한 볼륨에 연결되어 있습니다. 더 자세한 내용을 알고 싶다면 Compose 기초 가이드에서 명명된 볼륨과 바인드 마운트의 차이점, 그리고 각 데이터가 호스트의 어디에 저장되는지 확인할 수 있습니다.
docker-compose down -v 명령이 정확히 삭제하는 것
-v (전체 명령어 --volumes)는 Compose 파일의 volumes 섹션에 선언된 명명된 볼륨과 컨테이너에 연결된 익명 볼륨을 모두 제거합니다. 동일한 스택에 대해 이 명령을 실행해 보십시오.
docker compose down -v
docker volume lsvoltest_pgdata가 더 이상 목록에 나타나지 않습니다. 스택을 다시 시작하면 Postgres 엔트리포인트가 비어 있는 데이터 디렉터리를 발견하고 새로운 클러스터를 초기화합니다. 컨테이너 로그에 이 내용이 명확하게 기록됩니다.
The files belonging to this database system will be owned by user "postgres".
PostgreSQL init process complete; ready for start up.수개월 동안 실행 중이던 스택에서 해당 로그를 확인했다면 볼륨이 제거되었다는 의미입니다. marker 테이블은 사라졌으며, 이를 복구하는 유일한 방법은 백업본을 사용하는 것뿐입니다.
일부 스토리지는 -v 명령으로도 제거되지 않습니다. 바인드 마운트는 호스트 경로를 사용하므로 Docker는 마운트만 해제하며 파일은 원래 위치에 그대로 남습니다. external: true로 표시된 볼륨은 이 프로젝트 외부의 리소스로 선언되므로 Compose는 이를 제거하지 않습니다. down -v를 실행하기 전에 Compose 파일에서 삭제한 명명된 볼륨은 더 이상 선언된 상태가 아니므로, Compose는 해당 볼륨을 제거해야 할 대상으로 인식하지 못하며 결과적으로 docker volume prune 상태의 고아 볼륨으로 남게 됩니다.
마지막 사례는 리팩터링 과정에서 자주 발생합니다. 파일에서 서비스와 볼륨을 제거한 뒤 down -v를 실행하면, 파일에 해당 볼륨에 대한 언급이 없기 때문에 볼륨은 삭제되지 않고 살아남습니다. 파일을 수정하기 전, 즉 수정하기 전에 down -v를 실행해야 합니다.
--force-recreate가 실제로 필요한 경우
docker compose up -d은 매번 전체를 다시 빌드하지 않습니다. Compose는 각 서비스의 해석된 설정을 해시값으로 변환하여 컨테이너에 레이블로 저장합니다. 해시값과 이미지 ID가 일치하면 컨테이너는 그대로 유지되며 Recreated 대신 Container voltest-db-1 Running 메시지가 출력됩니다. 이 동작은 up -d를 반복적으로 실행해도 안전하게 만들어 주므로 거의 모든 상황에서 권장되는 방식입니다.
일부 수정 사항이 적용되지 않는 것처럼 보이는 이유도 여기에 있습니다. Compose는 정의가 가리키는 파일의 내용이 아니라 해석된 서비스 정의 자체를 해시합니다. 컨테이너에 마운트되어 시작 시점에 한 번 읽히는 설정 파일은 내용을 수정해도 마운트 경로가 바뀌지 않으므로 재생성(recreate)을 유발하지 않습니다. 서비스는 부팅 시 읽은 값으로 계속 실행됩니다.
docker compose up -d --force-recreate이 명령은 각 컨테이너를 중지하고 제거한 뒤 동일한 정의를 사용하여 새 컨테이너를 생성합니다. 마운트된 설정 파일을 수정한 후나, 컨테이너가 원인을 알 수 없는 상태로 변했을 때 사용하십시오. 볼륨은 건드리지 않으므로 데이터베이스는 강제 재생성 후에도 유지됩니다. 동일한 태그의 최신 이미지를 가져오려면 pull 작업도 함께 수행해야 합니다.
docker compose pull
docker compose up -dpull은 새로운 이미지 ID를 가져오며, up -d은 실행 중인 컨테이너와 다른 이미지 ID를 감지하여 자동으로 컨테이너를 재생성합니다. pull 없이 --force-recreate만 추가하면 기존과 동일한 구버전 이미지로 새 컨테이너가 생성될 뿐입니다. "강제 재생성을 했는데도 여전히 구버전이다"라는 불만이 자주 발생하는 이유가 바로 이것입니다.
docker compose restart는 이러한 작업을 전혀 수행하지 않습니다. 기존 컨테이너를 재시작할 뿐 Compose 파일을 다시 읽지 않으므로, 변경된 환경 변수나 포트 매핑은 적용되지 않습니다. 파일을 수정했다면 up -d를 사용하십시오.
유지해야 할 사고방식
컨테이너는 대체 가능합니다. 컨테이너는 프로세스와 얇은 쓰기 가능 계층의 결합이며, Compose는 저장소 내 파일을 사용하여 약 1초 만에 동일한 컨테이너를 빌드할 수 있습니다. 볼륨은 대체할 수 없습니다. 볼륨에는 저장소 내 어떤 파일로도 재생성할 수 없는 유일한 상태 데이터가 저장되기 때문입니다.
모든 Compose 명령어는 이러한 구분과 일치합니다. stop 및 start은 컨테이너를 유지합니다. down 및 up는 컨테이너를 교체하지만 볼륨은 유지합니다. down -v은 상태 데이터를 삭제하는 유일한 일상적 명령어이므로 명시적인 플래그가 필요합니다. 실제 운영 환경에서 이 명령어를 입력하기 전에, 최소 한 번 이상 복원해 본 백업이 있는지 확인하십시오.
동일한 논리가 비밀값(secrets)에도 적용됩니다. POSTGRES_PASSWORD을 통해 설정된 비밀번호는 데이터베이스가 처음 초기화될 때만 읽힙니다. 따라서 환경 파일에서 비밀번호를 변경하고 up -d를 실행해도 password authentication failed for user "postgres" 결과가 나타납니다. 컨테이너는 새로 생성되지만 볼륨은 이전 상태를 유지하며, 이전 볼륨에는 여전히 기존 비밀번호가 저장되어 있기 때문입니다. Compose가 환경 파일과 비밀값을 처리하는 방식에서는 동일한 변수가 두 번 설정될 때 어떤 계층이 우선하는지 설명합니다.
실패 유형 및 표시되는 메시지
no configuration file provided: not found는 compose.yaml나 docker-compose.yml이 없는 디렉터리에서 Compose를 실행 중임을 의미합니다. 전체 경로와 함께 -f을 전달하십시오.
down에서 발생하는 network voltest_default has active endpoints은 이 프로젝트 외부의 컨테이너가 프로젝트 네트워크에 연결되어 있음을 의미하며, 보통 docker run --network으로 수동 실행한 컨테이너가 원인입니다. 해당 컨테이너를 제거한 뒤 down을 다시 실행하십시오.
Found orphan containers ([voltest-old-1]) for this project는 서비스를 이름을 바꾸거나 삭제한 뒤에 나타납니다. 이전 컨테이너가 여전히 프로젝트 레이블을 가지고 있기 때문입니다. docker compose down --remove-orphans은 이를 정리하며, 정상적인 스택에서 실행해도 안전합니다.
수동 docker volume rm에서 발생하는 Error response from daemon: remove voltest_pgdata: volume is in use는 중지된 컨테이너를 포함하여 여전히 해당 볼륨을 참조하는 컨테이너가 있음을 의미합니다. 먼저 docker compose down을 실행한 뒤 볼륨을 제거하거나, 단순히 down -v을 사용하십시오. 규모가 큰 프로젝트의 경우 다중 서비스 Compose 스택에서 하나의 프로젝트에 얼마나 많은 볼륨이 쌓일 수 있는지 확인할 수 있습니다.
FAQ
docker compose down 명령을 실행하면 데이터베이스가 삭제됩니까?
데이터베이스가 명명된 볼륨(named volume)이나 바인드 마운트(bind mount)에 저장되어 있다면 삭제되지 않습니다. down은 컨테이너와 프로젝트 네트워크를 제거하지만, 볼륨은 데이터가 보존된 상태로 디스크에 남습니다. 이후 docker compose up -d를 실행하면 새 컨테이너가 동일한 볼륨에 연결되어 데이터를 그대로 사용할 수 있습니다. 명명된 볼륨은 오직 docker compose down -v 명령으로만 제거되며, 이 또한 Compose 파일의 volumes 섹션에 선언된 볼륨만 해당합니다.
다시 사용할 컨테이너를 다룰 때 stop과 down의 차이는 무엇입니까?
stop는 컨테이너를 유지하므로, docker compose start을 실행하면 동일한 쓰기 가능 레이어(writable layer)를 가진 원래의 컨테이너로 돌아갑니다. 컨테이너 내부에서 수동으로 설치하거나 수정한 내용은 그대로 유지됩니다. 반면 down는 컨테이너를 삭제하므로, 이후 up -d를 실행하면 이미지로부터 새로운 컨테이너가 생성되어 수동으로 변경한 내용은 모두 사라집니다. 디버깅 중이라면 stop을 사용하는 것이 좋습니다.
Compose 프로젝트가 생성한 모든 것을 제거하려면 어떻게 해야 합니까?
docker compose down -v --rmi all --remove-orphans은 컨테이너, 프로젝트 네트워크, 파일에 선언된 명명된 볼륨, 서비스가 사용한 이미지, 그리고 프로젝트 이름이 라벨링된 모든 컨테이너를 제거합니다. 이 명령은 바인드 마운트나 external: true로 표시된 볼륨에는 영향을 주지 않습니다. 명령을 실행하기 전에 docker volume ls를 사용하여 삭제될 항목을 미리 확인하십시오.
마운트된 설정 파일을 변경했는데 왜 컨테이너에 반영되지 않습니까?
Compose는 해석된 서비스 정의의 해시값을 비교하여 컨테이너를 다시 생성할지 결정하는데, 이 해시값에는 마운트된 파일의 내용은 포함되지 않습니다. 파일 경로가 변경되지 않았으므로 Compose는 컨테이너를 그대로 유지하며, 컨테이너는 시작 시점에 읽은 값을 계속 사용합니다. docker compose up -d --force-recreate을 실행하여 파일을 다시 읽는 새로운 컨테이너를 생성하십시오.
POSTGRES_PASSWORD를 변경했는데 왜 적용되지 않습니까?
Postgres 이미지는 데이터 디렉터리가 비어 있을 때만 POSTGRES_PASSWORD을 읽습니다. 볼륨에 이미 초기화된 클러스터가 존재하므로 해당 변수는 무시되며 기존 비밀번호가 유지됩니다. 이때 password authentication failed for user "postgres" 로그가 출력됩니다. 실행 중인 데이터베이스 내부에서 ALTER USER 명령을 사용하여 비밀번호를 변경하거나, 데이터를 삭제해도 괜찮다면 docker compose down -v를 사용하여 처음부터 다시 시작하십시오.