Docker Compose 실무 필수 명령어 정리
Docker Compose V2 환경에서 서버 관리에 꼭 필요한 명령어 12가지를 정리했습니다. 컨테이너 생명 주기 관리부터 로그 확인, 네트워크 및 볼륨 정리까지 실무에서 자주 사용하는 핵심 옵션과 주의사항을 확인하여 운영 효율을 높여보시기 바랍니다.
실무에서 자주 사용하는 Compose 명령어
Docker Compose는 40개가 넘는 하위 명령어를 제공합니다. 서버 관리 업무에서는 그중 12개 정도만 주로 사용합니다. 이 페이지에서는 작업 목적에 따라 명령어를 분류하고, 각 명령어의 사용 이유를 간략히 설명하며, 주의가 필요한 명령어는 심층 분석 문서로 연결합니다.
이 문서의 모든 내용은 Compose V2를 기준으로 합니다. 즉, 하이픈이 없는 docker compose를 사용하며, 구형인 docker-compose 스크립트는 사용하지 않습니다. V2는 Docker Engine과 함께 설치되는 Go 플러그인입니다. 현재 패키지에서는 V1이 제거되었으므로, 2026년 7월 기준 최신 Ubuntu 환경에서 docker-compose: command not found을 실행하면 오류 없이 정상 작동해야 합니다. docker compose version 명령어로 설치 여부를 확인하십시오. 만약 아무런 결과가 출력되지 않는다면 docker-compose-plugin 패키지를 설치해야 합니다.
아래의 모든 명령어는 compose.yaml 파일이 위치한 디렉터리에서 실행해야 합니다. Compose는 해당 디렉터리 이름으로 프로젝트명을 결정하고, 그 경로를 기준으로 파일을 찾기 때문입니다. 상위 디렉터리에서 동일한 명령어를 실행하면 Compose는 no configuration file provided: not found 오류를 내며 중단됩니다. 파일 형식이 생소하다면 VPS에서 첫 Compose 파일 작성하기를 먼저 읽고 돌아와 명령어를 확인하십시오.
생명 주기: 실행을 위한 네 가지 명령어와 컨테이너를 제거하는 명령어
docker compose up -d
docker compose up -d --wait
docker compose stop
docker compose start
docker compose restart web
docker compose downup -d은 네트워크를 생성하고 컨테이너를 생성한 뒤 실행하고 즉시 반환합니다. 컨테이너가 생성되는 즉시 반환되기 때문에, 이어서 curl로 상태를 확인하는 배포 스크립트는 첫 시도에서 실패하는 경우가 많습니다. up -d --wait은 healthcheck를 선언한 모든 서비스가 정상 상태를 보고할 때까지 차단하며, 하나라도 정상 상태에 도달하지 못하면 0이 아닌 종료 코드를 반환합니다. 이 플래그는 배후의 검사 로직만큼만 유효하므로, 자동화에 의존하기 전에 Compose가 신뢰할 수 있는 healthcheck를 먼저 작성하십시오.
stop는 컨테이너를 중지하고 유지하므로, start을 실행하면 동일한 쓰기 가능 계층을 가진 컨테이너가 다시 시작됩니다. down은 컨테이너를 중지한 후 컨테이너와 프로젝트 네트워크를 모두 제거합니다. 볼륨 외부의 컨테이너 내부에 기록된 모든 데이터는 함께 삭제됩니다. 이는 Compose에서 가장 비용이 큰 오해이며, down과 stop의 전체적인 차이에서 이러한 문제가 발생하는 지점을 다룹니다.
restart는 설정 다시 불러오기(reload)가 아닙니다. 이미 존재하는 설정으로 동일한 컨테이너를 중지했다가 다시 시작할 뿐이므로, 환경 변수 변경, 새로운 이미지 태그, 포트 매핑 수정 등은 전혀 반영되지 않습니다. 파일 변경 사항을 적용하려면 up -d을 다시 실행해야 합니다. Compose는 각 서비스와 실행 중인 컨테이너를 비교하여 설정이 변경된 컨테이너만 다시 생성합니다.
변경 사항 적용: 재생성, 풀(pull), 또는 재빌드
docker compose up -d --force-recreate
docker compose pull && docker compose up -d
docker compose build --no-cache web
docker compose up -d --build webup -d은(는) 변경 사항이 없을 때 아무런 동작도 하지 않으므로 반복해서 실행해도 안전합니다. --force-recreate은(는) 해당 비교 과정을 무시하고 설정이 동일하더라도 모든 컨테이너를 교체하므로, 컨테이너 내부의 비정상적인 상태를 초기화하는 가장 빠른 방법입니다.
이미지를 업데이트하려면 서로 다른 작업을 수행하는 2개의 명령이 필요하다. pull는 파일에 지정된 각 태그의 최신 이미지를 다운로드한다. up -d는 서비스의 이미지 ID가 실행 중인 컨테이너의 이미지 ID와 더 이상 일치하지 않는 것을 확인한 다음 컨테이너를 다시 생성한다. pull을 건너뛰면 up -d는 지난달의 latest를 오류 없이 계속 실행한다. 반대의 위험은 여러 서비스로 구성된 stack에서 발생한다. 모든 서비스에 대해 한 번에 latest를 pull하면 10초 전까지 정상적으로 작동하던 애플리케이션이 중단될 수 있다. 따라서 self-hosted AFFiNE workspace는 4개의 이미지 태그를 각각 고정한다. 태그 고정은 업그레이드를 태그를 의도적으로 수정한 다음 동일한 pull 및 recreate를 수행하는 작업으로 바꾼다. 부팅 과정에서 database를 마이그레이션하는 stack에서는 두 명령을 실행하기 전에 dump를 준비해야 한다. self-hosted Chatwoot support desk는 모든 버전 업그레이드에서 이 절차를 따른다.
build은(는) image: 대신 build: 섹션을 선언하는 서비스에 적용됩니다. up -d --build은(는) 빌드와 시작을 한 번에 처리하며, 코드를 수정하는 동안 사용하는 일반적인 반복 작업입니다. --no-cache은(는) 캐시된 레이어가 명확히 오래된 경우에만 사용하십시오. 모든 레이어를 처음부터 다시 빌드하기 때문입니다. 레지스트리 이미지가 아닌 체크아웃된 git 태그에서 스택을 배포하는 경우, 동일한 빌드 루프가 업데이트 경로가 됩니다. 이것이 바로 자체 호스팅 openGym 운동 추적기가 고정된 버전에서 다음 버전으로 이동하는 방식입니다.
실행 중인 서비스 확인하기
docker compose ps
docker compose ps -a
docker compose logs -f --tail=100
docker compose logs --since 15m --timestamps db
docker compose top
docker compose lsps은(는) 실행 중인 컨테이너만 나열합니다. 시작 도중 충돌한 서비스는 -a 옵션을 추가하기 전까지는 보이지 않습니다. 따라서 ps에는 없지만 ps -a에서는 Exited (1) 상태로 표시되는 컨테이너는 일반적인 시작 실패 사례입니다. 종료 코드를 확인한 뒤 로그를 읽으십시오.
logs -f은(는) 모든 서비스의 로그를 동시에 추적하며 각 줄 앞에 서비스 이름을 표시합니다. 서비스 간 통신이 이루어지고 이벤트 순서가 중요할 때 유용한 보기 방식입니다. 서비스 이름을 지정하면 범위를 좁힐 수 있습니다. --tail=100 옵션은 한 달 동안 실행된 컨테이너에서 중요합니다. 기본값은 전체 기록을 출력하여 터미널을 가득 채우기 때문입니다. --since 15m 옵션은 방금 수행한 재시작 중에 무슨 일이 일어났는지 확인하려는 일반적인 상황에 적합합니다.
top은(는) 각 컨테이너 내부의 프로세스를 나열합니다. 이를 통해 "컨테이너가 실행 중임"과 "내부 프로세스가 실행 중임"을 구분할 수 있습니다. ls은(는) 현재 디렉터리를 벗어나 호스트의 모든 Compose 프로젝트와 그 상태를 나열하므로, 3개월 전에 시작한 스택을 찾을 수 있습니다.
서비스 내부 셸 접근하기
docker compose exec web sh
docker compose exec -u root web sh
docker compose run --rm web env
docker compose run --rm --no-deps web shexec은 이미 실행 중인 컨테이너 내부에서 명령을 실행합니다. run은 동일한 서비스 정의를 사용하여 새로운 컨테이너를 시작하는데, 서비스가 짧게 실행되어 exec으로 접근할 수 없을 때 유용합니다. 항상 run과 --rm를 함께 사용하십시오. 이를 생략하면 호출할 때마다 중지된 컨테이너가 남게 되며, 이들이 쌓이면 docker compose ps -a의 가독성이 떨어집니다.
bash를 사용하기 전에 sh을 먼저 시도하십시오. Alpine 기반 이미지에는 bash이 포함되어 있지 않으며, 실행 시 exec: "bash": executable file not found in $PATH 오류가 발생합니다. run에 --no-deps를 추가하면 서비스의 의존성을 건너뜁니다. 이를 통해 전체 데이터베이스를 부팅하지 않고도 빠르게 설정을 확인할 수 있습니다.
run --rm web env은 모든 .env 파일, environment: 블록, 셸 변수가 병합된 후 서비스가 실제로 적용받는 환경을 확인하는 가장 빠른 방법입니다. 값이 잘못된 경우, 대개 병합 순서가 원인이며 Compose가 환경 파일과 비밀 값을 해석하는 방법에서 어떤 소스가 우선순위를 갖는지 확인할 수 있습니다.
네트워크, 포트 및 이름 확인
docker compose port web 80
docker compose exec web getent hosts db
docker compose config --networksCompose는 모든 서비스를 하나의 프로젝트 네트워크에 연결하며, 각 서비스 이름은 해당 네트워크에서 DNS 이름으로 사용된다. web 안에서 getent hosts db를 실행하면 이름 확인이 정상일 때 컨테이너 IP가 출력되고, 그렇지 않으면 아무것도 출력되지 않는다. 따라서 2초 안에 “이 컨테이너들이 서로 통신할 수 있는가”를 확인할 수 있다. 이름은 확인되지만 연결이 거부된다면 db 내부의 프로세스가 0.0.0.0가 아니라 127.0.0.1에 바인딩된 것이다. 그러면 다른 컨테이너에서 들어오는 패킷을 수락하지 않는다. 이 경계 때문에 docker run로 시작했거나 독립적인 스택으로 실행한 컨테이너처럼 프로젝트 외부에서 시작한 컨테이너는 jellyfin 같은 이름을 전혀 확인할 수 없다. 이는 Jellyfin 라이브러리용 Halcyon 프런트엔드가 지정된 서버에 연결하지 못할 때 가장 먼저 확인할 항목이다. 이 모델의 나머지 내용은 Compose 네트워크와 서비스 DNS의 작동 방식에서 확인할 수 있다.
port web 80는 컨테이너 포트가 게시된 호스트 주소와 포트를 출력하므로, 매핑이 변수에서 비롯된 경우 추측할 필요가 없습니다. 포트를 게시하면 Docker가 자체적으로 관리하는 방화벽 규칙이 생성되는데, 이 규칙은 사용자가 설정한 규칙보다 우선 적용됩니다. 따라서 비공개라고 생각했던 서비스가 인터넷에 노출될 수 있습니다. 이 사례는 게시된 Docker 포트가 ufw를 우회하는 이유에서 다룹니다. 포트를 게시하지 않고 프로젝트 네트워크상의 서비스 앞에 인증 프록시를 하나 두는 것이 더 안전한 구성이며, 이는 Authentik을 싱글 사인온 계층으로 실행하기를 통해 구현할 수 있습니다.
볼륨과 데이터
docker compose config --volumes
docker compose cp db:/etc/postgresql/pg_hba.conf ./pg_hba.conf
docker compose down -vconfig --volumes은 프로젝트에 선언된 명명된 볼륨을 한 줄에 하나씩 출력합니다. 이 목록이 바로 백업 대상입니다. 볼륨에 대체 불가능한 데이터가 저장되어 있다면, 목록만큼이나 정확한 백업 명령어가 중요합니다. 이것이 바로 PhotoPrism과 Immich 비교 문서에서 각 사진 서버에 필요한 덤프 및 복사 명령어를 상세히 다루는 이유입니다. cp은 셸을 열지 않고도 컨테이너 내부나 외부로 파일을 복사하며, 컨테이너 측 경로에는 service:path 형식을 사용합니다.
down -v는 컨테이너와 함께 해당 명명된 볼륨을 삭제합니다. 이 명령어는 테스트 스택을 제거할 때는 적합하지만, 중요한 데이터가 들어있는 경우에는 사용해서는 안 됩니다. 확인 절차가 없으며 되돌릴 수도 없기 때문입니다. 바인드 마운트는 호스트 파일 시스템에 존재하므로 이 명령의 영향을 받지 않습니다. 이러한 영향 범위의 차이는 바인드 마운트와 명명된 볼륨 중 하나를 신중하게 선택해야 하는 이유 중 하나입니다.
데이터 손실 없이 디스크 공간을 확보하는 정리 작업
docker compose down --remove-orphans
docker system df
docker image prune -a
docker builder prune--remove-orphans은(는) 프로젝트에는 속해 있으나 파일에는 더 이상 나타나지 않는 컨테이너를 삭제합니다. 이는 서비스를 이름을 바꾼 후에 발생하는 상황과 정확히 일치합니다. 이 명령을 사용하지 않으면 해당 컨테이너는 계속 실행되지만 docker compose ps에서는 보이지 않게 됩니다.
docker system df은(는) 삭제를 수행하기 전에 디스크 공간이 어디에 사용되고 있는지 보여줍니다. 이미지, 컨테이너, 로컬 볼륨, 빌드 캐시를 구분하여 각각 회수 가능한 용량을 표시합니다. image prune -a은(는) 태그가 지정되지 않은 모든 이미지를 제거합니다. 대용량 이미지의 여러 버전을 내려받은 서버라면 보통 이 작업으로 가장 많은 공간을 확보할 수 있습니다. builder prune은(는) 빌드 캐시를 삭제합니다. 빌드 캐시는 자체 이미지를 빌드하는 모든 서버에서 조용히 용량을 차지하며 증가합니다.
위의 명령들은 명명된 볼륨(named volume)에는 영향을 주지 않습니다. 오직 docker volume prune와(과) docker compose down -v만이 볼륨을 삭제합니다.
문제가 발생하기 전 파일 검사하기
docker compose config --quiet
docker compose config --services
docker compose --dry-run up -dconfig --quiet은(는) 성공 시 아무것도 출력하지 않고 검증만 수행하므로, 배포 전 단계나 git hook에 포함하는 것이 적합합니다. 단순히 config을(를) 실행하면 병합 및 보간이 완료된 전체 파일이 출력되는데, 이를 통해 변수가 올바르게 해석되었는지, override 파일이 의도한 대로 적용되었는지 확인할 수 있습니다. 설정되지 않은 변수는 경고 메시지 The "X" variable is not set. Defaulting to a blank string.와 함께 빈 값으로 표시됩니다.
--dry-run은(는) 하위 명령 플래그가 아닌 전역 플래그이므로 up 앞에 위치해야 합니다. 이 플래그는 Compose가 수행할 모든 작업을 출력하되 실제 변경은 가하지 않으므로, 중요한 스택에 down을(를) 실행하기 전에 30초 정도 시간을 투자하여 확인하는 것이 좋습니다.
파일, 프로필, 프로젝트 간 작업
docker compose -f compose.yaml -f compose.prod.yaml up -d
docker compose --profile debug up -d
docker compose -p staging up -d여러 개의 -f 플래그는 순서대로 병합되며, 나중에 지정된 파일이 앞선 파일의 설정을 키 단위로 덮어씁니다. 이것이 기본 설정 파일 하나와 운영 환경용 오버라이드 파일을 작게 유지하는 표준적인 방법입니다. 다만 리스트와 맵에 대한 병합 규칙은 다르므로, 예상치 못한 동작을 디버깅하기 전에 Compose가 여러 파일을 병합하는 방법을 먼저 읽어보시기 바랍니다.
--profile는 해당 프로필이 지정된 서비스를 태그가 없는 서비스와 함께 실행하며, 이를 통해 디버깅 도구를 일반적인 up 환경에서 분리할 수 있습니다. -p은 프로젝트 이름을 설정하므로, 동일한 스택의 복사본 두 개를 각각 별도의 네트워크와 볼륨 이름을 사용하여 동시에 실행할 수 있습니다. 재부팅 후 스택을 다시 복구하는 것은 사용자가 직접 입력하는 명령어가 아니라, 부팅 시 Compose 스택 시작하기에 설명된 대로 시스템이 대신 실행해 주는 유닛입니다.
FAQ
하이픈이 포함된 docker-compose은 무엇으로 대체되었습니까?
공백을 포함한 docker compose로 호출되는 Compose V2입니다. 이는 Docker Engine에 포함된 플러그인이며, 현재 패키지에서는 V1 Python 도구가 더 이상 설치되지 않습니다. 공백을 포함한 명령어가 아무것도 출력하지 않는다면 배포판에 맞는 docker-compose-plugin 패키지를 설치하십시오. V2에는 V1에 없던 플래그가 존재하므로, 별칭(alias)을 추가하기보다는 기존 스크립트를 공백을 포함한 형식으로 업데이트하십시오.
docker compose restart이 설정 변경 사항을 반영하지 않는 이유는 무엇입니까?
restart은 기존 컨테이너를 생성 당시의 설정으로 중지하고 시작할 뿐, compose.yaml를 다시 읽지 않습니다. 환경 변수, 포트, 볼륨 또는 이미지 태그를 변경하려면 docker compose up -d을 사용해야 합니다. 이 명령어는 각 서비스를 실행 중인 컨테이너와 비교하여 차이가 있는 컨테이너를 다시 생성합니다. 파일 내용이 변경되지 않았더라도 교체를 강제하려면 --force-recreate를 추가하십시오.
서비스를 최신 이미지로 업데이트하려면 어떻게 해야 합니까?
docker compose pull를 실행한 다음 docker compose up -d을 실행하십시오. pull 명령어는 파일에 명시된 각 태그의 최신 이미지를 가져오며, up -d은 이미지 ID가 컨테이너와 일치하지 않는 모든 서비스를 다시 생성합니다. up -d만 단독으로 실행하면 디스크에 이미 존재하는 이미지를 재사용합니다. 이것이 바로 latest로 고정된 스택이 오류 메시지 없이 수개월 전 빌드 상태로 유지되는 이유입니다.
운영 중인 서버에서 안전하게 실행할 수 있는 정리 명령어는 무엇입니까?
docker system df, docker image prune -a 및 docker builder prune는 이미지와 캐시만 제거하므로 실행 중인 서비스는 계속 작동하며 명명된 볼륨(named volumes)은 영향을 받지 않습니다. 위험한 명령어 조합은 docker compose down -v과 docker volume prune이며, 이는 확인 절차 없이 명명된 볼륨을 삭제합니다. 위험 요소를 파악하려면 먼저 docker compose config --volumes를 실행하십시오.
전체 스택을 시작하지 않고 하나의 명령어만 실행할 수 있습니까?
예, 가능합니다. docker compose run --rm --no-deps web sh은 web 서비스 정의에서 단일 컨테이너를 시작하고 의존성 서비스를 건너뛰며, 종료 시 컨테이너를 제거합니다. 컨테이너가 이미 실행 중인 경우에는 exec을 대신 사용하십시오. exec는 실행 중인 프로세스에 연결하여 서비스의 실제 상태를 보여줍니다.