Чтобы уверенно работать с Linux-сервером, разработчику нужно пять навыков: входить по SSH-ключу, работать от обычного пользователя с sudo, понимать владельцев и права файлов, находить процессы и занятые порты и запускать приложение как сервис systemd с журналом в journalctl. Ниже — вся первичная настройка VPS на Ubuntu 24.04 LTS с реальным выводом команд и итоговым чек-листом.
После материала вы сможете
- создать SSH-ключ ed25519, сверить отпечаток сервера и войти по псевдониму из
~/.ssh/config - завести пользователя с
sudo, исправить права~/.sshи найти причину отказа доступа черезjournalctlиnamei -l - найти процесс и занятый порт через
pgrep,psиss -tlnpи проверить ответ приложения черезcurl - описать приложение как сервис systemd с перезапуском, читать его журнал и закрыть вход по паролю с проверкой
sshd -t
Что знать заранее
Проверено на
Ubuntu 24.04.5 LTSOpenSSH 9.6p1systemd 255bash 5.2ufw 0.36.2Python 3.12 (http.server)Node.js 24.18
Сверено с документацией: 11 октября 2026 г.
Содержание
Сервер изнутри: где что лежит
VPS — это удалённый компьютер с Linux без графического интерфейса: всё делается командами через SSH. Урок продолжает «Терминал и командная строка», где уже разобраны навигация, перенаправления и основы chmod. Здесь — то, что появляется только на сервере: другие пользователи, сервисы, сеть и безопасность. Примеры проверены на Ubuntu 24.04.5 LTS. LTS-выпуски Ubuntu получают пять лет стандартных обновлений безопасности, для 24.04 — до мая 2029 года.
Проверяли на двух контейнерах Docker: «сервер» с systemd и OpenSSH и «ноутбук» только с клиентом SSH. В примерах адрес сервера заменён на документационный 203.0.113.10 — подставьте IP своего VPS. Приглашение # означает команду от root, $ — от обычного пользователя.
/etc
- Что там
- Настройки этой машины
- Пример
/etc/ssh/sshd_config,/etc/systemd/system
/home, /root
- Что там
- Домашние папки пользователей и root
- Пример
/home/deploy/.ssh/authorized_keys
/srv
- Что там
- Данные, которые сервер отдаёт наружу
- Пример
/srv/app— проект из этого урока
/opt
- Что там
- Сторонние программы, каждая в своей папке
- Пример
- Агент мониторинга от поставщика
/var/log, /var/lib
- Что там
- Журналы-файлы и изменяемое состояние программ
- Пример
auth.log,dpkg.log; данные баз
/tmp
- Что там
- Временные файлы, могут удаляться без предупреждения
- Пример
- Ничего ценного
/usr/bin, /usr/local/bin
- Что там
- Программы; в
local— установленные вручную - Пример
/usr/local/bin/node
# ls -C /
bin etc lib64 proc sbin.usr-is-merged usr
bin.usr-is-merged home media root srv var
boot lib mnt run sys
dev lib.usr-is-merged opt sbin tmp
# df -h /
Filesystem Size Used Avail Use% Mounted on
overlay 1007G 156G 800G 17% /
# du -sh /var/log /usr
26M /var/log
280M /usr
# ls -C /var/log
README auth.log dmesg journal private
alternatives.log bootstrap.log dpkg.log kern.log syslog
apt btmp faillog lastlog wtmpoverlay и её размер — от Docker, на VPS здесь будет диск вроде /dev/vda1. Каталоги *.usr-is-merged — служебные метки объединения /bin с /usr/bin в Ubuntu 24.04.SSH: вход по ключу вместо пароля
SSH — зашифрованный канал к серверу. Ключ состоит из двух частей: приватная остаётся на вашем ноутбуке, публичная кладётся на сервер в файл ~/.ssh/authorized_keys. Пароль можно подобрать перебором, подобрать ключ на практике нельзя. Документация Ubuntu рекомендует тип ed25519 — он короче и быстрее RSA; если где-то нужен RSA, берите длину 4096 бит.
$ ssh-keygen -t ed25519 -C "dev@laptop"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/dev/.ssh/id_ed25519):
Created directory '/home/dev/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/dev/.ssh/id_ed25519
Your public key has been saved in /home/dev/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:rQv/ITuF0G35TpciyF1zw3hCADi0ObFTTv7dfQ/g2J0 dev@laptop
$ ls -l ~/.ssh
total 16
-rw------- 1 dev dev 444 Oct 12 01:35 id_ed25519
-rw-r--r-- 1 dev dev 92 Oct 12 01:35 id_ed25519.pub
$ cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICJoFk+NOCBCDZTysLYYWbhJNTTZN/nijzyyyRBFj42/ dev@laptop- Парольная фраза шифрует приватный ключ на диске: если ноутбук украдут, ключом не воспользуются без неё
- Файл
.pubможно показывать кому угодно — его и вставляют на сервер; приватныйid_ed25519не передают никогда, даже коллегам ssh-keygenсам ставит приватному ключу права600— только владелец- Отдельный ключ на каждое устройство: потеряли ноутбук — удаляете одну строку из
authorized_keys, остальные доступы работают
Как публичный ключ попадает на сервер. У большинства провайдеров VPS при создании сервера есть поле «SSH-ключ» — вставьте туда содержимое id_ed25519.pub, и он окажется в /root/.ssh/authorized_keys. Если провайдер выдал только логин и пароль, используйте ssh-copy-id: команда последний раз войдёт по паролю и допишет ключ. По умолчанию OpenSSH не пускает root по паролю (PermitRootLogin prohibit-password), так что пароль root работает, только если провайдер изменил эту настройку.
$ ssh-copy-id -i ~/.ssh/id_ed25519.pub ops@203.0.113.10
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/dev/.ssh/id_ed25519.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
ops@203.0.113.10's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'ops@203.0.113.10'"
and check to make sure that only the key(s) you wanted were added.ops с паролем (временное разрешение входа по паролю только для неё, после проверки удалено).При первом подключении клиент показывает отпечаток ключа сервера и спрашивает, доверять ли ему. После ответа yes отпечаток запоминается в ~/.ssh/known_hosts, и если однажды он изменится, SSH откажется подключаться с громким предупреждением — это защита от подмены сервера.
$ ssh root@203.0.113.10 'hostname; whoami'
The authenticity of host '203.0.113.10 (203.0.113.10)' can't be established.
ED25519 key fingerprint is SHA256:IaTy+ntmy5vPYW0mWxRtuKucgJ/6xml8wEPCiK2pPoA.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '203.0.113.10' (ED25519) to the list of known hosts.
Enter passphrase for key '/home/dev/.ssh/id_ed25519':
vps
rootvps.# ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
256 SHA256:IaTy+ntmy5vPYW0mWxRtuKucgJ/6xml8wEPCiK2pPoA root@vps (ED25519)Пользователи, группы и sudo
Работать под root опасно: любая опечатка выполняется с полными правами. В Ubuntu пароль root по умолчанию заблокирован, а права администратора выдаются через sudo участникам группы sudo. Поэтому первое действие на новом сервере — создать обычного пользователя, например deploy, и дать ему sudo.
# 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' ...
# usermod -aG sudo deploy
# id deploy
uid=1000(deploy) gid=1000(deploy) groups=1000(deploy),27(sudo),100(users)
# ls -ld /home/deploy
drwxr-x--- 2 deploy deploy 4096 Oct 12 01:35 /home/deploydeploy понадобится для sudo, входить по SSH будем ключом.adduser— дружелюбная обёртка Ubuntu: создаёт группу с тем же именем, домашнюю папку и спрашивает пароль. Низкоуровневыйuseraddбез флагов не создаёт даже домашнюю папку- В
uid=1000(deploy) gid=1000(deploy)— числовые идентификаторы: ядро проверяет права именно по числам, имена — для людей - Флаг
-aвusermod -aGобязателен: без негоusermod -G users deployзаменит список групп, и пользователь выпадет изsudo(проверено). Вариант из документации Ubuntu —adduser deploy sudo - Домашние папки в Ubuntu 21.10 и новее создаются с правами
750: другие пользователи не могут в них заглянуть
Теперь ключ: скопируем authorized_keys от root в домашнюю папку deploy. Типичная ошибка — скопировать от root и забыть про владельца.
# install -d -m 700 -o deploy -g deploy /home/deploy/.ssh
# cp /root/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys
# ls -l /home/deploy/.ssh
total 4
-rw------- 1 root root 92 Oct 12 01:35 authorized_keysinstall -d создаёт папку сразу с правами и владельцем; а вот cp от root оставил владельцем файла root.$ ssh -o BatchMode=yes deploy@203.0.113.10 whoami
deploy@203.0.113.10: Permission denied (publickey,password).
# journalctl -u ssh -n 2 --no-pager
Oct 12 01:36:04 vps sshd[278]: Could not open user 'deploy' authorized keys '/home/deploy/.ssh/authorized_keys': Permission denied
Oct 12 01:36:04 vps sshd[278]: Connection closed by authenticating user deploy 198.51.100.23 port 52416 [preauth]BatchMode=yes запрещает клиенту спрашивать пароль, поэтому видно, что ключ не принят.Ошибка на клиенте ничего не объясняет, а журнал сервера называет причину прямо: sshd проверяет ключи от имени пользователя deploy, а файл принадлежит root и закрыт правами 600. Исправляем владельца:
# chown deploy:deploy /home/deploy/.ssh/authorized_keys
# chmod 600 /home/deploy/.ssh/authorized_keys
# ls -la /home/deploy/.ssh
total 12
drwx------ 2 deploy deploy 4096 Oct 12 01:35 .
drwxr-x--- 3 deploy deploy 4096 Oct 12 01:35 ..
-rw------- 1 deploy deploy 92 Oct 12 01:35 authorized_keyschown вход по ключу проходит.Чтобы не набирать каждый раз IP и имя пользователя, заведите на ноутбуке файл ~/.ssh/config с псевдонимом, а парольную фразу ключа доверьте ssh-agent — он помнит её до конца сеанса.
Host myvps
HostName 203.0.113.10
User deploy
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yesHost пишут выше общих правил. Файл должен быть закрыт от записи другими — ставим 600.$ eval "$(ssh-agent -s)"
Agent pid 363
$ ssh-add
Enter passphrase for /home/dev/.ssh/id_ed25519:
Identity added: /home/dev/.ssh/id_ed25519 (dev@laptop)
$ chmod 600 ~/.ssh/config
$ ssh myvps 'whoami; id'
deploy
uid=1000(deploy) gid=1000(deploy) groups=1000(deploy),27(sudo),100(users)
$ ssh -t myvps 'sudo whoami; sudo -l'
[sudo] password for deploy:
root
Matching Defaults entries for deploy on vps:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
use_pty
User deploy may run the following commands on vps:
(ALL : ALL) ALL
Connection to 203.0.113.10 closed.sudo спрашивает пароль самого deploy, а не root, и запоминает его на некоторое время.Владельцы и права на сервере: chown, umask, namei
Как читать rwxr-x--- и что значат chmod 644 и chmod 600, разобрано в уроке о терминале. На сервере добавляется вопрос «кто»: у каждого файла есть владелец и группа, а у каждого процесса — пользователь, от имени которого он работает. Право проверяется у тройки, которая подходит процессу: владелец, иначе группа, иначе все остальные. Подготовим папку проекта: создадим её через sudo и отдадим deploy.
$ sudo mkdir -p /srv/app/public
$ ls -ld /srv/app
drwxr-xr-x 3 root root 4096 Oct 12 01:37 /srv/app
$ sudo chown -R deploy:deploy /srv/app
$ ls -ld /srv/app /srv/app/public
drwxr-xr-x 3 deploy deploy 4096 Oct 12 01:37 /srv/app
drwxr-xr-x 2 deploy deploy 4096 Oct 12 01:37 /srv/app/public
$ umask
0002
$ echo '<h1>Hello from /srv/app</h1>' > /srv/app/public/index.html
$ printf 'PORT=8080\nAPP_SECRET=change-me\n' > /srv/app/.env
$ ls -la /srv/app /srv/app/public
/srv/app:
total 16
drwxr-xr-x 3 deploy deploy 4096 Oct 12 01:37 .
drwxr-xr-x 1 root root 4096 Oct 12 01:37 ..
-rw-rw-r-- 1 deploy deploy 31 Oct 12 01:37 .env
drwxr-xr-x 2 deploy deploy 4096 Oct 12 01:37 public
/srv/app/public:
total 12
drwxr-xr-x 2 deploy deploy 4096 Oct 12 01:37 .
drwxr-xr-x 3 deploy deploy 4096 Oct 12 01:37 ..
-rw-rw-r-- 1 deploy deploy 29 Oct 12 01:37 index.html
$ chmod 600 /srv/app/.env
$ chmod 750 /srv/app
$ stat -c '%A %a %U:%G %n' /srv/app /srv/app/.env /srv/app/public/index.html
drwxr-x--- 750 deploy:deploy /srv/app
-rw------- 600 deploy:deploy /srv/app/.env
-rw-rw-r-- 664 deploy:deploy /srv/app/public/index.html
$ sudo adduser --disabled-password --comment "" intern > /dev/null
$ sudo -u intern cat /srv/app/public/index.html
cat: /srv/app/public/index.html: Permission denied
$ sudo -u intern ls /srv/app
ls: cannot open directory '/srv/app': Permission denied
$ namei -l /srv/app/public/index.html
f: /srv/app/public/index.html
drwxr-xr-x root root /
drwxr-xr-x root root srv
drwxr-x--- deploy deploy app
drwxr-xr-x deploy deploy public
-rw-rw-r-- deploy deploy index.htmldeploy по SSH, реальный вывод. Пользователь intern создан только для проверки доступа.sudo mkdirсоздал папку от root — писать в неёdeployне мог бы.chown -R deploy:deployпередал владельца и группу рекурсивноumask 0002— маска прав для новых файлов: из666вычитается002, получается664(rw-rw-r--). Поэтому.envпосле создания был доступен на чтение всем, и права закрыли явно —chmod 600- Право
xу папки означает «можно войти». У/srv/appс правами750посторонние не войдут, и файлindex.htmlс правами664внутри им тоже недоступен namei -lпоказывает права на каждом шаге пути — сразу видно, на каком каталоге доступ обрывается
~/.ssh
- Права и владелец
700, владелец — пользователь- Почему
sshdотвергает ключи, если папку могут менять другие
~/.ssh/authorized_keys
- Права и владелец
600, владелец — пользователь- Почему
- Читается от имени пользователя, писать — только ему
/srv/app
- Права и владелец
750,deploy:deploy- Почему
- Приложению — всё, группе — чтение, остальным — ничего
/srv/app/.env
- Права и владелец
600, владелец — пользователь сервиса- Почему
- Пароли и ключи видит только приложение
/etc/systemd/system/*.service
- Права и владелец
644,root:root- Почему
- Описание сервиса правит только администратор
Процессы, порты и curl
Процесс — запущенная программа с номером PID и пользователем-владельцем. Запустим в фоне простой веб-сервер Python из папки проекта и посмотрим на него всеми основными инструментами: pgrep ищет процессы по имени, ps даёт снимок списка, top — живую таблицу нагрузки (выход — q).
$ nohup python3 -m http.server 8080 --bind 127.0.0.1 --directory /srv/app/public > /tmp/http.log 2>&1 &
$ sleep 1; pgrep -a python3
2241 python3 -m http.server 8080 --bind 127.0.0.1 --directory /srv/app/public
$ ps -eo pid,user,%cpu,%mem,cmd --sort=-%mem | head -5
PID USER %CPU %MEM CMD
2241 deploy 0.9 0.0 python3 -m http.server 8080 --bind 127.0.0.1 --directory /srv/app/public
24 root 0.0 0.0 /usr/lib/systemd/systemd-journald
1 root 0.0 0.0 /sbin/init
2225 root 0.0 0.0 sshd: deploy [priv]
$ top -b -n 1 | head -8
top - 01:47:17 up 3 days, 11:53, 0 user, load average: 2.15, 3.11, 3.34
Tasks: 20 total, 1 running, 19 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.3 sy, 0.0 ni, 97.9 id, 0.0 wa, 0.0 hi, 1.7 si, 0.0 st
MiB Mem : 31936.7 total, 4438.6 free, 4555.4 used, 23685.6 buff/cache
MiB Swap: 8192.0 total, 8190.3 free, 1.7 used. 27381.2 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 21336 12752 9684 S 0.0 0.0 0:00.63 systemd
$ sudo ss -tlnp
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=2066,fd=3),("systemd",pid=1,fd=93))
LISTEN 0 4096 127.0.0.11:41039 0.0.0.0:*
LISTEN 0 5 127.0.0.1:8080 0.0.0.0:* users:(("python3",pid=2241,fd=3))
LISTEN 0 4096 [::]:22 [::]:* users:(("sshd",pid=2066,fd=4),("systemd",pid=1,fd=94))
$ curl -sS http://127.0.0.1:8080/
<h1>Hello from /srv/app</h1>
$ curl -sI http://127.0.0.1:8080/
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.12.3
Date: Mon, 12 Oct 2026 01:47:17 GMT
Content-type: text/html
Content-Length: 29
Last-Modified: Mon, 12 Oct 2026 01:37:06 GMT
$ curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/missing
404
$ curl -sS http://127.0.0.1:8081/
curl: (7) Failed to connect to 127.0.0.1 port 8081 after 0 ms: Couldn't connect to server
$ kill $(pgrep -f 'http[.]server 8080')
$ sleep 1; pgrep -a python3 || echo "no python3 processes"
no python3 processes
$ cat /tmp/http.log
127.0.0.1 - - [12/Oct/2026 01:47:17] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [12/Oct/2026 01:47:17] "HEAD / HTTP/1.1" 200 -
127.0.0.1 - - [12/Oct/2026 01:47:17] code 404, message File not found
127.0.0.1 - - [12/Oct/2026 01:47:17] "GET /missing HTTP/1.1" 404 -deploy, реальный вывод. Строка 127.0.0.11:41039 — внутренний DNS Docker, на VPS её не будет; нагрузка и память в top — хоста, на котором шёл тест.ss -tlnp — главный ответ на вопрос «кто слушает порт»: t — TCP, l — только слушающие сокеты, n — числа вместо имён, p — процесс. Без sudo процессы других пользователей не показываются. Обратите внимание на адрес: 127.0.0.1:8080 доступен только с самого сервера, 0.0.0.0:22 — со всех интерфейсов. Порт 22 в Ubuntu 24.04 держит сам systemd (юнит ssh.socket) и запускает sshd при подключении — это видно в колонке процессов.
curl -sS URL
- Что показывает
- Тело ответа;
-sубирает индикатор,-Sоставляет сообщения об ошибках
curl -sI URL
- Что показывает
- Только заголовки ответа: код, тип, длина
curl -s -o /dev/null -w '%{http_code}\n' URL
- Что показывает
- Только код ответа — удобно в скриптах проверки
curl: (7) Failed to connect
- Что показывает
- На порту никто не слушает: процесс не запущен или слушает другой адрес
SIGTERM (15)
- Как отправить
kill PID— по умолчанию- Что происходит
- Просьба завершиться: программа может закрыть соединения и сохранить данные
SIGINT (2)
- Как отправить
- Ctrl+C в терминале
- Что происходит
- Прерывание из терминала, обычно ведёт себя как
SIGTERM
SIGHUP (1)
- Как отправить
kill -HUP PID- Что происходит
- Многие демоны по нему перечитывают конфигурацию
SIGKILL (9)
- Как отправить
kill -9 PID- Что происходит
- Немедленное уничтожение ядром, перехватить нельзя — последнее средство
Процесс, запущенный через nohup … &, переживёт выход из SSH, но не перезагрузку, и если он упадёт, никто его не поднимет. Для приложения на сервере нужен менеджер сервисов — systemd.
systemd: приложение как сервис и журналы
systemd — первый процесс системы (PID 1): он запускает службы при загрузке, перезапускает упавшие и собирает их вывод в журнал. Службу описывает unit-файл. Свои юниты кладут в /etc/systemd/system. Опишем наш веб-сервер — порт он возьмёт из .env.
[Unit]
Description=Hello web: static files from /srv/app/public
After=network.target
[Service]
Type=exec
User=deploy
Group=deploy
WorkingDirectory=/srv/app
EnvironmentFile=/srv/app/.env
ExecStart=/usr/bin/python3 -m http.server ${PORT} --bind 127.0.0.1 --directory /srv/app/public
Restart=on-failure
RestartSec=2
[Install]
WantedBy=multi-user.targetExecStart=/usr/local/bin/node server.mjs — такой юнит для Node.js собран в упражнении ниже. Создайте файл через sudo nano.User=, Group=
- Зачем
- Процесс работает от
deploy, а не от root
EnvironmentFile=
- Зачем
- Переменные из
.env; файл читает systemd, поэтому права600уdeployему не мешают
ExecStart=
- Зачем
- Команда запуска с абсолютным путём;
${PORT}подставляется из окружения
Restart=on-failure, RestartSec=2
- Зачем
- Перезапуск через 2 секунды, если процесс упал
WantedBy=multi-user.target
- Зачем
- Что включает
systemctl enable: запуск при обычной загрузке
$ sudo cp /tmp/hello-web.service /etc/systemd/system/hello-web.service
$ sudo systemctl daemon-reload
$ sudo systemctl enable --now hello-web
Created symlink /etc/systemd/system/multi-user.target.wants/hello-web.service → /etc/systemd/system/hello-web.service.
$ systemctl status hello-web --no-pager
● hello-web.service - Hello web: static files from /srv/app/public
Loaded: loaded (/etc/systemd/system/hello-web.service; enabled; preset: enabled)
Active: active (running) since Mon 2026-10-12 01:47:42 UTC; 5ms ago
Main PID: 2384 (python3)
Tasks: 1 (limit: 38311)
Memory: 2.4M (peak: 2.4M)
CPU: 3ms
CGroup: /system.slice/hello-web.service
└─2384 /usr/bin/python3 -m http.server 8080 --bind 127.0.0.1 --dir…
$ curl -sS http://127.0.0.1:8080/
curl: (7) Failed to connect to 127.0.0.1 port 8080 after 0 ms: Couldn't connect to server
$ sleep 1; curl -sS http://127.0.0.1:8080/
<h1>Hello from /srv/app</h1>
$ journalctl -u hello-web -n 5 --no-pager
Hint: You are currently not seeing messages from other users and the system.
Users in groups 'adm', 'systemd-journal' can see all messages.
Pass -q to turn off this notice.
Oct 12 01:47:43 vps python3[2384]: 127.0.0.1 - - [12/Oct/2026 01:47:43] "GET / HTTP/1.1" 200 -
$ sudo journalctl -u hello-web -n 5 --no-pager
Oct 12 01:47:42 vps systemd[1]: Starting hello-web.service - Hello web: static files from /srv/app/public...
Oct 12 01:47:42 vps systemd[1]: Started hello-web.service - Hello web: static files from /srv/app/public.
Oct 12 01:47:43 vps python3[2384]: 127.0.0.1 - - [12/Oct/2026 01:47:43] "GET / HTTP/1.1" 200 -
$ sudo kill -9 $(systemctl show -p MainPID --value hello-web)
$ sleep 3; systemctl status hello-web --no-pager | head -5
● hello-web.service - Hello web: static files from /srv/app/public
Loaded: loaded (/etc/systemd/system/hello-web.service; enabled; preset: enabled)
Active: active (running) since Mon 2026-10-12 01:47:46 UTC; 815ms ago
Main PID: 2401 (python3)
Tasks: 1 (limit: 38311)
$ sudo journalctl -u hello-web -n 6 --no-pager
Oct 12 01:47:43 vps python3[2384]: 127.0.0.1 - - [12/Oct/2026 01:47:43] "GET / HTTP/1.1" 200 -
Oct 12 01:47:43 vps systemd[1]: hello-web.service: Main process exited, code=killed, status=9/KILL
Oct 12 01:47:43 vps systemd[1]: hello-web.service: Failed with result 'signal'.
Oct 12 01:47:46 vps systemd[1]: hello-web.service: Scheduled restart job, restart counter is at 1.
Oct 12 01:47:46 vps systemd[1]: Starting hello-web.service - Hello web: static files from /srv/app/public...
Oct 12 01:47:46 vps systemd[1]: Started hello-web.service - Hello web: static files from /srv/app/public.
$ sudo kill $(systemctl show -p MainPID --value hello-web)
$ sleep 3; systemctl is-active hello-web; systemctl is-enabled hello-web
inactive
enabled
$ sudo systemctl start hello-web
$ systemctl is-active hello-web
active
$ sudo journalctl -u hello-web --since "10 min ago" -p warning --no-pager
Oct 12 01:47:43 vps systemd[1]: hello-web.service: Main process exited, code=killed, status=9/KILL
Oct 12 01:47:43 vps systemd[1]: hello-web.service: Failed with result 'signal'.
$ systemctl list-units --type=service --state=failed --no-pager
UNIT LOAD ACTIVE SUB DESCRIPTION
0 loaded units listed.deploy, systemd 255, реальный вывод (цвета убраны).Разберём, что произошло. Первый curl сразу после старта не дозвался: при Type=exec systemd считает службу запущенной, как только программа стартовала, а не когда она открыла порт. Проверки после деплоя поэтому повторяют запрос несколько раз или ждут ответа health-эндпоинта. kill -9 служба пережила: Restart=on-failure перезапускает процесс, завершённый сигналом. А обычный kill (SIGTERM) systemd считает чистым завершением — по документации systemd.service сигналы SIGHUP, SIGINT, SIGTERM и SIGPIPE не считаются сбоем, поэтому служба осталась остановленной. Если нужно поднимать её в любом случае, есть Restart=always.
-u hello-web
- Что делает
- Только записи этого юнита
-f
- Что делает
- Показывать новые записи по мере появления, как
tail -f
-n 50
- Что делает
- Последние 50 строк (без числа — 10)
--since "10 min ago", --since today
- Что делает
- Записи за период
-p warning
- Что делает
- Только предупреждения и серьёзнее
-b
- Что делает
- Только с последней загрузки
Пакеты, файрвол и вход только по ключу
Программы в Ubuntu ставят из репозиториев через apt. apt update только обновляет списки пакетов, apt upgrade устанавливает новые версии — поэтому сначала первое, потом второе. apt рассчитан на человека; в скриптах документация Ubuntu советует apt-get. Источники пакетов в 24.04 описаны в /etc/apt/sources.list.d/ubuntu.sources, а по умолчанию Ubuntu Server сам ставит обновления безопасности.
$ sudo apt update
Hit:1 http://security.ubuntu.com/ubuntu noble-security InRelease
Hit:2 http://archive.ubuntu.com/ubuntu noble InRelease
Hit:3 http://archive.ubuntu.com/ubuntu noble-updates InRelease
Hit:4 http://archive.ubuntu.com/ubuntu noble-backports InRelease
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
2 packages can be upgraded. Run 'apt list --upgradable' to see them.
$ apt list --upgradable
Listing... Done
libaudit-common/noble-updates 1:3.1.2-2.1ubuntu0.1 all [upgradable from: 1:3.1.2-2.1build1.1]
libaudit1/noble-updates 1:3.1.2-2.1ubuntu0.1 amd64 [upgradable from: 1:3.1.2-2.1build1.1]
$ sudo apt install -y tree ufw
…
The following NEW packages will be installed:
iptables libip4tc2 libip6tc2 libjansson4 libnetfilter-conntrack3
libnfnetlink0 libnftables1 libnftnl11 nftables tree ufw
0 upgraded, 11 newly installed, 0 to remove and 2 not upgraded.
…
Setting up tree (2.1.1-2ubuntu3.24.04.2) ...
Setting up ufw (0.36.2-6) ...
…
$ tree -a /srv/app
/srv/app
├── .env
└── public
└── index.html
2 directories, 2 files
$ apt policy tree
tree:
Installed: 2.1.1-2ubuntu3.24.04.2
Candidate: 2.1.1-2ubuntu3.24.04.2
Version table:
*** 2.1.1-2ubuntu3.24.04.2 500
500 http://archive.ubuntu.com/ubuntu noble-updates/universe amd64 Packages
100 /var/lib/dpkg/status
2.1.1-2ubuntu3 500
500 http://archive.ubuntu.com/ubuntu noble/universe amd64 Packages
$ sudo apt remove -y tree
…
The following packages will be REMOVED:
tree
…
Removing tree (2.1.1-2ubuntu3.24.04.2) ...
$ grep -E " (install|remove) tree" /var/log/dpkg.log
2026-10-12 01:40:02 install tree:amd64 <none> 2.1.1-2ubuntu3.24.04.2
2026-10-12 01:40:04 remove tree:amd64 2.1.1-2ubuntu3.24.04.2 <none>…). Каждая установка и удаление записываются в /var/log/dpkg.log.Файрвол ufw в Ubuntu изначально выключен. Порядок важен: сначала разрешить SSH, потом включить — иначе новое подключение к серверу станет невозможным. Профиль OpenSSH открывает порт 22/tcp. Остальные входящие подключения после включения отклоняются: в нашей проверке тестовый сервер на 0.0.0.0:8090 перестал отвечать снаружи (тайм-аут curl).
$ sudo ufw allow OpenSSH
Rules updated
Rules updated (v6)
$ 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), deny (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp (OpenSSH) ALLOW IN Anywhere
22/tcp (OpenSSH (v6)) ALLOW IN Anywhere (v6) Последний шаг — запретить вход по паролю и вход root. Настройки сервера SSH читаются из /etc/ssh/sshd_config, а он в Ubuntu первым делом подключает файлы из /etc/ssh/sshd_config.d/. Для sshd действует первое найденное значение, поэтому свой файл называем с маленьким номером — он прочитается раньше файлов, которые могли положить провайдер или cloud-init.
# Key-only login; root cannot log in over SSH
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no644, владелец root.$ sudo nano /etc/ssh/sshd_config.d/01-hardening.conf
$ echo 'PasswordAuthentification no' | sudo tee /etc/ssh/sshd_config.d/02-typo.conf
PasswordAuthentification no
$ sudo sshd -t
/etc/ssh/sshd_config.d/02-typo.conf: line 1: Bad configuration option: PasswordAuthentification
/etc/ssh/sshd_config.d/02-typo.conf: terminating, 1 bad configuration options
$ sudo rm /etc/ssh/sshd_config.d/02-typo.conf
$ sudo sshd -t && echo "config OK"
config OK
$ sudo sshd -T | grep -E '^(passwordauthentication|permitrootlogin|kbdinteractiveauthentication|pubkeyauthentication) '
permitrootlogin no
pubkeyauthentication yes
passwordauthentication no
kbdinteractiveauthentication no
$ sudo systemctl restart ssh.service
$ systemctl is-active ssh.socket ssh.service
active
activedeploy, реальный вывод. Опечатка добавлена специально, чтобы показать, как sshd -t ловит ошибку до перезапуска.$ ssh -o PubkeyAuthentication=no myvps
deploy@203.0.113.10: Permission denied (publickey).
$ ssh root@203.0.113.10
root@203.0.113.10: Permission denied (publickey).
$ ssh myvps hostname
vpsdeploy по ключу работает.sshd -T печатает итоговые значения настроек — так проверяют, что файл действительно применился. В Ubuntu 24.04 порт 22 держит ssh.socket, поэтому смену порта ищите в документации именно для этой версии: с socket-активацией порядок действий отличается от инструкций для старых выпусков.
Чек-лист первичной настройки и упражнения
- Ключ ed25519 с парольной фразой, отпечаток сервера сверен с консолью провайдера
- Создан пользователь с
sudo, вход под ним по ключу проверен в новом окне - В
sshd_config.d:PasswordAuthentication no,PermitRootLogin no;sudo sshd -tбез ошибок ufw: разрешёнOpenSSH, затем включён;sudo ufw status verboseпоказывает правила- Выполнены
sudo apt updateиsudo apt upgrade - Проект в
/srv/app, владелец — пользователь сервиса,.envс правами600 - Приложение работает как сервис systemd с
Restart=on-failure, включено черезenableи слушает127.0.0.1 - Известно, где смотреть:
journalctl -u <сервис>,/var/log/auth.log,ss -tlnp
Следующий шаг — Docker: тот же сервер, но приложение упаковано в образ со всеми зависимостями и запускается одной командой. Навыки этого урока — пользователи, права, порты и журналы — понадобятся и внутри контейнеров.
Как проверено14 · пройденосверено 11 октября 2026 г.
- запущено, работает
файловая система: ls -C /, df -h /, du -sh, ls -C /var/log
контейнер kb-linux-vps (ubuntu:24.04 = 24.04.5 + systemd 255 + openssh-server 9.6p1, --privileged, cgroupns private), реальный вывод
- запущено, работает
ssh-keygen ed25519 с парольной фразой, ssh-agent/ssh-add, первый вход с проверкой отпечатка, ssh-keygen -lf на сервере, ~/.ssh/config (Host myvps)
контейнер kb-linux-laptop (ubuntu:24.04 + openssh-client) → kb-linux-vps в сети kb-linux-net; интерактивные запросы через script(1) с подачей ответов; отпечатки совпали
- запущено, работает
ssh-copy-id по паролю
учётная запись ops с паролем и временным Match User ops / PasswordAuthentication yes (удалено после проверки): Number of key(s) added: 1, вход новым ключом. В проверке копировался второй ключ id_ed25519_second.pub, в статье имя файла заменено на id_ed25519.pub, хост — на 203.0.113.10
- запущено, работает
adduser deploy, usermod -aG sudo, id, ls -ld /home/deploy (750), sudo whoami / sudo -l
adduser 3.137ubuntu1 в pty через script; вывод вопросов adduser собран из того же прогона (ответы — Enter); usermod -G без -a выкидывает из sudo — проверено в отдельном ubuntu:24.04
- запущено, работает
ошибка владельца authorized_keys (root:root 600) и исправление chown
клиент: Permission denied (publickey,password) при BatchMode=yes; journalctl -u ssh: Could not open user 'deploy' authorized keys … Permission denied; после chown вход проходит
- запущено, работает
права: mkdir/chown -R, umask 0002, chmod 600/.env, chmod 750, stat, отказ intern, namei -l
сеанс deploy по SSH с sudo (пароль введён через pty), реальный вывод
- запущено, работает
процессы и сеть: nohup python3 -m http.server, pgrep, ps, top -b, sudo ss -tlnp, curl -sS/-sI/-w http_code, curl (7), kill
сеанс deploy, Python 3.12.3; строка 127.0.0.11 — DNS Docker, оговорена в подписи
- запущено, работает
hello-web.service: daemon-reload, enable --now, status, гонка Type=exec, journalctl (без sudo и с sudo), kill -9 → перезапуск, kill (SIGTERM) → inactive, -p warning, list-units --failed
systemd 255.4 в контейнере; журнал очищен (rotate+vacuum) перед прогоном; ANSI-цвета удалены из вывода. В исходном образе не было dbus — systemctl от обычного пользователя падал с Failed to connect to bus; установлен dbus (на Ubuntu Server он есть)
- запущено, работает
apt update, apt list --upgradable, apt install tree ufw, tree, apt policy, apt remove, dpkg.log
реальные репозитории noble; в статье вывод очищен от строк прогресса и сокращён (…)
- запущено, работает
ufw allow OpenSSH, ufw enable, status verbose
ufw 0.36.2 (iptables-nft) в привилегированном контейнере; SSH работает после включения; порт 8090 на 0.0.0.0 снаружи — тайм-аут curl (28). Правило allow и enable — из двух последовательных прогонов на одном сервере
- запущено, работает
01-hardening.conf, sshd -t (с опечаткой и без), sshd -T, systemctl restart ssh.service, проверка входа
OpenSSH 9.6p1: Bad configuration option ловится sshd -t; после перезапуска пароль и root отклоняются (Permission denied (publickey)), вход deploy по ключу работает; в статье первая строка «sudo nano …» вместо фактического «sudo cp» готового файла
- запущено, работает
упражнение repeat (права и группы)
docker run --rm ubuntu:24.04 (kb-linux-ex1): Permission denied до adduser intern deploy, чтение после
- запущено, работает
упражнение modify (порт через .env)
PORT=8081 в .env + systemctl restart без daemon-reload: ss показал 127.0.0.1:8081, curl ответил
- запущено, работает
упражнение apply (server.mjs + api.service скриптом)
скрипт выполнен целиком от deploy (bash -x): Node.js 24.18.0 (бинарник из node:24.18.0-bookworm-slim в /usr/local/bin/node), /health → {"status":"ok","env":"production"}, процесс от deploy, при stop — SIGTERM received, closing server
«Запущено» — код выполнен во временном проекте или контейнере, вывод в статье взят из прогона. «Частично» — проверено с заглушками вместо внешних сервисов. «Не запускалось» — нужны устройство, аккаунт или ключ; шаги сверены с официальной документацией. Файлы проверочного проекта и логи запусков сохранены в архиве InterLAB.