서버·SSH

SSH 접속이 자꾸 끊길 때, timeout 설정으로 잡는 법

SSH 접속이 자꾸 끊길 때, timeout 설정으로 잡는 법

터미널을 열어놓고 문서를 좀 읽다가 돌아왔더니 이런 게 떠 있습니다.

client_loop: send disconnect: Broken pipe

또는 이런 것들.

packet_write_wait: Connection to 203.0.113.10 port 22: Broken pipe
Connection to 203.0.113.10 closed by remote host.

다시 붙으면 되긴 하는데, 편집하던 파일이나 돌리던 명령이 날아가면 곤란합니다. 끊는 주체가 서버인지, 중간 네트워크 장비인지, 내 노트북인지부터 갈라야 고칠 곳이 정해집니다.

먼저 확인: 끊는 주체가 누구인가

SSH 세션이 죽는 경로는 크게 세 가지입니다. 서버의 sshd가 유휴 세션이라 판단해 직접 끊는 경우, 공유기나 통신사 NAT가 오래 조용한 TCP 연결을 테이블에서 지워버리는 경우, 그리고 클라이언트가 응답을 못 받아 스스로 포기하는 경우입니다.

메시지로 어느 정도 구분이 됩니다. 절대적인 규칙은 아니지만 경향은 이렇습니다.

메시지대체로 의미하는 것
Connection to ... closed by remote host.서버 쪽에서 명시적으로 세션을 닫음. sshd 설정이나 서버 재시작 의심
client_loop: send disconnect: Broken pipe중간 경로가 끊긴 뒤 클라이언트가 뒤늦게 알아챈 상태
packet_write_wait: Connection to ... : Broken pipe위와 비슷. 예전 OpenSSH 버전에서 자주 보이던 문구
Timeout, server ... not responding.클라이언트의 ServerAlive 카운트가 소진돼 클라이언트가 끊음

Broken pipe 계열은 “이미 끊긴 연결에 쓰려고 했다”는 뜻이라, 정작 누가 먼저 끊었는지는 알려주지 않습니다. 그래서 메시지만으로 단정하지 말고 아래 질문을 하나 더 던져야 합니다.

무입력일 때만 끊기나, 작업 중에도 끊기나

키보드를 몇 분간 놓았을 때만 끊긴다면 유휴 타임아웃 문제입니다. 서버 설정이나 NAT 테이블 만료가 원인일 가능성이 높고, 이 글의 keepalive 설정으로 대부분 잡힙니다.

반면 tail -f를 걸어두고 로그가 계속 흐르는 중에도 끊긴다면 timeout이 아닙니다. 무선 신호 문제, 회선 불안정, VPN 재협상 같은 네트워크 쪽을 봐야 합니다. keepalive를 아무리 촘촘히 넣어도 소용이 없습니다.

간단한 판별법 하나. 같은 서버에 다른 사람도 붙어 있는데 그 사람은 안 끊긴다면 서버 설정이 아니라 내 쪽 경로 문제입니다. 여러 명이 비슷한 주기로 끊긴다면 서버의 ClientAliveInterval이나 ClientAliveCountMax를 먼저 보세요.

내 컴퓨터 쪽에서 해결하기 (~/.ssh/config)

ServerAliveInterval은 “조용한 채로 N초가 지나면 클라이언트가 서버에게 살아있냐고 물어본다”는 설정입니다. 이 물음이 실제 패킷으로 나가기 때문에, NAT 테이블 입장에서는 연결이 계속 쓰이고 있는 것으로 보입니다. 그래서 조용히 세션이 버려지는 걸 막아줍니다.

ServerAliveCountMax는 그 물음에 연달아 몇 번까지 응답이 없어도 참을지입니다. 둘을 곱한 값이 대략적인 포기 시점입니다.

ServerAliveInterval 60 × ServerAliveCountMax 3 ≈ 180초 무응답 시 연결 종료

config 파일에 적어두기

~/.ssh/config 파일을 만들거나 열어서 아래처럼 넣습니다.

# ~/.ssh/config

Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3

Host myserver
    HostName 203.0.113.10
    User deploy
    Port 22
    ServerAliveInterval 30
    ServerAliveCountMax 5

Host *는 모든 접속에 적용되는 기본값입니다. 특정 서버만 더 촘촘하게 하고 싶으면 아래처럼 개별 Host 블록에 따로 적으면 됩니다. 이 파일에 별칭까지 정리해두면 접속 자체가 편해지는데, 그 부분은 SSH config 파일로 서버 별칭 등록하기, ssh 서버이름 한 줄로 접속하는 설정에 따로 정리해 뒀습니다.

주의할 점은 OpenSSH가 같은 키를 여러 번 만나면 먼저 읽은 값을 씁니다. 나중 값이 덮어쓰는 게 아닙니다. 그래서 Host * 블록은 파일 맨 아래에 두는 게 안전합니다.

권한이 안 맞으면 통째로 무시됩니다

