우분투 서버 초기 설정: 신규 설치 후 Ubuntu 서버에서 시작할 9단계

공정위 광고 문구 Ver 3.1
공정위 광고 문구 Ver 3.2(모바일 전용)
테스트 환경Debian 계열Ubuntu 24.04.5 LTSroot · 일반 사용자 계정
우분투 서버 첫 설정 작업 확인

새 서버를 받으면 서비스를 올리기 전에 우분투 서버 초기 설정을 한 번 거칩니다. 디지털오션에서 만든 Ubuntu 24.04 서버에 root로 접속해 패키지 업데이트부터 sudo 사용자, 키 로그인, 방화벽, 시간대, 자동 보안 업데이트, 스왑까지 차례로 설정하고, 재부팅한 뒤에도 설정이 그대로 남는 것까지 확인했습니다.

아래 결과 상자는 명령을 이 순서대로 실행한 출력입니다. 서버 IP 203.0.113.10은 예시 값이며, 새로 만드는 일반 사용자 이름은 deploy로 정했습니다.

🎯 요점
패키지 업데이트와 재부팅 필요 여부 확인
sudo 그룹에 넣은 일반 사용자 deploy
deploy 키 로그인 확인 뒤 root 로그인 차단
ufw로 SSH, 80, 443 포트만 허용
시간대 변경과 자동 보안 업데이트 확인
2GB 스왑 파일과 재부팅 뒤 설정 유지 확인
전제 조건
접속root로 접속할 수 있는 SSH 키와 Ubuntu 24.04 서버

1️⃣ 우분투 패키지 업데이트

새로 만든 서버에도 설치할 업데이트가 쌓여 있습니다. 패키지 목록을 새로 받은 뒤, 실제로 설치하기 전에 무엇이 바뀌는지부터 확인합니다.

🔲 apt-get update

export DEBIAN_FRONTEND=noninteractive

아래 apt-get 명령은 이 값을 먼저 둔 뒤 실행했습니다. 이 값을 두면 패키지 설치 중 설정을 묻는 창이 뜨지 않고 기본 답이 적용됩니다.

apt-get update
Hit:1 http://mirrors.digitalocean.com/ubuntu noble InRelease
Hit:2 http://mirrors.digitalocean.com/ubuntu noble-updates InRelease
Hit:3 http://mirrors.digitalocean.com/ubuntu noble-backports InRelease
Hit:4 https://repos-droplet.digitalocean.com/apt/droplet-agent main InRelease
Hit:5 http://security.ubuntu.com/ubuntu noble-security InRelease
Reading package lists...

🔲 apt-get -s upgrade

apt-get -s upgrade
Calculating upgrade...
The following upgrades have been deferred due to phasing:
  libsqlite3-0
The following packages will be upgraded:
  apt apt-transport-https apt-utils bind9-dnsutils bind9-host bind9-libs
  cloud-init distro-info-data gir1.2-glib-2.0 git git-man libapt-pkg6.0t64
  libbrotli1 libglib2.0-0t64 libglib2.0-bin libglib2.0-data liblzma5
  libmspack0t64 libpcre2-8-0 python3-jwt python3-twisted tmux
  ubuntu-pro-client ubuntu-pro-client-l10n xz-utils zlib1g
26 upgraded, 0 newly installed, 0 to remove and 1 not upgraded.

-s 옵션은 설치하지 않고 결과만 계산하는 모의 실행입니다. 이 서버에서는 26개가 업그레이드 대상이었고, libsqlite3-0 하나는 [deferred due to phasing]으로 이번 업그레이드에서 빠졌습니다. 대상 개수는 서버를 만든 시점과 이미지에 따라 서버마다 다릅니다.

🔲 apt-get -y upgrade

apt-get -y upgrade
26 upgraded, 0 newly installed, 0 to remove and 1 not upgraded.
Need to get 15.4 MB of archives.
After this operation, 398 kB of additional disk space will be used.

설치가 끝난 뒤에는 다시 시작한 서비스 목록이 이어서 나옵니다.

Running kernel seems to be up-to-date.

Restarting services...
 /etc/needrestart/restart.d/systemd-manager
 systemctl restart cron.service fwupd.service multipathd.service packagekit.service polkit.service ssh.service systemd-journald.service systemd-networkd.service systemd-resolved.service systemd-timesyncd.service systemd-udevd.service udisks2.service

Service restarts being deferred:
 systemctl restart ModemManager.service
 /etc/needrestart/restart.d/dbus.service
 systemctl restart systemd-logind.service
 systemctl restart unattended-upgrades.service

