docker run 명령어를 실행했는데 컨테이너가 바로 꺼진다. docker ps를 보면 아무것도 없고, docker ps -a에서 Exited(0)만 남아 있다. Exited(0) 해결이 막막하다면 원인부터 짚어야 한다.

Exited(0) 해결이 막막하다면 원인부터 짚어야 한다.
에러 상황은 보통 이렇게 보인다.
$ docker run myapp
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
a1b2c3d4e5f6 myapp "node server…" 3 seconds ago Exited (0) 2 seconds ago nice_euler
docker container exits immediately가 뜨는 원인은 크게 세 가지다. 가장 흔한 순서대로 진단법과 함께 정리했다.
docker container exits immediately 에러가 뜨는 이유
Docker 컨테이너는 메인 프로세스(PID 1)가 살아 있는 동안만 실행된다. PID 1이 종료되면 컨테이너도 즉시 종료된다.
VM이나 일반 서버와 달리 컨테이너는 운영체제를 공유하는 격리된 프로세스 집합이다. 그 집합의 루트 프로세스가 없어지면 컨테이너가 유지될 이유가 없다. 따라서 컨테이너가 바로 종료된다는 것은 PID 1 프로세스가 즉시 종료됐다는 뜻이다.
원인은 아래 세 가지 중 하나다.
원인 1: RUN vs CMD 혼동 — 가장 흔한 경우
Dockerfile에서 RUN node server.js처럼 작성하면 이미지 빌드 시 서버가 실행되는 게 아니다.
# 잘못된 예 -- 빌드 시점에 실행, 컨테이너 런타임에는 아무것도 없음
FROM node:18
WORKDIR /app
COPY . .
RUN npm install
RUN node server.js # 여기가 문제
RUN은 이미지 빌드 시점에 실행된다. 빌드가 끝나면 해당 레이어는 완료된 상태로 저장될 뿐이다. 컨테이너가 시작될 때 실행할 프로세스는 CMD나 ENTRYPOINT로 지정해야 한다.
# 올바른 예 -- CMD로 런타임 프로세스 지정
FROM node:18
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "server.js"] # 컨테이너 시작 시 실행
CMD vs RUN 차이를 혼동하는 경우가 많다. 세 명령어의 실행 시점 차이를 정리하면 다음과 같다.
| 명령어 | 실행 시점 | 용도 |
|---|---|---|
RUN | 이미지 빌드 시 | 패키지 설치, 파일 복사 등 환경 구성 |
CMD | 컨테이너 시작 시 | 메인 프로세스 실행 (재정의 가능) |
ENTRYPOINT | 컨테이너 시작 시 | 메인 프로세스 실행 (재정의 불가) |
공식 문서에서 CMD와 ENTRYPOINT의 상세 동작 차이를 확인할 수 있다: Dockerfile CMD reference
원인 2: 포그라운드 프로세스 없음 (PID 1 문제)
앱은 정상적으로 실행되지만 백그라운드로 돌고 있어서 PID 1 자리가 비는 경우다.
# 잘못된 예 -- 백그라운드 실행으로 PID 1 없음
CMD ["sh", "-c", "node server.js &"]
node server.js &처럼 백그라운드(&)로 실행하면 셸 프로세스가 PID 1이 된다. 셸은 백그라운드 작업을 시작한 뒤 즉시 종료되고, PID 1이 없어지면서 컨테이너도 꺼진다.
셸 스크립트로 시작하는 경우 exec를 쓰면 셸 프로세스를 앱 프로세스로 교체해서 PID 1을 유지할 수 있다.
#!/bin/sh
# start.sh
exec node server.js # exec로 셸을 Node.js 프로세스로 교체 -- PID 1 유지
exec를 쓰면 셸 스크립트가 끝나도 Node.js 프로세스가 PID 1을 이어받아 컨테이너가 유지된다.
원인 3: 앱 자체가 에러로 즉시 종료
앱이 시작되자마자 예외나 설정 오류로 종료되는 경우다. 이때는 종료 코드가 0이 아닌 1 이상이다.
$ docker ps -a
CONTAINER ID IMAGE STATUS NAMES
a1b2c3d4e5f6 myapp Exited (1) 5 seconds ago nice_euler
Exit Code 1이 보이면 앱 런타임 에러를 의심해야 한다. docker logs로 에러 메시지를 바로 확인한다.
docker logs로 컨테이너 종료 원인 진단하기
컨테이너가 종료된 뒤에도 로그는 남아 있다.
docker logs [컨테이너ID 또는 이름]
- 로그가 완전히 비어 있다 — 원인 1(RUN/CMD 혼동). 런타임에 아무것도 실행되지 않은 것이다.
- 에러 메시지가 있다 — 원인 3(앱 에러). 스택 트레이스나 환경 변수 누락 메시지를 확인한다.
- 정상 시작 로그 후 종료됐다 — 원인 2(PID 1 문제) 또는 앱이 작업을 마치고 정상 종료된 것이다.
더 상세한 진단이 필요하면 docker inspect로 종료 정보를 확인한다.
docker inspect [컨테이너ID] --format='{{.State}}'
# ExitCode, Error, OOMKilled 필드를 확인할 수 있다
컨테이너 내부 환경을 직접 확인하고 싶으면 인터랙티브 모드로 진입한다.
# 기존 ENTRYPOINT를 무시하고 셸로 진입
docker run -it --entrypoint /bin/sh myapp
이 상태에서 node server.js를 직접 실행해보면 실제 에러 메시지를 바로 확인할 수 있다.
종료 코드(Exit Code)별 원인
Exited (N) 뒤의 숫자가 원인을 좁혀준다.
| Exit Code | 의미 | 주요 원인 |
|---|---|---|
0 | 정상 종료 | CMD 프로세스가 작업을 마치고 종료. 서버라면 이상 신호 |
1 | 일반 에러 | 앱 런타임 에러, 환경 변수 누락, 설정 오류 |
137 | SIGKILL | OOM(메모리 부족)으로 강제 종료. docker inspect에서 OOMKilled: true 확인 |
143 | SIGTERM | docker stop 명령으로 정상 종료 요청됨. SIGTERM 무시 시 10초 후 SIGKILL로 강제 종료 |
Exit Code 0이면서 서버가 바로 꺼지는 경우가 가장 흔한 혼동 케이스다. 서버 프로세스는 계속 실행되어야 하는데, CMD로 지정한 프로세스가 완료되면 Exit Code 0이 된다.
자주 묻는 질문
-d 플래그로 실행했는데 왜 바로 꺼지나?
docker run -d myapp은 백그라운드 실행이지만, PID 1 프로세스가 종료되면 그 상태와 관계없이 컨테이너도 꺼진다. -d는 컨테이너를 살려두는 옵션이 아니라 분리 모드(detached mode)일 뿐이다.
로컬에서는 잘 됐는데 Docker에서만 종료되는 이유는?
node server.js를 로컬에서 직접 실행하면 터미널 세션이 유지되는 동안 프로세스도 유지된다. Docker에서는 터미널 세션이 없고 PID 1 프로세스 자체가 컨테이너의 생명이다. 로컬 환경의 암묵적인 프로세스 유지 조건이 Docker에서는 적용되지 않는다.
docker run -it로 실행하면 안 꺼지는데 -d로 실행하면 꺼지는 이유는?
-it 플래그는 TTY와 stdin을 연결한다. 이 연결이 PID 1(셸이나 프로세스)을 살아있게 만든다. -d로 실행하면 이 연결이 없어서 입력 대기 없이 바로 종료된다. 이 동작 차이를 이해하면 왜 CMD에 서버 프로세스를 명시해야 하는지 명확해진다.
한줄 정리: docker container exits immediately는 PID 1 프로세스가 종료됐다는 신호다.
docker logs로 먼저 진단하고, 원인에 따라 CMD 수정 / exec 추가 / 앱 에러 수정 중 하나를 택하라.