Docker Compose는 여러 옵션이 붙은 긴 docker run 명령을 compose.yaml 파일 한 개로 정리하고, docker compose up -d 한 줄로 실행하게 해 주는 도구입니다. 지금 쓰는 Compose는 Docker CLI에 붙는 플러그인 방식이라 명령도 하이픈 없이 docker compose로 입력합니다. 이 글에서는 설치 확인부터 nginx 웹서버 하나를 띄워 보는 첫 compose 파일 작성, 자주 쓰는 명령과 오류 해결까지 순서대로 정리했습니다.

아직 Docker를 설치하지 않았다면 이전 글 Ubuntu Docker 설치 방법: 공식 저장소로 설치하고 오류까지 해결하기를 먼저 따라 해 주세요. 이 글은 그 글의 다음 단계입니다.
Docker Compose는 무엇이고 왜 쓰나요?
docker run으로 컨테이너를 띄우면 포트, 볼륨, 이름, 재시작 정책 같은 옵션을 매번 명령줄에 적어야 합니다. 컨테이너가 하나일 때는 참을 만하지만, 며칠 뒤 같은 설정으로 다시 만들려고 하면 어떤 옵션을 썼는지 기억나지 않는 경우가 많습니다. 서비스가 두세 개로 늘어나면 실행 순서와 네트워크까지 신경 써야 합니다.
Compose는 이 설정을 YAML 파일에 적어 두는 방식입니다. 파일만 있으면 다른 서버에서도 같은 환경을 그대로 재현할 수 있고, Git으로 변경 이력도 남길 수 있습니다. 공식 문서에서도 Compose를 여러 컨테이너 애플리케이션을 정의하고 실행하는 도구로 소개합니다.

Docker Compose는 어떻게 설치하나요?
Docker 공식 apt 저장소로 Docker Engine을 설치했다면 Compose도 이미 함께 설치되어 있을 가능성이 높습니다. 공식 설치 명령에 docker-compose-plugin 패키지가 포함되어 있기 때문입니다. get.docker.com 편의 스크립트로 설치한 경우에도 이 플러그인이 같이 들어갑니다. 먼저 버전부터 확인합니다.
docker compose versionDocker Compose version v5.x.x처럼 버전이 출력되면 바로 사용할 수 있습니다. 2026년 11월 기준 공식 문서가 안내하는 최신 릴리스는 v5 계열입니다. 만약 docker: 'compose' is not a docker command 같은 메시지가 나온다면 플러그인만 따로 설치하면 됩니다. Docker 저장소가 등록되어 있다는 전제입니다.
sudo apt-get update
sudo apt-get install docker-compose-plugin
docker compose version업데이트도 같은 명령으로 합니다. 공식 문서에는 GitHub 릴리스 파일을 직접 내려받는 수동 설치 방법도 있지만, 이 방식은 자동으로 업데이트되지 않아 저장소 방식을 권장합니다.
docker-compose와 docker compose는 무엇이 다른가요?
검색하다 보면 하이픈이 들어간 docker-compose 명령을 쓰는 글이 아직 많습니다. 이것은 Python으로 만들어진 예전 Compose v1이고, 2023년 7월부터 업데이트가 중단되었습니다. 지금은 Go로 다시 만든 플러그인 방식이 표준이므로 새로 시작한다면 docker compose만 기억하면 됩니다. Ubuntu 기본 저장소의 docker-compose 패키지를 apt로 설치하는 것도 피하는 편이 좋습니다.
첫 docker-compose.yml은 어떻게 작성하나요?
실습은 nginx 웹서버 하나로 진행합니다. 작업 폴더를 만들고, 웹페이지로 보여 줄 html 폴더와 index.html 파일을 준비합니다.
mkdir -p ~/hello-compose/html
cd ~/hello-compose
echo '<h1>Hello Docker Compose</h1>' > html/index.html이제 같은 폴더에 compose 파일을 만듭니다. 파일 이름은 compose.yaml이 현재 공식 문서의 기본 이름이고, 예전부터 쓰던 docker-compose.yml도 그대로 인식됩니다. 이 글에서는 제목에 맞춰 docker-compose.yml로 만들어도 괜찮지만, 두 파일을 한 폴더에 같이 두지는 마세요.
services:
web:
image: nginx:1.27-alpine
container_name: hello-web
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
restart: unless-stopped
각 줄의 의미는 다음과 같습니다.
services: 실행할 서비스 목록이 들어가는 최상위 키입니다.web: 서비스 이름입니다. 나중에 다른 컨테이너가 이 이름으로 접속할 수 있습니다.image: 사용할 이미지와 태그입니다. latest 대신 버전을 적어 두면 갑자기 바뀌는 일을 막을 수 있습니다.ports: 호스트의 8080번 포트를 컨테이너의 80번 포트로 연결합니다. 왼쪽이 내 서버, 오른쪽이 컨테이너입니다.volumes: 내 html 폴더를 nginx의 웹 루트에 연결하는 바인드 마운트입니다. 끝의 ro는 읽기 전용이라는 뜻입니다.restart: 서버가 재부팅되어도 직접 멈추지 않은 컨테이너는 다시 시작합니다.
예전 글에서 파일 맨 위에 version: "3.8" 같은 줄을 넣는 예시를 자주 볼 수 있습니다. 현재 Compose 명세에서 version 키는 더 이상 쓰이지 않으며, 넣으면 obsolete라는 경고만 나옵니다. 새로 작성할 때는 빼도 됩니다.
docker compose up, ps, logs, down은 어떻게 쓰나요?
compose 파일이 있는 폴더에서 아래 명령으로 서비스를 만들고 백그라운드로 실행합니다. -d는 터미널을 붙잡지 않고 뒤에서 실행하라는 옵션입니다.
docker compose up -d
docker compose ps
ps 결과의 STATUS가 Up이고 PORTS에 8080->80/tcp가 보이면 성공입니다. 브라우저에서 http://서버IP:8080에 접속하거나 서버에서 curl http://localhost:8080을 실행하면 Hello Docker Compose 문구가 보입니다. 자주 쓰는 명령은 다음 다섯 가지입니다.
docker compose logs -f # 로그 실시간 보기 (Ctrl+C로 빠져나옴)
docker compose stop # 컨테이너 잠시 중지 (삭제하지 않음)
docker compose start # 중지한 컨테이너 다시 시작
docker compose down # 컨테이너와 네트워크 삭제
docker compose config # 파일 문법 검사와 최종 설정 출력
stop과 down의 차이를 헷갈리기 쉽습니다. stop은 컨테이너를 멈추기만 하고, down은 컨테이너와 Compose가 만든 네트워크까지 지웁니다. 바인드 마운트한 html 폴더는 내 서버에 있는 파일이라 down을 해도 그대로 남습니다. 다만 docker compose down -v처럼 -v를 붙이면 이름 있는 볼륨까지 지워지니, 데이터베이스를 쓰기 시작한 뒤에는 조심해야 합니다.
설정을 바꾸면 어떻게 다시 적용하나요?
html/index.html 내용만 고쳤다면 바인드 마운트 덕분에 새로고침만 해도 바로 반영됩니다. 반면 compose 파일의 포트나 이미지 같은 설정을 바꿨다면 다시 docker compose up -d를 실행합니다. Compose가 바뀐 서비스만 찾아서 컨테이너를 새로 만들어 줍니다.
# 예: 포트를 8080에서 8081로 바꾼 경우
sed -i 's/"8080:80"/"8081:80"/' docker-compose.yml
docker compose up -d
docker compose ps설정은 그대로인데 컨테이너를 강제로 새로 만들고 싶다면 docker compose up -d --force-recreate를, 이미지 새 버전을 받으려면 docker compose pull 뒤에 up -d를 실행합니다.
Docker Compose에서 자주 나는 오류는 어떻게 해결하나요?