재부팅이 필요한 업데이트가 들어왔을 때는 /var/run/reboot-required 파일이 생깁니다. 이번에는 [Running kernel seems to be up-to-date.]가 나오고 그 파일도 생기지 않아 재부팅 없이 다음 단계로 넘어갔습니다.

2️⃣ 우분투 사용자 추가와 sudo 권한

평소 작업은 일반 사용자로 하고, 관리 명령만 sudo로 실행합니다. sudo는 root 비밀번호 대신 자기 비밀번호로 권한을 올리므로 누가 어떤 명령을 실행했는지 남습니다.

🔲 adduser deploy

adduser deploy
info: Adding user `deploy' ...
info: Selecting UID/GID from range 1000 to 59999 ...
info: Adding new group `deploy' (1000) ...
info: Adding new user `deploy' (1000) with group `deploy (1000)' ...
info: Creating home directory `/home/deploy' ...
info: Copying files from `/etc/skel' ...
New password: 
Retype new password: 
passwd: password updated successfully
Changing the user information for deploy
Enter the new value, or press ENTER for the default
	Full Name []: 
	Room Number []: 
	Work Phone []: 
	Home Phone []: 
	Other []: 
Is the information correct? [Y/n] Y
info: Adding new user `deploy' to supplemental / extra groups `users' ...
info: Adding user `deploy' to group `users' ...

adduser는 useradd를 감싼 도구로, 홈 폴더와 그룹을 만든 뒤 비밀번호와 사용자 정보를 차례로 묻습니다. 비밀번호는 입력해도 화면에 나오지 않고, 이름과 전화번호 같은 정보는 Enter로 비워 둡니다.

🔲 usermod -aG sudo deploy

usermod -aG sudo deploy
groups deploy
deploy : deploy sudo users

🔲 su

sudo 그룹에 들어간 사용자는 sudo로 관리 명령을 실행할 수 있습니다. su로 deploy 계정으로 전환해 권한을 확인합니다.

su - deploy
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
whoami
deploy
sudo whoami
[sudo] password for deploy: 
root
exit

sudo를 붙이면 deploy의 비밀번호를 묻고, 맞으면 명령이 root 권한으로 실행됩니다.

3️⃣ 새 사용자 SSH 키 등록

처음에는 root에만 SSH 키가 등록되어 있어 deploy로는 키 로그인이 되지 않습니다. root의 authorized_keys를 deploy의 홈 폴더로 복사합니다.

🔲 install -o deploy -g deploy

install -d -m 700 -o deploy -g deploy /home/deploy/.ssh
install -m 600 -o deploy -g deploy /root/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys
ls -la /home/deploy/.ssh
total 12
drwx------ 2 deploy deploy 4096 Nov  5 11:48 .
drwxr-x--- 3 deploy deploy 4096 Nov  5 11:48 ..
-rw------- 1 deploy deploy  100 Nov  5 11:48 authorized_keys

install은 폴더 만들기와 복사, 소유자와 권한 지정을 한 번에 처리합니다. sshd는 로그인을 받기 전에 사용자 파일의 권한과 소유자를 검사하므로(StrictModes), .ssh 폴더는 700, authorized_keys는 600으로 둡니다.

🔲 ssh deploy@203.0.113.10

지금 열린 root 창은 닫지 않고, 새 터미널 창에서 deploy로 접속되는지 먼저 확인합니다.

ssh deploy@203.0.113.10
whoami
deploy
🧭 접속이 잠기지 않는 설정 순서
설명 다이어그램
🧭 단계
🔑① 키 복사root 키를 deploy에게
➜
🪟② 새 창 확인root 창은 열어 둔 채
➜
⛔③ root 로그인 차단PermitRootLogin no
➜
🪟④ 새 창 확인deploy 접속 그대로
✅⑤ SSH 허용ufw allow OpenSSH
➜
🧱⑥ ufw 켜기허용한 포트만 열림
➜
🪟⑦ 새 창 확인접속 그대로
root의 authorized_keys를 deploy의 홈 폴더로 복사합니다.
지금 열린 root 창은 닫지 않고, 새 터미널 창에서 deploy로 키 로그인이 되는지 확인합니다.
deploy 접속이 확인된 뒤에 root 로그인을 막습니다. 새 창에서 접속이 안 되면 열어 둔 창에서 되돌립니다.
SSH 설정을 다시 읽은 뒤 새 창에서 deploy 접속이 그대로 되는지 확인합니다.
방화벽을 켜기 전에 SSH 허용 규칙부터 넣습니다.
켜는 순간 허용하지 않은 접속은 막히므로, SSH 허용 규칙이 먼저 들어가 있어야 합니다.
방화벽을 켠 뒤 새 창에서 deploy로 다시 접속되는지 확인합니다.