파일을 고쳤는데 아무 변화가 없다면 권한부터 보세요. 그룹이나 다른 사용자에게 쓰기 권한이 있으면 SSH가 config를 거부합니다.

chmod 600 ~/.ssh/config
chmod 700 ~/.ssh
ls -l ~/.ssh/config

-rw------- 로 나오면 정상입니다.

적용됐는지 확인하기

-v를 붙여서 접속하면 어떤 설정 파일의 몇 번째 줄을 읽었는지 출력됩니다.

ssh -v myserver

출력에서 이런 줄을 찾으면 됩니다.

debug1: Reading configuration data /home/user/.ssh/config
debug1: /home/user/.ssh/config line 4: Applying options for *

Applying options for 줄이 안 보이면 파일 경로나 권한, 혹은 Host 패턴이 안 맞은 겁니다.

클라이언트 keepalive 적용 확인
클라이언트 keepalive 적용 확인

파일 안 건드리고 한 번만

지금 이 접속에만 적용하고 싶을 때는 -o를 씁니다.

ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 user@203.0.113.10

테스트할 때 이 방식이 편합니다. 효과가 확인되면 그때 config에 옮겨 적으면 됩니다.

TCPKeepAlive와 뭐가 다른가

TCPKeepAlive yes도 비슷해 보이지만 동작 계층이 다릅니다. TCPKeepAlive는 OS의 TCP 레벨 신호이고 주기가 보통 두 시간대로 아주 깁니다. ServerAliveInterval은 SSH 프로토콜 안에서 오가므로 암호화 채널을 타고, 주기를 내가 정할 수 있습니다. 유휴 끊김을 막는 목적이라면 ServerAlive 쪽을 쓰세요.

서버 쪽에서 해결하기 (sshd_config)

서버에는 이름이 반대로 붙어 있습니다. 서버가 클라이언트에게 신호를 보내는 것이라 ClientAlive입니다. 클라이언트 설정의 ServerAlive와 방향만 다르고 하는 일은 같습니다.

설정 위치지시어누가 누구에게
~/.ssh/config (내 PC)ServerAliveInterval / ServerAliveCountMax클라이언트 → 서버
/etc/ssh/sshd_config (서버)ClientAliveInterval / ClientAliveCountMax서버 → 클라이언트

편집 전에 백업

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F)
sudo nano /etc/ssh/sshd_config

아래 두 줄을 넣거나 주석을 풉니다.

ClientAliveInterval 60
ClientAliveCountMax 3

이러면 서버가 60초마다 확인 신호를 보내고, 3번 연속 무응답이면 세션을 정리합니다. 반대로 서버가 유휴 세션을 빨리 끊게 만들고 싶다면 ClientAliveCountMax 0으로 두고 Interval만 짧게 주는 방식도 있습니다. 보안 정책상 그렇게 설정된 서버라면, 클라이언트에서 아무리 keepalive를 넣어도 정해진 시간에 끊깁니다.

문법 검사 후 재시작

여기서 실수하면 SSH 자체가 안 뜹니다. 반드시 문법 검사를 먼저 하세요.

sudo sshd -t

아무것도 출력되지 않으면 정상입니다. 오류가 있으면 파일 이름과 줄 번호를 알려줍니다. 통과하면 재시작합니다.

sudo systemctl restart sshd

배포판에 따라 서비스 이름이 다릅니다. Debian/Ubuntu 계열은 ssh, RHEL/Rocky/Alma 계열은 sshd인 경우가 많습니다. 실패하면 이름을 바꿔서 다시 시도하세요.

sudo systemctl restart ssh
systemctl status ssh --no-pager

sshd를 재시작해도 이미 붙어 있는 세션은 끊기지 않습니다. 그래도 설정이 잘못돼 sshd가 안 올라올 수 있으니, 기존 터미널은 그대로 두고 새 터미널로 접속이 되는지 확인한 뒤에 닫으세요. 이 습관은 SSH 포트 변경하고 방화벽까지, 접속 끊기지 않게 하는 순서에서도 똑같이 적용됩니다.

sshd_config 안전하게 고치는 순서
sshd_config 안전하게 고치는 순서

값을 넣었는데 안 먹을 때: Include 확인

최근 배포판은 sshd_config 상단이나 하단에 이런 줄이 있습니다.