port is already allocated (포트가 이미 사용 중)
호스트의 8080 포트를 다른 프로그램이나 다른 컨테이너가 쓰고 있다는 뜻입니다. 어떤 프로세스가 쓰는지 확인한 뒤 그 프로그램을 멈추거나, compose 파일의 왼쪽 포트 번호를 바꿉니다.
sudo ss -ltnp | grep 8080
docker ps --format '{{.Names}} {{.Ports}}'permission denied while trying to connect to the docker API (docker.sock 권한)
현재 사용자가 docker 그룹에 들어 있지 않아 생기는 오류입니다. 아래 명령으로 그룹에 추가한 뒤 로그아웃했다가 다시 로그인합니다. docker 그룹은 사실상 root와 같은 권한이라는 점도 기억해 두세요.
sudo usermod -aG docker $USER
newgrp docker
docker compose psyaml: line N: mapping values are not allowed (들여쓰기 오류)
YAML은 들여쓰기로 구조를 구분합니다. 탭 문자를 쓰거나 칸 수가 섞이면, 혹은 콜론 뒤에 공백이 빠지면 이런 오류가 납니다. 공백 두 칸으로 통일하고, 실행 전에 docker compose config로 검사하면 몇 번째 줄이 문제인지 알려 줍니다.
다음에는 무엇을 배우면 좋을까요?
compose 파일 하나로 컨테이너를 만들고 지우는 흐름을 익혔다면, 다음은 컨테이너를 지워도 데이터가 남게 하는 Docker 볼륨과 컨테이너끼리 서비스 이름으로 통신하는 Docker 네트워크입니다. 이 두 가지를 알면 WordPress와 데이터베이스처럼 서비스 두 개를 함께 묶는 compose 파일도 어렵지 않게 작성할 수 있습니다. 오늘 만든 hello-compose 폴더는 지우지 말고 다음 실습에 그대로 이어서 쓰겠습니다.