4️⃣ SSH root 로그인 차단

Ubuntu 24.04의 sshd_config는 맨 위 Include 줄에서 /etc/ssh/sshd_config.d/ 폴더의 파일을 먼저 불러오고, 같은 항목은 먼저 읽은 값이 적용됩니다. 그래서 설정을 바꾸기 전에 지금 적용된 값과 그 값이 들어 있는 파일부터 확인합니다.

/etc/ssh└─ sshd_config  맨 위 Include /etc/ssh/sshd_config.d/*.conf   ├─ ① sshd_config.d/50-cloud-init.conf         PasswordAuthentication no   ├─ ② sshd_config.d/60-cloudimg-settings.conf  PasswordAuthentication no   └─ ③ sshd_config 42행                          PermitRootLogin yes → no

🔲 sshd -T

sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication) '
permitrootlogin yes
passwordauthentication no
sudo grep -rn 'PasswordAuthentication' /etc/ssh/sshd_config.d/
/etc/ssh/sshd_config.d/60-cloudimg-settings.conf:1:PasswordAuthentication no
/etc/ssh/sshd_config.d/50-cloud-init.conf:1:PasswordAuthentication no

비밀번호 로그인은 sshd_config.d 안 두 파일에서 이미 꺼져 있고, 바꿀 것은 본 파일의 root 로그인 한 줄입니다.

🔲 sed -i PermitRootLogin

sudo grep -n '^PermitRootLogin' /etc/ssh/sshd_config
42:PermitRootLogin yes
sudo sed -i 's/^PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sudo grep -n '^PermitRootLogin' /etc/ssh/sshd_config
42:PermitRootLogin no

🔲 systemctl reload ssh

sudo sshd -t
sudo systemctl reload ssh
sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication) '
permitrootlogin no
passwordauthentication no

sshd -t는 설정 문법을 검사하며 아무것도 나오지 않으면 오류가 없는 것입니다. 다시 읽은 뒤 새 창에서 deploy 접속이 그대로 되는 것을 확인하고, root로 접속해 거절되는지 봅니다.

ssh root@203.0.113.10
root@203.0.113.10: Permission denied (publickey).

키가 맞아도 root로는 들어갈 수 없고, 관리 작업은 deploy로 접속해 sudo로 합니다.

5️⃣ 우분투 방화벽 ufw

ufw는 Ubuntu의 기본 방화벽 도구이지만 처음에는 꺼져 있습니다. 켜는 순간 허용하지 않은 접속이 막히므로, SSH 허용 규칙을 먼저 넣고 마지막에 켭니다.

sudo ufw status verbose
Status: inactive

🔲 ufw allow OpenSSH

sudo ufw app list
Available applications:
  OpenSSH
sudo ufw allow OpenSSH
Rules updated
Rules updated (v6)
sudo ufw allow 80/tcp
Rules updated
Rules updated (v6)
sudo ufw allow 443/tcp
Rules updated
Rules updated (v6)
sudo ufw show added
Added user rules (see 'ufw status' for running firewall):
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp

OpenSSH는 22번 포트를 여는 프로필이고, 80과 443은 웹 서버에 사용하는 포트입니다. 규칙은 IPv4와 IPv6에 함께 들어갑니다.

🔲 ufw enable

sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp (OpenSSH)           ALLOW IN    Anywhere                  
80/tcp                     ALLOW IN    Anywhere                  
443/tcp                    ALLOW IN    Anywhere                  
22/tcp (OpenSSH (v6))      ALLOW IN    Anywhere (v6)             
80/tcp (v6)                ALLOW IN    Anywhere (v6)             
443/tcp (v6)               ALLOW IN    Anywhere (v6)             

들어오는 접속은 기본으로 막히고, 허용한 세 포트만 열린 상태로 부팅 때도 켜집니다. 켠 뒤 새 창에서 deploy로 다시 접속해 규칙 번호를 확인합니다.

sudo ufw status numbered
Status: active

     To                         Action      From
     --                         ------      ----
[ 1] OpenSSH                    ALLOW IN    Anywhere                  
[ 2] 80/tcp                     ALLOW IN    Anywhere                  
[ 3] 443/tcp                    ALLOW IN    Anywhere                  
[ 4] OpenSSH (v6)               ALLOW IN    Anywhere (v6)             
[ 5] 80/tcp (v6)                ALLOW IN    Anywhere (v6)             
[ 6] 443/tcp (v6)               ALLOW IN    Anywhere (v6)             

6️⃣ 우분투 시간대 설정

처음 시간대는 Etc/UTC입니다. 로그와 예약 작업의 시각을 서버 지역에 맞추기 위해, 싱가포르 지역에 만든 이 서버는 Asia/Singapore로 설정했습니다.

timedatectl
               Local time: Thu 2026-11-05 12:00:31 UTC
           Universal time: Thu 2026-11-05 12:00:31 UTC
                 RTC time: Thu 2026-11-05 12:00:31
                Time zone: Etc/UTC (UTC, +0000)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

🔲 timedatectl list-timezones

timedatectl list-timezones | grep Singapore
Asia/Singapore

🔲 timedatectl set-timezone

sudo timedatectl set-timezone Asia/Singapore
timedatectl
               Local time: Thu 2026-11-05 20:00:32 +08
           Universal time: Thu 2026-11-05 12:00:32 UTC
                 RTC time: Thu 2026-11-05 12:00:32
                Time zone: Asia/Singapore (+08, +0800)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

바뀌는 것은 Local time과 Time zone 두 줄이며, 시계 동기화(NTP)와 UTC 시각은 그대로입니다.

7️⃣ unattended upgrades 자동 보안 업데이트

Ubuntu는 보안 업데이트를 unattended-upgrades 패키지로 자동 설치하며, 이 패키지는 처음부터 설치되어 켜져 있습니다. 그래서 이 단계에서는 켜져 있는지와 무엇을 자동으로 설치하는지를 확인합니다.

🔲 dpkg-query -W

dpkg-query -W unattended-upgrades
unattended-upgrades	2.9.1+nmu4ubuntu1
systemctl is-enabled unattended-upgrades apt-daily.timer apt-daily-upgrade.timer
enabled
enabled
enabled

🔲 cat 20auto-upgrades

cat /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

두 값의 1은 하루에 한 번이라는 뜻이며, 0으로 바꾸면 자동 업데이트가 꺼집니다. 무엇을 설치할지는 50unattended-upgrades 파일이 정합니다.

grep -vE '^[[:space:]]*(//|$)' /etc/apt/apt.conf.d/50unattended-upgrades
Unattended-Upgrade::Allowed-Origins {
	"${distro_id}:${distro_codename}";
	"${distro_id}:${distro_codename}-security";
	"${distro_id}ESMApps:${distro_codename}-apps-security";
	"${distro_id}ESM:${distro_codename}-infra-security";
};
Unattended-Upgrade::Package-Blacklist {
};
Unattended-Upgrade::DevRelease "auto";

허용된 출처에 -security는 있고 -updates는 없어, 일반 업데이트는 자동으로 설치되지 않고 보안 업데이트만 설치됩니다. 일반 업데이트까지 자동으로 받으려면 이 파일에서 -updates 줄의 주석을 풉니다.

🔲 unattended-upgrade

sudo unattended-upgrade --dry-run --debug
Starting unattended upgrades script
Allowed origins are: o=Ubuntu,a=noble, o=Ubuntu,a=noble-security, o=UbuntuESMApps,a=noble-apps-security, o=UbuntuESM,a=noble-infra-security

같은 실행의 마지막 두 줄입니다.

No packages found that can be upgraded unattended and no pending auto-removals
The list of kept packages can't be calculated in dry-run mode.

--dry-run은 설치하지 않고 자동 업데이트가 할 일만 계산합니다. 패키지 업데이트를 마친 뒤라 자동으로 설치할 패키지가 없다고 나왔습니다. 실제 실행 기록은 /var/log/unattended-upgrades/ 폴더에 남습니다.

sudo ls -l /var/log/unattended-upgrades/
total 32
-rw-r--r-- 1 root root     0 Nov  5 20:00 unattended-upgrades-dpkg.log
-rw-r--r-- 1 root root     0 Nov  5 17:34 unattended-upgrades-shutdown.log
-rw-r--r-- 1 root root 29093 Nov  5 20:04 unattended-upgrades.log

8️⃣ 우분투 스왑 메모리 설정

이 서버는 메모리가 1GB이고 처음에 스왑이 0입니다. 앱을 올리기 전에 2GB 스왑 파일을 만들고, 재부팅 뒤에도 켜지도록 /etc/fstab에 등록합니다.

free -h
               total        used        free      shared  buff/cache   available
Mem:           961Mi       328Mi       161Mi       4.0Mi       631Mi       633Mi
Swap:             0B          0B          0B
df -h /
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        24G  2.0G   22G   9% /

🔲 fallocate -l 2G /swapfile

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
ls -lh /swapfile
-rw------- 1 root root 2.0G Nov  5 20:04 /swapfile

🔲 mkswap /swapfile

sudo mkswap /swapfile
Setting up swapspace version 1, size = 2 GiB (2147479552 bytes)
no label, UUID=0b43ccd8-5b4a-4048-9b84-eee12667b387

🔲 swapon /swapfile

sudo swapon /swapfile
swapon --show
NAME      TYPE SIZE USED PRIO
/swapfile file   2G   0B   -2
free -h
               total        used        free      shared  buff/cache   available
Mem:           961Mi       328Mi       160Mi       4.0Mi       632Mi       632Mi
Swap:          2.0Gi          0B       2.0Gi

🔲 tee -a /etc/fstab

swapon은 지금 켜는 것까지이며, 재부팅 뒤에도 켜지게 하려면 fstab에 한 줄을 더합니다.

echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
/swapfile none swap sw 0 0

9️⃣ 우분투 서버 초기 설정 확인

설정이 재부팅 뒤에도 남는지 확인하면 우분투 초기 설정을 마칩니다.

🔲 systemctl reboot

sudo systemctl reboot

🔲 uptime -p

재부팅 뒤 deploy로 다시 접속합니다.

uptime -p
up 2 minutes
swapon --show
NAME      TYPE SIZE USED PRIO
/swapfile file   2G   0B   -2
sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication) '
permitrootlogin no
passwordauthentication no
sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp (OpenSSH)           ALLOW IN    Anywhere                  
80/tcp                     ALLOW IN    Anywhere                  
443/tcp                    ALLOW IN    Anywhere                  
22/tcp (OpenSSH (v6))      ALLOW IN    Anywhere (v6)             
80/tcp (v6)                ALLOW IN    Anywhere (v6)             
443/tcp (v6)               ALLOW IN    Anywhere (v6)             
timedatectl | head -n 4
               Local time: Thu 2026-11-05 20:06:35 +08
           Universal time: Thu 2026-11-05 12:06:35 UTC
                 RTC time: Thu 2026-11-05 12:06:35
                Time zone: Asia/Singapore (+08, +0800)
systemctl is-active unattended-upgrades ssh.socket
active
active

스왑, SSH 설정, 방화벽, 시간대, 자동 업데이트가 모두 재부팅 전과 같습니다. 이 서버에는 다음으로 Node.js 앱을 PM2와 Nginx로 올립니다.

🔢 FAQ & 추천 콘텐츠

그대로 두어도 됩니다. [deferred due to phasing]으로 빠진 패키지는 단계적 배포 중인 업데이트입니다. Ubuntu는 일부 업데이트를 처음에는 일부 서버에만 내보내고, 문제가 없으면 대상을 넓혀 모든 서버에 배포합니다. 보안 업데이트는 단계적 배포를 하지 않으며, 빠진 패키지는 배포 대상이 넓어지면 다음 업그레이드에서 함께 설치됩니다.

자동 업데이트 작업은 하루 한 번이며, apt-daily.timer와 apt-daily-upgrade.timer 두 타이머가 정해진 시각에서 무작위로 늦춰 실행합니다. 이 서버에서는 apt-daily-upgrade.timer의 다음 실행이 다음 날 06:27:59(+08)로 잡혀 있었습니다. 서버가 꺼져 있어 시각을 놓치면 다음 부팅 때 바로 실행될 수 있어, 부팅 직후 apt 작업이 잠시 기다리기도 합니다.

기본 설정에서는 재부팅하지 않습니다. 50unattended-upgrades의 Automatic-Reboot 기본값이 false이고, 이 서버의 설정에도 그 줄이 켜져 있지 않습니다. 재부팅이 필요한 업데이트가 설치되면 /var/run/reboot-required 파일이 생기므로, 그 파일이 있을 때 직접 재부팅합니다.

ℹ️ 제휴 안내
본 사이트의 콘텐츠에는 제휴 링크가 포함되어 있습니다. 방문자가 이 링크를 통해 상품 또는 서비스를 구매하면 본 사이트는 판매처로부터 수수료를 지급받습니다. 이 과정에서 구매자가 지불하는 금액(이벤트 할인 시 금액이 내려갑니다↓)은 오르지 않습니다. 게시된 가격·할인·재고 정보는 작성 시점 기준이며 실제와 다를 수 있으므로, 구매 전 판매처에서 최종 확인하시기 바랍니다. 상품 선정과 평가는 자체 기준에 따라 작성되며, 수수료 지급 여부가 소개 순서나 평가 내용에 영향을 주지 않습니다.

답글 남기기

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

인간임을 증명하세요: 1   +   8   =