Include /etc/ssh/sshd_config.d/*.conf

sshd_config는 클라이언트와 달리 나중에 읽은 값이 유효한 경우가 있고, 클라우드 이미지에서 자동으로 넣어둔 .conf 파일이 값을 덮어쓰기도 합니다. 내가 고친 값이 반영되지 않으면 이 디렉터리를 먼저 보세요.

ls -l /etc/ssh/sshd_config.d/
sudo grep -rn "ClientAlive" /etc/ssh/sshd_config /etc/ssh/sshd_config.d/

실제 적용값은 아래 명령으로 확인할 수 있습니다.

sudo sshd -T | grep -i clientalive

카페24나 닷홈 같은 공유호스팅처럼 root 권한이 없는 환경이라면 이 절은 통째로 건너뛰세요. 서버 설정은 손댈 수 없으니 클라이언트 ~/.ssh/config만 조정하는 수밖에 없습니다.

설정을 다 해도 끊긴다면: 세션을 살려두는 쪽으로

keepalive는 끊김을 늦추는 장치입니다. 작업 내용을 보존해 주지는 않습니다. 노트북 뚜껑을 덮거나, 와이파이에서 테더링으로 넘어가거나, 회선이 잠깐 죽으면 어떤 설정으로도 못 막습니다. 그럴 때 필요한 게 세션을 서버 쪽에 남겨두는 방식입니다.

tmux 기본 흐름

# 서버에 접속한 뒤
tmux new -s work

# 작업하다가 떼어놓기
# Ctrl+b 를 누르고 손을 뗀 다음 d

# 나중에 다시 접속해서
tmux attach -t work

# 어떤 세션이 있는지 확인
tmux ls

접속이 끊겨도 work 세션은 서버에서 계속 돌아갑니다. 다시 붙으면 화면이 그대로 있습니다. 서버 작업을 자주 한다면 이건 설정이 아니라 습관으로 들여야 하는 쪽에 가깝습니다.

screen을 쓴다면

screen -S work
# 떼어놓기: Ctrl+a 다음 d
screen -r work
screen -ls

긴 작업은 처음부터 분리

apt upgrade, 대용량 rsync, DB 덤프 복원처럼 오래 걸리는 명령은 tmux 안에서 돌리거나 nohup으로 띄우세요. 중간에 끊기면 SIGHUP이 날아가 프로세스가 죽습니다.

nohup ./long_job.sh > job.log 2>&1 &
tail -f job.log

mosh는 어떤가

mosh는 IP가 바뀌거나 잠깐 네트워크가 끊겨도 세션이 유지됩니다. 모바일 환경에서는 확실히 편합니다. 다만 서버와 클라이언트 양쪽에 설치해야 하고, UDP 60000~61000 대역을 방화벽에서 열어야 합니다. 포트를 함부로 열기 어려운 서버라면 부담이 될 수 있습니다. tmux로 충분한 경우가 많으니 순서상 tmux를 먼저 시도해 보길 권합니다.

상황별 정리표

증상의심 원인손댈 곳대표 설정
몇 분 무입력 시 closed by remote host.서버 유휴 타임아웃/etc/ssh/sshd_configClientAliveInterval 60, ClientAliveCountMax 3
무입력 시 client_loop: send disconnect: Broken pipe공유기/NAT가 세션 폐기~/.ssh/configServerAliveInterval 60
특정 회사망·카페 와이파이에서만 끊김중간 장비 정책클라이언트ServerAliveInterval 30 으로 더 촘촘히
작업 중에도 끊김무선/회선 불안정timeout 아님유선 전환, mosh 또는 tmux
뚜껑 덮었다 열면 끊김클라이언트 절전설정으로 불가tmux / screen
나만 끊기고 동료는 멀쩡내 경로 또는 내 config~/.ssh/config위와 동일
여러 명이 비슷한 주기로 끊김서버 정책sshd_config`sshd -T \grep clientalive` 로 확인

원복은 어렵지 않습니다. 클라이언트는 ~/.ssh/config에서 추가한 두 줄을 지우면 끝입니다. 서버는 백업해 둔 파일을 되돌리고 다시 검사·재시작하면 됩니다.

sudo cp /etc/ssh/sshd_config.bak.2026-09-03 /etc/ssh/sshd_config
sudo sshd -t && sudo systemctl restart sshd

다음에 할 일 체크리스트

  • [ ] 끊길 때 나오는 메시지를 그대로 메모한다. closed by remote host.인지 Broken pipe인지에 따라 볼 곳이 갈립니다.
  • [ ] 무입력일 때만 끊기는지, 작업 중에도 끊기는지 한 번 재현해 본다.
  • [ ] ssh -o ServerAliveInterval=60 user@host로 일회성 테스트를 먼저 해본다.
  • [ ] 효과가 있으면 ~/.ssh/config에 Host * 블록으로 옮기고 chmod 600 ~/.ssh/config를 확인한다.
  • [ ] ssh -v로 Applying options for * 줄이 뜨는지 본다.
  • [ ] 서버 권한이 있다면 sudo sshd -T | grep -i clientalive로 현재 값부터 확인한다.
  • [ ] 고치기 전에 백업, 고친 뒤 sudo sshd -t, 그다음 재시작. 기존 터미널은 살려둔다.
  • [ ] sshd_config.d/ 안에 값을 덮어쓰는 파일이 있는지 확인한다.
  • [ ] 서버에 tmux를 설치하고 tmux new -s work로 시작하는 습관을 들인다.

접속 자체가 거부되는 상황이라면 timeout이 아니라 인증 문제입니다. 그쪽은 Permission denied (publickey) 원인별 진단 순서 정리를 따라가면서 확인해 보세요.

댓글 1개

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다